Skip to content

feat: publish the registry referral through the running VTA - #92

Merged
vthwang merged 2 commits into
feat/optional-trust-registryfrom
feat/optional-trust-registry-online-referral
Oct 24, 2026
Merged

vthwang merged 2 commits into
feat/optional-trust-registryfrom
feat/optional-trust-registry-online-referral

Conversation

@geoffturk

Copy link
Copy Markdown
Member

Into feat/optional-trust-registry. This picks up where the branch's design doc §6 left off: "it needs an authenticated online VTA edit or upstream offline transport support". This is the authenticated online edit.

Why the referral Job failed

The referral Job ran the offline vta did-mgmt dids edit on the VTA's PVC. That binary holds no mediator session, so the only transport it can initiate toward a DID host is REST (vta-service/src/webvh_cli.rs builds its deps with DIDCommBridge::signing_only and tsp: None; Outbound::initiable_for then yields [rest]). A Full Stack dids daemon is provisioned through the VTA with a mediator, and in that case the webvh-service setup picks did-host-didcomm, which advertises TSPTransport and DIDCommMessaging and no HTTPS endpoint (did-hosting-common/src/server/vta_setup.rs, build_webvh_provision_ask). Empty intersection, hence no transport in common … it advertises [tsp, didcomm] and this agent can initiate [rest]. The offline command had also already committed the new log entry locally before it tried to publish, so the stack it ran on now has a local head the host has never seen.

The running VTA does not have the problem: it holds the mediator session, publishes to the host over TSP or DIDComm, and re-publishes an unconfirmed local head before applying the next update. The upstream fix for the error message itself is OpenVTC/verifiable-trust-infrastructure#2275; it does not change what the offline command can reach.

What this adds

  • internal/vta: the farm's first client for a VTA. A did:key minted for one call; login as two Trust Tasks on /trust-tasks (auth/challenge/0.1 unsigned, auth/authenticate/0.2 signed) for a bearer token; vta/webvh/dids/get/1.0 for the VTA's head of the log; vta/webvh/dids/update/1.0 with document and expectedVersionId. Every authenticated document carries the bearer token and the did:key's proof, because the VTA checks that signer, issuer and session subject are one DID. Refusals are typed (TaskError, with the VTA's details.reason, conflict among them). vta/webvh/servers/domains/0.1, a read-only task the VTA answers only by reaching the host, is the probe that waits out the window after a restart in which the VTA serves HTTPS before its mediator session is up; an update in that window would commit locally and then fail to publish, the same failure as the offline one.
  • publishRegistryReferral (internal/setup/trust_registry_referral.go) replaces the Job in EnableTrustRegistry. It skips the grant and the edit when the published document already carries the referral. Otherwise: grant the key as an admin of the VTC's context for an hour with vta acl create --role admin --contexts <VtcName> --expires 1h, in the same stopped-VTA window pattern as the registry context grant; log in; wait for the host to be reachable; read the head; send the update. When the VTA's head carries the referral but the host does not (the state the failed Job left), it sends the document unchanged, which makes the VTA reconcile the host and append a version. The verify phase is unchanged and still reads the host. The key is never stored; the entry expires on its own.
  • internal/trusttaskclient: the envelope, the eddsa-jcs-2022 proof and the reply checks that internal/didhosting carried, so both clients share one implementation. didhosting keeps its names as aliases; its tests are unchanged.
  • docs/optional-trust-registry-design.md: step 6 and the paragraph on the offline edit describe this path, the downtime note counts the second VTA window, and the host-side change that would let offline publishes work (a TrustTaskHTTPS entry on the daemon's own DID, which the daemon's readiness check already logs) is named as a separate change.

What I could not do here

  • Run it against a stack. EnableTrustRegistry has no end-to-end test on the branch (it uses ExecCapture and builds the resolver inline), and I have no staging farm. The new step sits behind the newVtaClient seam so one can be written. On dev-myvta-fs, the first run will take the "head carries it, host does not" path, since the failed Job already committed the referral at the VTA.
  • Confirm the exact wait after restart. The probe makes that a measurement rather than a guess: the step logs nothing until the host answers, and times out at three minutes with the VTA's last refusal in the error.

Verification

gofmt -l clean, go vet ./... clean, and go test on internal/vta, internal/trusttaskclient, internal/didhosting and internal/setup, all in a golang:1.26 container. The vta tests run the client against a fake VTA that verifies each request's proof and bearer, answers with signed replies, refuses an update with conflict, refuses the first probes the way a VTA without a mediator session does, and rejects a reply from another DID.

geoffturk and others added 2 commits October 23, 2026 11:17
The existing-stack enablement appended the TrustRegistry referral to the
VTC's DID document with the offline `vta did-mgmt dids edit`, run in a
Job on the VTA's PVC. That command holds no mediator session, so the
only transport it can initiate toward a DID host is REST, and a Full
Stack `dids` daemon, provisioned through the VTA with a mediator,
advertises TSP and DIDComm alone. The Job failed with "no transport in
common ... it advertises [tsp, didcomm] and this agent can initiate
[rest]" after the VTA had already committed the entry locally.

The referral is now published through the running VTA, which holds the
mediator session and publishes over either transport, and which
re-publishes an unconfirmed local head before applying the next update,
so a stack left behind by the offline attempt converges too.

internal/vta is the farm's first client for a VTA: a did:key minted for
one call, the two login tasks (auth/challenge/0.1, auth/authenticate/0.2)
over /trust-tasks for a bearer token, then vta/webvh/dids/get/1.0 for the
VTA's head of the log and vta/webvh/dids/update/1.0 with the document and
expectedVersionId. Refusals come back typed (TaskError, with the reason
the VTA gives, "conflict" among them). vta/webvh/servers/domains/0.1, a
read-only task the VTA answers only by reaching the host, is the probe
that waits out the gap after a restart in which the VTA serves HTTPS but
has no mediator session yet; an update in that gap would commit and then
fail to publish.

publishRegistryReferral (internal/setup/trust_registry_referral.go)
replaces the referral Job: it skips everything when the published
document already carries the referral; otherwise it grants the key as an
admin of the VTC's context for an hour (`vta acl create`) in the same
stopped-VTA window pattern as the registry context grant, logs in,
waits for the host to be reachable, reads the head, and sends the
update. When the VTA's head carries the referral but the host does not,
it sends the document unchanged, which makes the VTA reconcile the host
and append a version. The verify phase still reads the host.

The envelope, proof and reply checks the DID hosting client carried move
to internal/trusttaskclient so the two clients share one implementation
of eddsa-jcs-2022; didhosting keeps its names as aliases and its tests
are unchanged.

docs/optional-trust-registry-design.md: step 6 and the paragraph on the
offline edit now describe this path, and name the host-side change that
would let offline publishes work (a TrustTaskHTTPS entry on the daemon's
own DID), which is separate from registry enablement.

Verified with gofmt, go vet ./... and go test on internal/vta,
internal/trusttaskclient, internal/didhosting and internal/setup, all in
a golang:1.26 container. EnableTrustRegistry itself still has no
end-to-end test; the new step is behind the newVtaClient seam for one.

Signed-off-by: Geoff Turk <geoff@ic3.dev>
Reduce the exposure window for the ephemeral VTA administrator grant while preserving enough time for the bounded registry workflow.

- Change the referral ACL expiry from one hour to fifteen minutes
- Keep the design documentation aligned with the implementation

Signed-off-by: vthwang <vthwang.work@gmail.com>
@vthwang
vthwang self-requested a review October 24, 2026 05:59
@vthwang
vthwang merged commit cd62db8 into feat/optional-trust-registry Oct 24, 2026
1 check passed
@vthwang
vthwang deleted the feat/optional-trust-registry-online-referral branch October 24, 2026 06:00
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.

2 participants