Skip to content

feat(core): the webvh DID lifecycle, and the rest of contexts - #123

Merged
stormer78 merged 1 commit into
mainfrom
feat/webvh-and-context-families
Aug 19, 2026
Merged

feat(core): the webvh DID lifecycle, and the rest of contexts#123
stormer78 merged 1 commit into
mainfrom
feat/webvh-and-context-families

Conversation

@stormer78

Copy link
Copy Markdown
Contributor

What

Takes @openvtc/pnm-core's coverage of the agent's task surface from
110 of 161 families to 130.

webvh/dids/* create, get, list, update, delete, rotate-keys, register-with-server
webvh/servers/* list, register, remove, domains, reconcile
webvh/agent-name/* set, remove, list, check, enable, disable
vta/contexts/* get, update, update-did

Why they were missing

Not by choice — they had no published schema to implement against, which is
the usual reason a family is absent here. Specifying them upstream
(trustoverip/dtgwg-trust-tasks-tf#240) and publishing the bindings
(@openvtc/trust-tasks 0.10.0, #242) is what unblocked them. This is the other
half of that work rather than new ground.

webvh/ is a new module, deliberately not part of did-hosting/

The distinction is the counterparty, and it isn't cosmetic:

  • did-hosting/ talks to a hosting service — it publishes a document at a
    URL and serves its log, and the agent is one of its clients.
  • webvh/ talks to the agent — it holds the keys and signs the log entries.

Only the agent can mint or update a DID it controls; the host can only serve
what it's handed. Folding these into one module would invite sending a signing
request to something that cannot sign.

New entry point @openvtc/pnm-core/webvh, registered at layer 4 in the
module-boundary test.

Three things the tests pin

Each is silently wrong rather than loudly wrong, which is why they're worth a
test each:

  • An omitted serverId is absent, not null. Absence means serverless
    the caller serves the log itself — so a stray null asks for a different DID.
  • portable: false is sent. False is a decision and absent is not, and
    portability cannot be added to a DID after it's minted.
  • servers/{domains,reconcile} are 0.1, not 1.0. The version is part of
    the task URI, so assuming 1.0 to match their neighbours would address a task
    that doesn't exist. This one actually bit during implementation.

contexts/update replaces, it doesn't merge

Documented on the parameter, because the failure mode is quiet: the agent stores
the policy it's given, so a partial object drops the constraints it omits —
which for a policy means silently widening what a context may do. Read the
current policy, apply your edit, send it whole.

contexts/{get,update,update-did} live in vta/contexts.ts beside list and
create, and reuse that file's normalizeContext fold so a pre-camelCase agent
still decodes.

Coverage snapshot

Moved 110 → 130 in this commit, as its test requires — the number is a snapshot
to be reviewed in a diff, not a threshold.

Of the 31 still outstanding: vault/*'s archive/restore/purge and credentials
sub-family, backup, attestation, seeds and audit retention are unspecced, and
the wider vtc/* surface belongs to a community rather than an agent.

Test

Lint (tsc -b) clean. 527 tests passing, 0 failing (was 504) — 17 new
across webvh.dids-servers-names.mjs and vta.contexts-get-update.mjs.

Checklist (stack guide §9)

  • No new bare fetch(); these ride the injected TrustTaskSender (R1.2)
  • No lock held across a network await (R1.3)
  • No local state committed before its remote effect (R2.1)
  • Every retry bounded + backed off (R1.4)
  • Accept/poll/listen loops survive transient errors (R1.5)
  • Acks/deletes happen only after durable handoff (R1.6)
  • New/changed wire types: camelCase, schema registered, consumers updated
    (R3.*)
    — payloads come from the generated bindings, so the wire is the
    schema by construction
  • Config absence = most restrictive (R5.*)
  • Logs/status claim only what was verified (R6.*)
  • Deviations flagged with rule numbers

Takes coverage of the agent's task surface from 110 of 161 families to 130.

These 20 were not missing by choice — they had no published schema to implement
against, which is the usual reason a family is absent here. Specifying them
upstream (trust-tasks #240) and publishing the bindings (0.10.0) is what
unblocked them, so this is the other half of that work rather than new ground.

**`webvh/` is a new module, deliberately separate from `did-hosting/`.** The
distinction is the counterparty, and it is not cosmetic: `did-hosting/` talks to
a hosting *service* that publishes a document at a URL and serves its log, and
the agent is merely one of its clients. Everything here is addressed to the
agent, which holds the keys and signs the log entries. Only the agent can mint
or update a DID it controls; the host can only serve what it is handed. Putting
them in one module would invite sending a signing request to something that
cannot sign.

- `dids/*` — create, get, list, update, delete, rotate-keys,
  register-with-server
- `servers/*` — list, register, remove, domains, reconcile
- `agent-name/*` — set, remove, list, check, enable, disable

`contexts/{get,update,update-did}` go into `vta/contexts.ts` beside list and
create, and use that file's `normalizeContext` fold so a pre-camelCase agent
still decodes.

Three things the tests pin, because each is silently wrong rather than loudly
wrong:

- **An omitted `serverId` must be absent, not null.** Absence means serverless —
  the caller serves the log itself — so a stray `null` asks for a different DID.
- **`portable: false` is sent**, because false is a decision and absent is not:
  portability cannot be added to a DID afterwards.
- **`servers/{domains,reconcile}` are 0.1, not 1.0.** The version is part of the
  task URI, so guessing 1.0 to match its neighbours would address a task that
  does not exist.

`contexts/update` sends the policy whole, and its doc says so: the agent stores
what it is given rather than merging, so a partial policy quietly widens what a
context may do.

The coverage snapshot moves 110 → 130 in this commit, as its test requires. Of
the 31 still outstanding, `vault/*`'s archive/restore/purge and credentials
sub-family, backup, attestation, seeds and audit retention are unspecced, and
the wider `vtc/*` surface belongs to a community rather than an agent.

Lint clean; 527 tests passing.

Signed-off-by: Glenn Gore <glenn.g@affinidi.com>
@stormer78
stormer78 merged commit e75aef5 into main Aug 19, 2026
3 checks passed
@stormer78
stormer78 deleted the feat/webvh-and-context-families branch August 19, 2026 13:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant