Verifiable Trust Infrastructure (VTI)
Repo: github.com/OpenVTC/verifiable-trust-infrastructure
The Verifiable Trust Infrastructure is a Rust workspace containing the core services that power Verifiable Trust Communities. It’s the infrastructure layer of the OpenVTC ecosystem — the plumbing that makes decentralized trust work.
Components
The workspace stands at 32 members at the VTI-Eucalyptus tag (2026-10-07): in late July 2026 the monolithic vta-service was decomposed into eleven subsystem crates (docs/05-design-notes/vta-service-decomposition.md), September 2026 added a twelfth holder store (vta-persona) plus the four data-rooms crates, and the Eucalyptus window added vti-vetting-pcs — the VTC’s half of zero-knowledge hidden-vetter admission, a publish = false development crate reached only through vtc-service’s off-by-default vetting-pcs feature. Versions are shown at the coordinated Cypress release (coordinated-releases, 2026-08-17), at VTI-Dogwood (2026-08-30), at VTI-Eucalyptus-RC-0 (2026-09-17), at the 2026-09-18 interim chore: release (#1552) and at VTI-Eucalyptus (2026-10-07, #1985). ★ = new since Cypress. Because release-plz only bumps published crates, the publish = false members (vtc-service, vta-enclave, vta-mobile-core, vta-mcp, didcomm-test, room-host, vti-rooms-wasm, vti-vetting-pcs, tests/e2e) keep a frozen manifest version even though their code moved — read those numbers as labels, not as release markers. The size of the Eucalyptus jumps (vti-common 0.19 → 0.40, vta-service 0.34 → 0.56 in nineteen days) is release-engineering, not drama: 95 of the window’s 399 commits were marked breaking, and since #1807 moving a sibling’s compatibility range is itself a breaking bump, so every chore: release ratcheted the whole closure.
| Crate | Purpose | Cypress | Dogwood | Euc-RC-0 | 09-18 | Eucalyptus |
|---|---|---|---|---|---|---|
| vta-service | The VTA server “spine” — the Trust-Task dispatch spine (sender binding, proof purpose, replay/freshness/idempotency gates), the HTTPS /trust-tasks binding (the superseded REST routes are gone since #1858), outbound transport seam and durable push engine, orchestration; re-exports the subsystem crates | 0.17.0 | 0.23.3 | 0.33.0 | 0.34.0 | 0.56.0 |
| vta-sdk | Public client SDK / data model — consumed by eight sibling repos (OpenVTC, did-hosting-service, the TDK mediator, VGI, the browser plugin’s type bindings …); every client carries an identity, signs what it sends and verifies what it receives; since Eucalyptus one Trust Task surface (the protocol-message leg is gone), purpose-bound proof verification, PUBLIC_URIS, task-consent helpers | 0.25.0 | 0.32.2 | 0.42.1 | 0.43.0 | 0.64.2 |
| vta-config | AppConfig TOML shape and sub-configs (runtime-tunable [server] rate-limit quotas with trust_xff_cidrs, [did_cache], [fjall] memory tuning) | 0.3.8 | 0.4.1 | 0.5.2 | 0.5.3 | 0.17.0 |
| vta-keyspaces | Keyspace-name registry (shared storage vocabulary); BACKED_UP is now what a backup walks | 0.1.4 | 0.2.4 | 0.2.9 | 0.2.9 | 0.13.0 |
| vta-audit | Audit sink: audit! macro, best-effort recording, and (since 0.3.x) a hash-chained, keyed-actor log with a verify pass | 0.1.6 | 0.3.1 | 0.3.12 | 0.3.13 | 0.12.0 |
| vta-keys | Master-seed storage, BIP-32 derivation, key wrapping, seed-store backends; non-extractable internal keys; ML-DSA-44/65 post-quantum keys, per-key exportable, a domain-separated opaque signing oracle; since Eucalyptus the key-custody rules (custody.rs — a path belongs to one context, only a super-admin chooses one) and SSHSIG signing for git commits | 0.2.5 | 0.4.1 | 0.6.0 | 0.6.1 | 0.17.0 |
| vta-vault | Holder credential vault: store / query / receive / verify / present / status-refresh — Data Integrity, BBS, SD-JWT, ISO mdoc; signing loaders held to the resource’s context | 0.3.0 | 0.5.1 | 0.5.12 | 0.5.13 | 0.15.0 |
| vta-webvh | WebVH DID-record store; since #1789 the store only — the REST client to a did:webvh host and the DID-auth handshake are gone, the host is reached with Trust Tasks | 0.1.9 | 0.2.2 | 0.2.13 | 0.2.14 | 0.13.0 |
| vta-policy | Regorus (Rego) Policy Decision Point, default policy bundle, decision evaluators (the task-consent core moved down to vti-common so the VTC can share it, #1730) | 0.2.7 | 0.3.1 | 0.3.12 | 0.3.13 | 0.12.0 |
| vta-support | Mid-layer services: trust-context storage, sealed-transfer helper, sealed-bootstrap nonce store | 0.2.7 | 0.3.1 | 0.3.12 | 0.3.13 | 0.14.0 |
| vta-tee | Nitro / SEV-SNP attestation providers, KMS attest/decrypt + storage-key derivation, anchor MAC, first-boot DID autogen; attestation reads are public Trust Tasks | 0.1.8 | 0.2.1 | 0.2.9 | 0.2.9 | 0.16.0 |
| vta-backup | Encrypted full-state export/import; since 0.16 the vta-backup-v2 whole-agent format — every BACKED_UP keyspace, a staged restore committed through RestoreCommitter and applied on re-exec, portable between plain, hardened and Nitro VTAs | 0.1.10 | 0.3.1 | 0.4.1 | 0.4.2 | 0.16.0 |
| vta-sweepers | Background TTL sweepers (ACL grant expiry, pending consent, soft-deleted vault purge, persona binding expiry) | 0.1.3 | 0.3.2 | 0.3.7 | 0.3.7 | 0.12.0 |
| ★ vta-persona | The holder’s own identity: attribute pool, faces (profiles) projected over it, bindings to persona DIDs, contacts peers disclosed — implements persona/*; the fourth holder store. Since Eucalyptus: entry slots, retire/reinstate, compose a face in the context that asks for it, promote, worlds, derived provenance | — | — | 0.3.9 | 0.3.9 | 0.17.0 |
| vta-enclave | AWS Nitro Enclave binary (TEE mode) — not published; tsp now a default feature | 0.7.7 | 0.7.7 | 0.7.7 | 0.7.7 | 0.7.7 |
| vta-mobile-core | UniFFI engine behind the Authenticator + PNM mobile apps (DIDComm/TSP, Trust Tasks, AAL step-up); Android AAR / iOS xcframework — not published (version frozen; code now signs every request under authentication and verifies every reply, #1744) | 0.6.18 | 0.6.18 | 0.6.18 | 0.6.18 | 0.6.18 |
| vta-mcp | MCP stdio server bridging VTA capabilities to MCP hosts (Claude Code / Claude Desktop) — not published; local operation guard, per-call logging, docs/02-vta/vta-mcp.md | 0.1.5 | 0.1.5 | 0.1.5 | 0.1.5 | 0.1.5 |
| vti-webauthn | DID-VM-resolved WebAuthn verifier | 0.1.1 | 0.2.0 | 0.2.2 | 0.2.2 | 0.2.2 |
| vtc-service | VTC daemon — community lifecycle, policies, credentials, personhood, vetting, trust graph, public website, admin console; since Eucalyptus git namespaces, role-based administration with an action list, hidden vetting (vetting-pcs) — not published (publish = false, version frozen) | 0.11.58 | 0.11.58 | 0.11.58 | 0.11.58 | 0.11.58 |
| vtc-client | Thin client SDK for a VTC — the VTC counterpart to vta-sdk; any DID method can hold (0.6); every call a signed Trust Task (#1840); git-ns, actions and task-consent helpers | 0.3.7 | 0.5.1 | 0.6.8 | 0.6.9 | 0.19.0 |
| vti-common | Shared auth, ACL, storage, config, error handling; context_path; capability enforcement; the audit-chain writer both services use; since Eucalyptus the shared task-consent core, durable Trust Task push engine, tsp_route, tsp_reach, inbox watch and [did_cache] | 0.12.1 | 0.16.0 | 0.19.1 | 0.19.2 | 0.40.0 |
| vti-secrets | Pluggable secret-store backends (plaintext, HashiCorp Vault, KMS/TEE, Kubernetes Secrets); cached seed reads | 0.1.14 | 0.3.1 | 0.3.12 | 0.3.13 | 0.13.0 |
| vta-cli-common | Shared CLI command implementations (persona, consent, QR rendering, bundle opening) | 0.11.0 | 0.12.2 | 0.17.1 | 0.17.2 | 0.37.0 |
| cnm-cli | Community Network Manager (multi-community client); cnm vetting (incl. bootstrap-pgp), cnm git, cnm actions, cnm consent, one admin identity per community | 0.11.22 | 0.13.2 | 0.16.3 | 0.16.4 | 0.30.0 |
| pnm-cli | Personal Network Manager (single-VTA client); pnm memory, pnm persona (worlds, claim types), pnm rooms, pnm consent, pnm vta qr, pnm messaging grant; usable from a script (#1753) | 0.12.6 | 0.14.2 | 0.17.2 | 0.17.3 | 0.36.0 |
| ★ vti-rooms | Data-room storage, wire types and authorization — the parts of a room that are not a service (MLS group custody via openmls, record sealing, epoch key chain) | — | — | 0.2.9 | 0.2.9 | 0.11.0 |
| ★ vti-rooms-dtg | The DTG-credential chain verifier for data rooms (DTG v1 context since #1859) | — | — | 0.2.7 | 0.2.8 | 0.13.0 |
| ★ vti-rooms-wasm | A room’s member half compiled to WebAssembly so a browser can be a room member — not published | — | — | 0.1.0 | 0.1.0 | 0.1.0 |
| ★ room-host | Stores data-room records for rooms it does not govern — “a delivery service, not a community”; speaks Trust Tasks over DIDComm and TSP — not published | — | — | 0.1.0 | 0.1.0 | 0.1.0 |
| ★ vti-vetting-pcs | Hidden-vetter admission (ZKP): the VTC’s half of the Predicate Credential System path — verifies an applicant’s proof that k distinct vetters vetted them and turns it into the StatementFacts the VTC already counts, each vetter’s tag where a DID would be — not published; development branch zkp-pcs, mirrored from OpenVTC’s openvtc-vetting-pcs | — | — | — | — | 0.0.0 |
| didcomm-test | DIDComm connectivity test harness — not published | 0.6.9 | 0.6.9 | 0.6.9 | 0.6.9 | 0.6.9 |
| tests/e2e | In-process end-to-end harness (MockVta / MockVtc) — not published | 0.6.0 | 0.6.0 | 0.6.0 | 0.6.0 | 0.6.0 |
VTI-Dogwood-R1 (2026-09-01) differs from Dogwood by a patch bump on eleven crates (vta-service 0.23.4, vta-sdk 0.32.3, vti-common 0.16.1, pnm 0.14.3, cnm 0.13.3, vti-webauthn 0.2.1, vta-keys 0.4.2, vta-vault 0.5.2, vta-tee 0.2.2, vta-policy 0.3.2, vta-webvh 0.2.3, vta-backup 0.3.2, vta-cli-common 0.12.3); see the Recent Development log for what those carry. (vti-fuzz is a nested cargo-fuzz workspace, not a member.) Layering: L0 vti-common / vta-sdk / vti-secrets → L1 keyspaces / config / audit → L2 support / keys / vault / webvh / policy / persona → L3 tee / backup / sweepers → L4 vta-service; the rooms crates hang off vta-sdk + dtg-credentials and are consumed by vta-service, vtc-service, vtc-client and room-host; vti-vetting-pcs hangs off vta-sdk (vetting feature) and predicate-credential-system, and is reached only from vtc-service’s vetting-pcs feature.
The vti-didcomm-js crate (JavaScript DIDComm primitives + spec test vectors) was extracted into its own repository in May 2026 — see vti-didcomm-js. Publishing moved in August 2026 to release-plz (#938): merging a PR is no longer releasing — release-plz keeps one chore: release PR open, and merging that tags each crate (<crate>-v<ver>), generates per-crate CHANGELOGs from conventional commits, runs cargo-semver-checks, and publishes via crates.io trusted publishing (GitHub OIDC). 23 of 32 members publish; the nine internal ones are vtc-service, vta-enclave, vta-mcp, vta-mobile-core, didcomm-test, tests/e2e, room-host, vti-rooms-wasm and vti-vetting-pcs (RELEASING.md still says “20 of 26” and “six internal” — it predates the September and October crates; a loose end carried over from the last cycle). Since September the semver report is a gate: a Release PR whose bump is smaller than its API change is blocked (#1256), after two exhaustive enums and sixteen wire structs nearly shipped as patch releases — which is why so many wire types became #[non_exhaustive] (#1262, #1270, #1271, #1502). The Eucalyptus window added a second guard after release #1795 halted at vta-backup and left vta-service 0.44.0 unbuildable from a fresh resolve: a published crate’s dev-only dependency on a sibling must carry no version, and moving a sibling’s compatibility range is a breaking bump (scripts/check-release-bump-sizes.py, #1807; the first catch was #1852, vta-tee 0.7.0 and vta-webvh 0.4.0 instead of patches). Twenty-nine chore: release merges landed between Cypress and 2026-09-18 and thirty-six more between 09-18 and the Eucalyptus tag — roughly two a day. The workspace states that it implements the ToIP VTI specification and that the specification is normative where the two disagree (#1403); the window’s commits cite its requirement ids (VTI-ACL-052, VTI-APV-014, VTI-OPS-021, …) in their titles, and several spec changes (trustoverip/dtgwg-vti-spec#54, #55) were merged upstream to match what was built.
How It Fits in the Stack
VTI sits in the middle of the ecosystem stack:
┌─────────────────────────────┐
│ OpenVTC CLI / TUI │ ← User-facing tools
├─────────────────────────────┤
│ VTI (VTA + VTC services) │ ← This layer: key management, signing, coordination
├─────────────────────────────┤
│ Affinidi TDK + WebVH │ ← DID resolution, messaging, DID hosting
├─────────────────────────────┤
│ didwebvh-rs │ ← DID method implementation
└─────────────────────────────┘
Applications at the top (like OpenVTC) use VTI to manage keys and sign things. VTI in turn uses the Affinidi TDK for DID resolution and messaging, and the affinidi-webvh-service for DID hosting.
Dependencies
Key external dependencies (versions at the VTI-Eucalyptus tag, 2026-10-07; 09-18 values in brackets):
affinidi-tdk0.23 [0.16] — DID resolution, messaging, data integrity proofs;affinidi-messaging-sdk0.33.2 taken directly (the mediator now serves Trust Tasks only, and routes a cross-mediator TSP invite itself);affinidi-messaging-delivery0.1.20 — the reliable outbox layer both services run on, now with the SDK deleting frames a node cannot unpack;affinidi-tspat the TSP Rev 3 line;affinidi-data-integrity0.8 [0.7] with theml-dsafeature on andaffinidi-bbs0.4;affinidi-did-resolver-cache-sdk≥ 0.8.39 so a deactivated did:webvh no longer resolves to its last documenttrust-tasks-*0.27.7 [0.21] — the ToIP Trust Tasks document framework every wire operation is expressed in;trust-tasks-https(binding 0.3 streamed responses),-didcomm,-tsp,-proof,-capability-clientbindings. Six minor releases in nineteen days, each pulling newly published specs: worlds (0.22), attestation and public tasks (0.23), the mediator cut andtrust-task-discovery/0.3(0.24), DTG Credentials v1 andwebvh/dids/create/1.1(0.25), the DI 0.8 cascade andauth/signing-key/enroll/0.2(0.26), the action list, git-ns PR gate, hidden vetting andkeys/sign-sshsig(0.27)didwebvh-rs0.8 [0.7] — did:webvh operationsdtg-credentials0.13 [0.9.1] — trust graph credential types at the DTG Credentials v1 context (issuerScope, one subtype, VAC/VSC shapes); the data-rooms verifier sits on itpredicate-credential-system0.1 (crates.io, viavti-vetting-pcsonly) with the arkworks BLS12-381 stack — hidden-vetter zero-knowledge admission;pgp(rPGP) 0.20 —cnm vetting bootstrap-pgpreads an OpenPGP web of trustaffinidi-mdoc0.3 /coset0.4 — ISO mdoc;regorus0.12 — Rego policy engineopenmls— MLS group custody for data rooms (vti-rooms)fjall3 — embedded key-value storage, now with operator-tunable block cache / write buffer / journalaxum0.8 — async HTTP framework;governor— runtime-tunable rate limiting keyed on trusted-proxy CIDRsed25519-dalek3 /curve25519-dalek5 — Ed25519/X25519 cryptography (SLIP-0010 derivation in-tree); ML-DSA viaaffinidi-secrets-resolver(ml-dsafeature);rand0.10 workspace-wide, held at 0.8 insidevti-vetting-pcsbecauseark_std::randre-exports ituniffi0.32 — the mobile bindings;aes-gcm0.11,jsonwebtoken11,rustls0.23,tokio1.49
Tech Stack
- Language: Rust (edition 2024,
rust-version1.95) - Async runtime: Tokio
- HTTP: Axum 0.8
- Storage: fjall (embedded LSM)
- Crypto: ed25519-dalek 3, x25519-dalek, p256; ML-DSA-44 / ML-DSA-65 post-quantum signing (since September 2026 — ML-DSA-44 for W3C Quantum-Resistant Data Integrity suites, ML-DSA-65 as TSP Rev 3 §8.1 mandates); MLS (openmls) for data rooms; BLS12-381 pairing-based zero-knowledge proofs (arkworks, via
predicate-credential-system) for hidden-vetter admission, behind the off-by-defaultvetting-pcsfeature - Auth: every remote operation is a signed Trust Task (HTTPS
/trust-tasks, the DIDComm binding envelope, TSP); EdDSA JWTs remain for sessions, with refresh-token reuse detection; every response is signed with the node’s operational key underproofPurpose: authenticationand clients verify it; a proof’s key is checked against the relationship its purpose names - Supply chain: Dependabot-managed bumps, every GitHub Action pinned to a commit SHA, least-privilege CI tokens,
cargo denyacross all features
Recent Development
This page is the canonical activity log for the VTI workspace. The VTA entity keeps a focused, VTA-relevant subset. Implementation continues to evolve quickly; treat low-level details as in flux.
The Eucalyptus window (2026-09-18 → 10-07: 399 commits, PRs #1562 → #1985; 378 of them by one maintainer, 13 by Dependabot, 8 by four others; 36 chore: release merges; 95 commits marked breaking and 19 security(...) commits) closes the arc that VTI-Eucalyptus-RC-0 opened, and the VTI-Eucalyptus tag landed on 2026-10-07 at #1985 (vta-sdk 0.64.2) — one of eighteen repositories tagged together that day (coordinated-releases). Like Dogwood, the tag carries only the description “VTI Eucalyptus”; the release notes live with the coordinated release. If the RC window was about what the agent holds (rooms, persona, post-quantum keys), the final window was about who may do what, and how we know: every remote operation on both services is now a signed Trust Task — the REST routes are gone, the DIDComm protocol-message surface is gone, and a document’s proof, not the transport that carried it, is what authorizes it. On top of that single door the VTC grew role-based administration with an N-of-M action list, git namespaces that govern GitHub and Forgejo repositories and publish rights to the Trust Registry, DTG Credentials v1 shapes, and hidden vetting on a zero-knowledge Predicate Credential System; the VTA closed its key-custody holes, made TSP the default, and learned to sign git commits without ever exporting a key. The release-arc summary says it in nine words: signed Trust Tasks only, data rooms, vetting, faces, git namespaces, role-based admin, ML-DSA keys.
Eucalyptus — 2026-10-07 — the tag
Snapshot versions are in the Components table above: vta-service 0.56.0, vta-sdk 0.64.2, pnm-cli 0.36.0, cnm-cli 0.30.0, vti-common 0.40.0, vtc-client 0.19.0, vta-persona 0.17.0, vti-rooms 0.11.0; vtc-service stays at its frozen 0.11.58 (publish = false, so release-plz never bumps it, though it changed more than any other crate). Dependencies at the tag: trust-tasks-rs 0.27.7, dtg-credentials 0.13, didwebvh-rs 0.8, affinidi-tdk 0.23, predicate-credential-system 0.1 (see Dependencies). The last commits before the tag were the operator-facing finish: the VTC console explains itself and its Recognition page lists every trust record under the community’s authority, git rights included (#1984, #1983); the request log names the Trust Task and logs TSP requests at all (#1982); and the messaging stack notices a node that has stopped collecting its inbox (#1978, #1979 — below). Three field failures from OpenVTC deployments were fixed inside the window and are worth knowing because they explain several of the “fix(messaging)” titles: a persona on one mediator joining a community on another stayed Pending because the VTC’s TSP reply never reached it (#1894); two admin sessions that stopped collecting filled their mediator inboxes until every further reply was refused 503 e.p.limits.queue.peer (#1978); and a provision/integration reply lost to a Cloudflare 1015 at the mediator’s edge left a VTC setup timed out with its DID minted (#1906).
Signed Trust Tasks only — the REST surface retires on both services — 2026-09-23 → 10-01
The largest stream, and the one the release is named for. At RC-0 the VTC still served some fifty bearer-token REST routes beside the Trust-Task door, the VTA some fifty-six routes it had marked superseded, and both accepted DIDComm protocol messages typed as a task URI. By 10-01 none of that exists. On the VTC the console first learned to sign: a console signing key is a credential of the operator’s admin DID (#1692), the console signs its documents (#1695), and the keys became auth/signing-key Trust Tasks with a wallet-authorized enroll/0.2 that replaces a key in one act (#1836, #1903) — then a run of “served as signed documents” commits moved join decisions and profile edits (#1689), the admin member tasks (#1681), config export/import (#1702), backup export and the chunked backup/* transfer (#1708, #1724), endorsement-type writes (#1705), acl/grant and acl/change-role with the passkey gesture bound to the document (#1718, #1719, vtc-operation-bound-step-up.md), acl/{show,list,update,revoke} (#1772), the administrator’s operational and community verbs (#1824, #1827), the policy log and the community’s own DID log (#1834), the REST-only admin surfaces (#1835), git-ns admin reads (#1781, #1841), website content (#1839) and the member verbs — renew, rotate, personhood, relationships, endorsements (#1809, #1845, #1846). The console’s signed calls have no bearer twins (#1808), every vtc-client call is a signed Trust Task (#1840, breaking), and the console refuses to show a dashboard until signing is set up (#1899). On the VTA: health, restore status, session revoke and the wrapping key (#1790); the TEE attestation reads as public Trust Tasks over any transport (#1776 — PUBLIC_URIS, the tasks a caller may send with no identity, machine-checked to be proof-optional and read-only); service management (#1782); provision/integration as one task over TSP, DIDComm or HTTPS (#1785); the did:webvh hosting service reached with Trust Tasks only, the REST client and DID-auth handshake deleted (#1789); the last legacy backup and webvh surfaces removed and realign-keys a task (#1783); /cache and /acl/swap removed (#1786); and finally #1858 (breaking): the ~56 superseded REST routes are deleted and pre-session auth (auth/challenge, authenticate/0.2+0.3, refresh/0.2) moves onto /trust-tasks, dispatched by a family-owned bypass ahead of the ACL pipeline on all three transports. What remains as REST is declared in a machine-checked REST_EXCEPTIONS table: the passkey verification-method routes (the browser driving a WebAuthn ceremony holds no DID key to sign with, #1793), POST /bootstrap/request, the backup blob, openapi.json, the TEE mnemonic window and /metrics. The SDK followed: one Trust Task surface — VtaClient::protocol_message_transport and rpc are gone (#1793, breaking), every credential-exchange step is a signed task on the spine over any transport (#1771), a Trust Task over DIDComm rides the binding envelope only (#1687, breaking — twelve task-typed arms removed on the VTC, the bare self-remove/vmc arms had been skipping freshness and proof checks), and refusals stay signed trust-task-error documents rather than DIDComm problem-reports (#1800). pnm became usable from a script (#1753, breaking). Both nodes advertise their ten-minute acceptance window over trust-task-discovery/0.3 (#1817).
A document’s proof is the authority — sender binding, proof purpose, signed replies — 2026-09-22 → 09-27
Closing the door was only useful if what comes through it is checked. The VTC’s dispatcher had verified a proof when one was present and otherwise trusted the transport; #1659 (breaking) enforces what each specification declares — recipient, proof, audience, issuedAt within 10 min + 60 s skew, a replay record bounded to that window — on every transport, and #1672 flips “a declared proof is required” to unconditional once OpenVTC (openvtc#371) signed its five join documents, deleting the switch and recording that no setting may put it back. Then #1739 (breaking): every DIDComm and TSP Trust Task needs a Data Integrity proof that verifies as its issuer, and that issuer must be the transport’s sender (proofRequired / proofInvalid / identityMismatch); the sender-authorized protocol-message surface it replaces is removed outright. Both services sign every response, errors included, with their operational key under proofPurpose: authentication (#1740, VTI-KEY-106), and a verifier now checks a proof’s key against the relationship the proof claims (#1752, breaking — a key published only for keyAgreement could make an assertionMethod proof; a proof set is accepted only if every proof verifies). An approver’s decision needs assertionMethod (#1757); the mobile engine signs under authentication and verifies every reply, including errors (#1744). Lower down: an authcrypt sender is trusted only when skid, apu and the key actually used agree (#1732); one bounded DID-document cache per node with a 60 s TTL and one re-resolution before a verification fails, so a revoked key stops verifying within a minute and a rotated peer is not refused as a forger (#1737, VTI-KEY-134); a resolver failure is told apart from an invalid proof on the two caller-side paths where that is not an oracle (#1748, FTL-29595); a signed REST sign-in must be addressed to the service that receives it, closing a relay (#1646); the challenge is consumed atomically before minting (#1664). Design note: vtc-trust-task-proof-enforcement.md.
Key custody and the scope sweep — 2026-09-21 → 09-26
Reported as FTL-29904: a context-scoped admin could list and rotate the instance-wide seed. Reviewing it found four holes with one shape — a gate that asked about the caller’s role or context, never about the path or key the caller named — including keys/create with a derivationPath deriving any key (another tenant’s, the VTA’s own did:webvh update key) and exporting it, and a vault entry naming {vta_did}#key-0 as its signing key so the VTA signed as itself. #1715 (breaking) states the custody rules and enforces them in the operations, not the transports (vta-keys/src/custody.rs, key-custody.md): seed state is instance-wide; a context’s key derives from that context’s base and no other; only a super-admin chooses a path; m/26'/9' is the sign-only delegated-identity subtree; rules are checked at use, so a planted record is inert; every refusal is audited. The sweep that followed scoped sessions and consent grants to the caller’s authority (#1717), step-up approval (which had signed “this VTA approved a step-up” for any authcrypt sender), audit verify, policy reads and device management (#1720), applied the key-export and sign capability checks on every transport — private keys are no longer released over REST or the HTTPS binding at all (#1733, #1743, breaking) — made key-export its own capability and DID minting need KeyMint rather than admin (#1619), gated vta/contexts/secrets on KeyExport (#1634), and closed the ACL’s self-service: no principal widens its own entry and no grant exceeds its granter (#1738, VTI-ACL-052/053 — a companion service’s credential could clear its own narrowing, and swap-key rebuilt an entry field by field so a one-hour bootstrap grant became permanent). Also: no type derives Debug over secret material — about 55 types across twelve crates, kept closed by a syn census (#1711); refresh-token reuse detection with a 30-second innocent-retry grace, revoking the session a replayed token belongs to (#1650, RFC 9700), and the superseded refresh token retired when a DID logs in again (#1683); the per-IP limiter keyed on trusted-proxy CIDRs instead of a global XFF flag (#1562, breaking config); a deactivated did:webvh no longer resolves to its last document (#1881 — the one commit on the public SEC-4045 thread, shared with VGI). Holder authority over the persona pool is granted, never inherited — super-admin had carried it anyway (#1673); did:webvh key rotation now rotates each method in place with its own algorithm instead of republishing X25519 and ML-DSA methods with Ed25519 keys (#1734, breaking); and a backup is the whole agent — format vta-backup-v2 walks every BACKED_UP keyspace (ten had never been exported), stages the import, commits the seed and re-execs, and restores between plain, hardened and Nitro VTAs in all eight pairings (#1655, breaking; backup-restore-portability.md).
DTG Credentials v1 — role VACs, vetted/1, witnessed/1, issuerScope — 2026-09-23 → 10-02
dtg-credentials moved 0.9 → 0.11 (#1669) → 0.13, and #1859 (breaking) conforms every credential the VTC issues and the SDK reads to the DTG Credentials Core v1 context: roles are Authority Credentials whose action is role:<name> with maxAttenuation: 0; endorsements, witnessing and vetting are Statement Credentials under a registry predicate (endorses/1, witnessed/1, vetted/1, presented/1 — the four core predicates seeded at first boot as the community’s fail-closed accept list); every DTG credential carries a top-level issuerScope; the retired EndorsementCredential and WitnessCredential types are refused on ingress with no alias; roleVec became roleVac on the wire. Room credentials declare issuerScope under the v1 context too. Because Rego treats a missing field as undefined, a custom policy written against the old shapes silently denies — docs/03-vtc/policy-upgrade-dtg-v1.md (#1871) walks operators through foreign_vec → foreign_vac, the witnessed digest moving to object.digestMultibase, and the community’s own identity check, which is no longer a plain IdentityVerificationCredential but a vetted/1 statement issued under the community’s own DID (#1874 — the registry’s vetted/1 admits the community as issuer, with the three vetter-only members optional and all-or-nothing; a statement without them is personhood evidence, never admission evidence). That statement can be bound server-side to a uniqueness pseudonym for personhood.singleMembership (#1876), is delivered to its subject over credential-exchange/issue like the role VAC (#1898), and an approved applicant can ask for its credentials again when delivery was lost (#1900). docs/03-vtc/credential-delivery.md is new: an admitted member receives two credential-exchange/issue pushes, one per credential, through the durable push engine, in no guaranteed order. See dtg-credentials, dtg-credentials-overview.
Vetting — hidden vetters on a Predicate Credential System, and the first-vetter problem — 2026-09-21 → 10-06
peer-identity-vetting was designed for a concrete deadline: OpenVTC’s docs/design/vetting-process.md targeted V0 at 2026-10-05 in Prague, ahead of the Linux Kernel Maintainer Summit (10-08), and the project was presented at the Linux Plumbers Conference on 10-07 (“Cryptographic Proofs of Personhood: Solving the Kernel Web of Trust Problem in a Privacy-Preserving Manner”). The VTI half of the window reflects that. Named vetting gained the pieces a real community hits first: a community can ask an applicant to tell it about themselves (requestedAttributes on the manifest, self-asserted and never fed to policy; cnm vetting ask, #1614, breaking), an applicant can withdraw or supplement a request in place (#1591, #1593), a by-DID vetter status lookup (#1671), the cnm vetting/audit/backup commands authenticate to the VTC with the VTC’s DID as audience rather than failing against the VTA’s (#1637), and join criteria state their own admission — automatic admits, review refers to an administrator (#1907, Keyring VTI-13). The bootstrap runbook (docs/03-vtc/bootstrap-runbook.md, #1627) records two Keyring findings that needed documentation, not code: a fresh VTC’s ACL is empty including for the admin DID setup prints, and an admin is not a member, so the first vetter is a separate identity admitted before any criterion requires vetting. cnm vetting bootstrap-pgp, which seeds a community’s first vetters from an existing OpenPGP web of trust (keyring + root keys + clearsigned link statements, everything local verified before the community is contacted), landed at the end of the RC window (#1432) and is what that runbook’s Part 2 points a kernel-shaped community at. Hidden vetting is new in the window: vti-vetting-pcs (#1838) lets an applicant prove, in zero knowledge, that k distinct vetters of this community vetted them; the VTC verifies the proof and counts it, learning nothing about which vetters — each vetter’s tag stands where their DID would, so requirements::evaluate and join.rego are untouched, a spent-token ledger is a new backed-up keyspace, and the whole path sits behind vtc-service’s off-by-default vetting-pcs feature on the zkp-pcs development branch (the predicate-credential-system crate was vendored, then taken from crates.io). The console turns it on, off, reads it back and runs vetting events (#1955, #1956 — vtc/vetting/hidden/{publish,withdraw,show}/0.1, with approvals recorded in the approving administrator’s own name); the attestation drip is a window of time (tickLength, default P3D), a signing-key change refuses to re-key silently, and it runs on one criterion (#1960); a vetter who lost the enrollment answer is re-issued it to the same identifier only, three times per label (#1972); and republishing the drip rate no longer voids every attestation in flight, which had held a live join (#1977). Refusals say why (#1954, #1961, #1976). See zero-knowledge-proofs.
Git namespaces — forge governance published to the Trust Registry — 2026-09-24 → 10-06
A VTC can now govern repositories on the forges its community uses — GitHub, a Forgejo instance such as Codeberg — and publish who may do what there to its Trust Registry, where did-git-sign verify-trust reads it (docs/03-vtc/git-namespaces.md; the consumer is verifiable-git-infrastructure). #1694 landed the git-ns/* family: a namespace binds the VTC to one owner on one forge; rights (git.ns.admin, git.repo.create, git.repo.own, git.repo.maintain, git.commit.sign) are held by DIDs on forge-qualified resources keyed by an internal repository id so a rename moves them; the fixed rules are code, not policy (whole-segment containment, no escalation, repo.create not re-delegable, a repository keeps an owner, a namespace keeps an admin, members only) and the community’s gitNamespace Rego policy can only refuse; one TRQP authorization record per live right is written to the registry with the implied git.commit.sign of every owner, maintainer and admin — who granted a right and why is never published; bridge jobs travel signed over TSP or DIDComm; a namespace admin gets no role on the forge (#1729). cnm git {namespace,repos,grant,revoke,adopt,view,link,unlink,drift,reseat} and the console’s Repos plugin (#1696) followed, with drift resolve and headless-namespace reseat (#1703), forge-account linking with one account per member (#1726, #1742, #1746), separation of duties and a ratified break-glass for elevated rights (#1745), and view/0.4 requiring a proof (#1770). Then #1930 (breaking) folded the rights store into the ACL — one authority model: every git right is a resource grant on its holder’s own entry (git.ns.admin at git-ns:github.com/acme; git.repo.manage graded create/own/maintain; git.commit.sign), bounded by the granter’s own entry, with a departed granter’s grants going to review; a non-member holding a grant (the bridge, an external signer) holds it on an entry of the new community role application, which never signs in. A pull-request gate closes PRs from openers the policy does not allow — “hygiene, not the merge gate” (#1937, bridge/event/0.4, bridge/job/0.5); single-administrator mode waives the self-grant rule where nobody else is eligible (#1936); a suspended subject’s rights are withdrawn from registry and forge for the duration of a cooling-off (#1945); a community serves governance/capability/* so OpenVTC’s Capabilities panel can list and enable capability modules with the registry following (#1948). On the VTA side, keys/sign-sshsig/0.1 signs a git commit without exporting the key (#1957): did-git-sign had been calling keys/export-secret on every commit because the opaque oracle frames caller bytes under a domain tag — exactly the hardening that made SSHSIG impossible — so the VTA now builds the PROTOCOL.sshsig signed data itself under a constrained sign-sshsig capability.
Role-based administration, consent and the action list — 2026-09-25 → 10-05
At RC-0 a VTC administrator was “an admin entry with an empty context list”, one stolen admin credential with its passkey could mint a second unrestricted admin, and a context-scoped admin could invite a DID it controlled into community-wide authority. The window rebuilt this in three phases, each with a design note first (#1914: vtc-admin-roles.md, vtc-action-list.md, vtc-approver-step-up.md; operator guide docs/03-vtc/admin-access.md with an infographic, #1915, #1933). Consent. The task-consent core moved into vti-common so the VTC runs the VTA’s own ceremony (#1730): making or widening an unrestricted administrator needs another unrestricted administrator’s consent (#1741, VTI-APV-014), invites and attrition respect it so the last administrator able to consent cannot step down behind a scoped one (#1747), a co-admin can be installed at setup and the offline ACL break-glass is audited (#1750), and approvers answer from cnm consent or pnm consent as well as a device (#1759, #1761). The action list (#1918, breaking, VTI-APV-017): a consent-gated operation is no longer refused and re-sent — it is parked in a new admin_actions keyspace with the requester’s signed document verbatim, answered trust-task-next-step (202), and completes by itself on the N-th approval, re-running every check against the community as it then is; one deny closes it, it expires, and it is invalidated when the pinned state moves. Removing an administrator, lowering the consent threshold and changing an authority-deciding policy take a second party (#1917); operator writes made offline must be acknowledged at the next boot, a reduction sends its subject a signed notice and an unopposed reduction waits out a 24-hour cooling-off during which the subject is suspended (#1920, #1944, #1922); an approver device can be an administrator’s step-up factor — the browser plugin’s approver identity answers for a wallet-signed-in admin who holds no passkey (#1919, VTI-APV-015/016); once a dedicated step-up passkey exists it is the only gesture that counts (#1850; the VTA matches it, #1854); members get self-revoke and takeover notices for their step-up passkeys (#1872, #1875). Roles. #1924 (breaking): administration is role-based — a typed registry of twenty capabilities with authority-conferring flags, seven built-in roles (community-admin, moderator, vetting-lead, repo-manager, credential-officer, auditor, and the least-privilege approver), explicit act scope, resource qualifiers (a git namespace, a policy purpose, a join criterion), acl/*/0.2 beside 0.1, and an audited in-place migration of every pre-role ACL row at boot; custom roles, approver sets read off approve authority alone, a departed granter’s grants reviewed as an action, and acl/swap-key rolling an entry to a new key with exactly its authority (#1927; cnm now holds one identity per community and rotates it this way, #1923); single-administrator mode, set at install, for the community with nobody to consent — the requester’s bound step-up stands in, every waiver audited Critical with a permanent console banner (#1925, #1941). The console became live: one hint-only event stream per session over the ordinary /v1/trust-tasks door as an HTTPS-binding 0.3 streamed response — never a record, an id or a DID on the wire (#1932); break-glass ratification, join review and vetting review wait in the same Actions list (#1931). An administrator may withdraw a member’s registry consent, never grant it (#1962), and the registry syncer publishes only members who consented (#1691).
Persona — faces composed where they are asked for — 2026-09-21 → 09-23
persona-context-first.md (#1598) reviewed the RC-0 persona store against the UX discussion in dtgwg-htx-tf#11 and reached a verdict in a table: keep the pool and the one-way boundary, invert the authoring model — do not start from the identity screen, start from the context that is asking. The primitives followed in nine breaking feat(persona)! commits: entry slots so a face can say which of its names is its name — the Donald Duck / Mickey Mouse question (#1605); a pinned value kept through an edit, with purge (#1606); compose a face in the context that asks for it, local by default, and promote a local value into the pool, one-way (#1623); retire and reinstate a face, a binding that ends on its own (until), and disclosedTo counts on delete — “deleting does not un-tell them” (#1628); where a face may be worn, where it is, what it has done (#1635); derived provenance and endorsements as inventory (#1639); a credential-backed value follows its credential and fails closed (#1653); wear a face here without naming a persona DID (#1654); and worlds — facet renamed to what every UI already said on screen, plus a narrow attribute/get so revealing one email no longer decrypts every email (#1690). pnm persona reaches worlds and the claim-type registry (#1663). The correlation answer on a local write is the holder’s (#1676), and the holder grant hint names persona-holder (#1885). See correlation-scope.
TSP by default, cross-mediator routing, and a push engine that re-issues — 2026-09-21 → 10-06
TSP is on by default (#1622, breaking — Keyring VTI-33): tsp joined vta-service’s and vta-enclave’s default features, the setup wizard pre-ticks it and checks the mediator actually carries TSPTransport before anything is minted; a persona minted by a TSP-capable VTA now advertises TSP when its mediator can route it (#1665). The VTC’s pushes to members flipped to TSP > DIDComm > REST with escalation on missing delivery evidence — the Phase B flip tsp-enablement.md §14 had deferred (#1754, VTI-TRN-042) — and learn that a service-less did:key wallet is on TSP from its own inbound frames (#1765); the engine is shared in vti-common and the VTA’s device pushes ride it too (#1760, #1767). Because both consumers refuse a document older than the acceptance window, a push that outlives it is re-issued as a new attempt — fresh id, fresh issuedAt, re-signed, same thread — rather than re-sent (#1799, SPEC §8.4). Relationships: a stale TSP half is re-invited alongside the request after an hour idle, after the browser wallet timed out a DID creation at 30 s while the recovered answer arrived at 30.07 s (#1816, D10); vta tsp-relationships {list,reset,delete} works offline (#1812); a relationship can be started with a peer on another mediator and the SDK routes the invite itself (#1873, #1880, VTI-56); a TSP reply reaches a sender on another mediator by nesting through the sender’s mediator (#1894); a rate-limited reply is retried with backoff (#1906); inbound messages are handled concurrently so a bind’s bridge reply is read (#1973). Finally the inbox watch (#1978, #1979, breaking): a node that stops collecting is noticed from both ends, a receive leg that stops delivering is reconnected under a governor, and deleting frames a node cannot unpack belongs to the SDK (affinidi-messaging-sdk 0.33.2 / delivery 0.1.20). Design notes tsp-relationship-recovery.md D1–D10 and tsp-enablement.md updated.
webvh, contexts, backup, storage, CLI — September–October 2026
- did:webvh:
versionTimeupdates serialized and never backdated (#1599, breaking);vta/webvh/dids/create/1.1statesserverlessso a client is told, not left to infer, that a DID exists only until its first log entry is served (#1886, VTI-20); a self-hosted community installs its own DID log overdid-management/did/register(#1632); a context’s DID can be cleared (contexts/update-did/1.1, #1828, #1830). - Contexts: a context delete previews the whole subtree it destroys and deletes hosted did:webvh DIDs off their servers, reporting what it could not remove (#1576, #1577, breaking); a holder grant survives an admin rollover (#1573);
pnm/cnmcontext ids are paths —--parentis removed (#1848, breaking:--id acme/eng). - Backup / TEE: whole-agent v2 (above); an abandoned import releases boot after its TTL (#1870); mnemonic export on a DID-less TEE VTA points at the real remedy, and
pnm vta mnemonic opendecrypts a sealed bundle locally (#1878); opening a bundle to inspect it keeps the request seed (#1868). - Storage / ops: optional fjall memory settings so a pod stays inside its Kubernetes limit (#1803); a VTA as a macOS LaunchAgent (
docs/02-vta/macos-launchd.md, #1704);pnm vta qrand the VTC landing page show a DID as a QR code for a phone to scan (#1700, #1698);pnm messaging consoleopens the mediator console as a VTA-managed DID andpnm messaging grantgives a DID an account role there (#1626, #1686); an active console admin stays signed in and the idle timeout is settable (#1586, breaking); a vetted invitation is delivered to the DID it admits, pushed as an OID4VCI offer or as a QR (#1648). - Keyring review. Several titles cite “Keyring VTI-nn”: an external review of the stack whose findings were worked through in the window (#1800 VTI-09/23/27, #1868 VTI-44/45/50/56, #1886 VTI-20, #1687 VTI-42, #1622 VTI-33, #1907 VTI-13).
Dependency and release-engineering moves — September–October 2026
trust-tasks-rs 0.21 → 0.27.7 in six minors, each pulling a batch of newly published specs (worlds; attestation and public tasks; the mediator’s Trust-Tasks-only cut and discovery/0.3; DTG v1 and create/1.1; the DI 0.8 cascade and signing-key/enroll/0.2; the action list, PR gate, hidden vetting and sign-sshsig); affinidi-tdk 0.16 → 0.23 and affinidi-messaging-sdk → 0.33.2 in lockstep, so the graph holds one trust-tasks-rs again (#1690, #1922); affinidi-data-integrity 0.8 / affinidi-bbs 0.4 (after an =0.7.13 pin to stop a patch release splitting the BBS types, #1882); dtg-credentials 0.13; didwebvh-rs 0.8; affinidi-did-resolver-cache-sdk 0.8.39 (deactivated DIDs, #1881); predicate-credential-system 0.1 and the arkworks stack; pgp 0.20; rand 0.10; MSRV stays 1.95 while CI builds clean on rustc 1.99 (#1877). Release engineering: the release guard (#1807, #1852, #1892) after #1795 halted partway; tags cut with RELEASE_PLZ_TOKEN because the Actions token gets 403 on refs (#1863); the publishing pass retried on a fresh crates.io token (#1857); the Release PR job fitting eleven parallel semver checks, then capped at two (#1938, #1939); CI in tiers with Feature combos split five ways (#1819), one test binary per service and no fail-fast (#1862), dependencies built optimized in dev and tests under nextest — vtc-service’s suite 1098 s → 893 s (#1926), test stores skipping crash-durability fsyncs — 16.3 s → 3.6 s on a Mac (#1928).
The August–September cycle (≈510 commits, PRs #999 → #1561 since the Cypress tag on 2026-08-18; 482 of them by one maintainer, 20 by Dependabot) is the fourth consecutive record month, and it produced two milestone tags rather than one. VTI-Dogwood (RC-1 2026-08-29 at #1197; the tag on 2026-08-30 at #1213; a VTI-Dogwood-R1 re-cut on 2026-09-01 at #1218) was a deliberately quiet release — tag-only, no GitHub Release, nothing headline-shaped for a user — because the month it closes was spent making the wire honest: every served Trust Task now matches its published schema on both sides, every producer signs, retries are idempotent, replay and freshness are enforced the way the framework says, and errors no longer leak. VTI-Eucalyptus-RC-0 (2026-09-17 at #1553) is the opposite kind of window: two and a half weeks in which the workspace gained five crates, data rooms, a persona store, peer vetting, a hash-chained VTA audit log, post-quantum (ML-DSA) keys and hybrid credentials, and a TSP Rev 3 stack with persisted relationships and multi-hop metadata privacy. Dependencies moved just as hard — trust-tasks-rs 0.9 → 0.21, affinidi-tdk 0.8 → 0.16, dtg-credentials 0.2 → 0.9 — and the wire types they carry are now generated from the published specs rather than hand-written. The direction of travel: the VTA is becoming the holder’s whole agent (identity, memory, application state, rooms), and the workspace now declares itself an implementation of the normative ToIP VTI specification.
Dogwood — 2026-08-30 — the silent release: conformance, idempotency, honesty on the wire
The VTI-Dogwood tag sits on #1213 (a VTC feature commit, not a release merge — the versions were cut at RC-1’s #1197 and rolled forward by four chore: release merges in between). Snapshot versions: vta-service 0.23.3, vta-sdk 0.32.2, vti-common 0.16.0, vti-secrets 0.3.1, vtc-client 0.5.1, pnm 0.14.2, cnm 0.13.2, vta-keys 0.4.1, vta-vault 0.5.1; deps trust-tasks-rs 0.17, affinidi-tdk 0.10, dtg-credentials 0.4, jsonwebtoken 11, aes-gcm 0.11. The tag carries only the description “VTI Dogwood”; there is no GitHub Release and the word “Dogwood” appears nowhere in the repo’s docs — it exists for the other repos’ pins (coordinated-releases). What the twelve days between Cypress and Dogwood actually bought:
- The conformance sweep — drift reaches zero (#1076 → #1115, 08-24..08-26). A new test layer observes the real responses each handler emits and validates them against the published Trust-Task schema, rather than trusting fixtures (“a false green, and the fixture that caused it”, #1100). The VTC started at 33 drift entries and the sweep took it to zero in two days (#1093–#1112: camelCase envelopes, nested response wrappers, the endorsement model the spec defines,
issuedis a credential receipt not a timestamp;recognise/submitmoved to 0.2 — “drift reaches zero”, #1105). The VTA side found three of four response defects the same way (#1113–#1116), gated response conformance in CI and measured task coverage: 102 of 109 dispatched specs exercised end-to-end, with the last seven named and explained (#1151). A “produced-URI census” now checks the URIs the services send, not only those they serve (#1181). Design notes:trust-task-envelope-conformance.md. - Framework 0.5.0 enforced at the dispatch spine. The VTA had enforced none of the four checks a Trust-Task spec declares for itself; #1146 (breaking) enforces all of them —
recipienton all 109 dispatched specs,proofon 72, audience binding,issuedAton 70 — keyed by Type URI throughspec_policy_for, which was authored upstream for this. Freshness bounds (#1117), the framework’sReplayGuard+FreshnessPolicyreplacing the hand-rolled pair — the acceptance window and the replay record’s retention are the same bound, and the service had a ten-minute record against an unbounded window “believing it had a replay defence” (#1126, #1127) — and error messages stop being a probing oracle: internal causes no longer leak intomessage,detailsand audit reasons are bounded (#1130–#1132). Authrevoke-sessionno longer tells a stranger the session exists (#1141). - Retry and idempotency — who owns what (#1009 → #1014,
retry-and-idempotency.md). The dangerous case is a processed request whose reply was lost: a naive retry ofwebvh/dids/createmints a second DID and orphans the first. The VTA dedups keyed Trust Tasks on anidempotencyKey(#1011, breaking) and the SDK holds one key across every attempt of an operation viaVtaClient::idempotent(#1012); every task is classified by what a lost reply costs (#1010) and the contract is proven by counting effects, not statuses (#1013). - Every client has an identity, and every producer signs. #1146 made the VTA refuse unsigned tasks and left seven production client paths that could not sign; #1147 (breaking) replaced the three-step
new+with_identity+set_tokenwithVtaClient::authenticated(url, identity, token)and adoptedprovision/integration0.3. Then the restriction that only adid:keycould sign a Trust Task — never a policy, just the shape of two helpers, and the reason a provisioned did:webvh integration could dispatch none of the 210 proof-requiring tasks — was lifted: any DID that names a key may sign (#1193, breaking). The consent approve-request pushed to an approver’s phone is signed too (#1180). - Retire on evidence, not by hand.
vta/discovery/capabilitieswas retired in favor of the canonicaltrust-task-discovery/0.1family (#1042, #1044); thevta/servicestask family absorbed twenty/services/*REST routes with a drain guard so a mediator teardown cannot discard in-flight messages (#1017); and the superseded-route table gained a guard so a deleted route cannot sit at “zero usage” forever (#1047). A third store — versioned, namespaced application state — arrived asvta/app-state/*beside the secrets and credential vaults (#1041, #1051). The audit destination became a deployment choice (#1049): a seam for a stronger sink, which September then filled (below). A design note for Trust-Task version negotiation (#1046) records that negotiation is tractable only if versions carry a compatibility contract — a proposal for the shared registry, not yet implemented. - VTC — DTG conformance. Trust Task Context Binding is enforced on presented credentials (#1178, breaking —
taskContexthad zero occurrences, and its absence permits the spec’s “context collapse”); the digest binding on presented witness credentials is verified (#1161); personhood runs over messaging with a challenge bound to something signed (#1086), a community can vet a member in person and have it count as personhood evidence (#1085), the two DTG personhood-conformance gaps closed (#1089); the relationships graph distinguishes half-edges from complete edges (#1073), a member can publish a VRC without naming their membership DID in it — R-DIDs as the spec recommends (#1061, breaking); the VPC lands as the deliberate-correlation mechanism on an edge (#1074); publishing is authenticated by the document’s own proof (#1088); credentials past their validity window are rejected at ingress (#1075); and — the commit the tag sits on — the membership edge is completed and drawn on the trust graph (#1213): a VMC pair is an edge like a VRC pair, and the VTC now keeps the credentials it issues so it can prove the digest binding. The console’s wire types are generated from the daemon (#1188). - Operational fixes worth knowing. DID deletion cascades, refuses or revokes across ACL entries, issued credentials, sessions and per-DID state, with a dry-run that shows what would be destroyed (#1198/#1199,
did-deletion-cascade.md); a VTC ACL revoke can no longer orphan a member row (#1194/#1196 — found in production);vta-mcpcompiles a session store in so session mode works at all (#1091) and gains a local operation guard + per-call log (#1101); TEE bootstrap 410 / vsock ENOTCONN (#1003);CIcaches keyed onCargo.lockwere restoring staletarget/for path-dependency changes across seven jobs (#1134). - Dependencies (breaking, mostly). trust-tasks-rs 0.9 → 0.11 (#1015) → 0.12 (#1121) → 0.17 (#1144, #1176, #1182) — each step pulling registry-moved error codes,
keyIdfrom the wire,exton every payload (#1231);credentials/issue0.2, vault 0.3,device/wipe0.2 (#1145, #1159); affinidi-tdk 0.8 → 0.10; dtg-credentials 0.2 → 0.3 (#1207) → 0.4; aes-gcm 0.11 (nonce conversions no longer panic, #1173), jsonwebtoken 11, tower-http 0.7, azure_identity 1.0, k8s-openapi 0.28; the legacy rustls tree dropped (#1158); “bring every dependency to latest” (#1055); Dependabot configured (#1160).AppState,AppStatePartsand the growth-prone wire bodies became#[non_exhaustive]so adding a field stops being a break (#1024, #1057).
Dogwood-R1 — 2026-09-01 — the re-cut
VTI-Dogwood-R1 is Dogwood plus one chore: release (#1218) carrying five fixes and five dependency moves — 51 files. The fixes are all “the VTA said the wrong thing”: device list / disable / wipe are scoped to the caller’s contexts (#1217); not-found, conflict and gone stay typed across the Trust-Task boundary instead of collapsing to an internal error (#1219); provisioning says which side is out of date and checks authorization before minting (#1220); approver sets resolve from one row-first source rather than three (#1221); the VTC tells a member which side is out of date instead of “unknown verb” (#1223). The dependency moves: uniffi 0.28 → 0.32 (mobile bindings), affinidi-tdk 0.11 with affinidi-mdoc 0.3 / coset 0.4 moved together (#1230), regorus 0.11, base64 0.23. Not the reason for R1: the “VTC setup began failing after Dogwood” regression fixed by #1560 on 2026-09-18 — that defect was introduced by #1483 on 09-15 (the unified webvh transport selector reading a compile-time TSP constant while the DIDComm handler holds no TSP socket) and belongs to the Eucalyptus window, after both Dogwood tags.
Trust Tasks over TSP Rev 3 — relationships, one outbound path, multi-hop privacy — 2026-09-15 → 09-18
The largest post-Dogwood stream, and the one that reverses a design doubt. tsp-vs-didcomm-trust-task-surface.md was written to ask whether the §7.2.2 relationship layer was worth its cost; the answer, recorded as decided, is that multi-hop TSP — routing where intermediaries never learn the final recipient — is a requirement, so the recovery work is “justified investment, not speculative rent”. What landed: Rev 3 adopted (#1512, breaking — a flag day, since nothing a Rev 2 peer packs can be unpacked by a Rev 3 one; inventory in tsp-rev3-migration.md); Trust Tasks carried in the TSP binding envelope (#1478, #1488); one outbound path chosen from what a peer advertises, replacing four hand-rolled carriages (room-host REST, webvh DIDComm, eight webvh verbs, device push — #1474), with the webvh selector folded onto it so a TSP did-host is reached over TSP (#1483) and a did-host advertising where it accepts Trust Tasks (#1476); replies correlated in the spine so TSP can initiate (#1482); TSP relationships persisted across restarts (#1531), formed before a send or a health ping (#1527, #1537, #1540), answered instead of ignored (#1525), re-formed on a reply timeout on the client (D4, #1544), the VTC registry client (#1546) and the VTA’s server-initiated sends via a single-flight RecoveryCoordinator (D6, #1549), with boot enumeration + idle eviction (#1534) and relationship-gate drops surfaced as telemetry (D8, #1536); and — after the RC — cross-mediator sends nested for metadata privacy (#1559): our mediator never learns the recipient. One TSP transport, and it must carry a mediator (#1507). The tsp-relationship-recovery.md note tracks D1–D9; D7/D9 hardening remain open. #1560 (breaking) then fixed the transport seam to select by the sender’s live capability rather than the build feature — the post-Dogwood VTC-setup regression.
Post-quantum: ML-DSA keys, hybrid credentials, did-templates 3.0 — 2026-09-16 → 09-18
KeyType gains MlDsa44 and MlDsa65 (#1502, breaking via #[non_exhaustive]) — two parameter sets because two specs require different ones: W3C Quantum-Resistant Cryptosuites define Data Integrity suites for ML-DSA-44, TSP Rev 3 §8.1 mandates ML-DSA-65. Keys derive from the BIP-32 chain (#1505); a derived key carries the algorithm it was minted with rather than a literal at every save site (#1532, breaking); an operator can create one and see which axis it protects (#1535). did-templates gain a keys block declaring which algorithms a template’s slots use (#1530, breaking), a third slot beyond the classical pair (#1554 — the previous rejection’s own advice would have published a PLACEHOLDER-NEVER-SUBSTITUTED literal into a write-once did:webvh log), and the VTA accepts did-templates 3.0 alongside 2.0 (#1538) with clients carrying a post-quantum template (#1542). On the VTC: verification reads a document carrying more than one proof (#1548, #1550), and the RC commit itself (#1553) has the VTC sign with every key it holds — one proof per key, each under its own cryptosuite, byte-identical output for a single-key VTC so nothing downstream changes until a second key exists. Its most consequential finding: affinidi-data-integrity was pinned without its ml-dsa feature, so every capability built above it led to a key that could not sign — “built, tested, and unreachable”. Post-RC: a sealed-transfer template-bootstrap variant carrying a second signing key (#1556) and a VTC provisioned from the v2 template issuing hybrid credentials (#1557).
Security sprint — audit chain, domain-separated oracle, exportability, endpoint vetting, CI supply chain — 2026-09-10 → 09-12
Nine security(...) commits and the audit work, most closing requirements of the VTI specification’s divergence register. The VTA’s audit log is now a hash chain (vta-audit-chaining.md, #1408; #1412 → #1421): its own audit key from the OS random source, deliberately not seed-derived so a mnemonic holder cannot recompute it (#1419); each entry commits to its predecessor, with actors and DID-shaped targets under a keyed hash beside the plaintext so an erasure can null the plaintext while the chain still verifies (#1420); a verify pass that separates the counts a bare pass/fail hides (#1421); pre-chain keyspaces served (#1413); and failed audit writes reported rather than let _ =-swallowed at 27 call sites, plus the signing oracle finally leaving a keys.sign row (#1443, breaking — sign_payload takes a required audit sink). The opaque signing oracle is domain-separated (#1417, breaking): unparseable payloads are framed under a domain tag so a signature obtained for one purpose cannot verify as an assertion, token or proof in another protocol. A key can be marked never leaving the VTA (exportable, #1401, #1407), and keys/export-secret replaces the misnamed, unspecced, global-Admin-gated seeds/export-mnemonic (#1404). The SDK vets DID-advertised VTA endpoints (private, loopback, link-local, CGNAT … refused) and binds cached tokens to their origin (#1436, breaking); the resolver refuses did:webvh resolution to non-public hosts by default (#1448, breaking); cnm checks who sealed an admin credential before installing it (#1437); backup exports are written 0600 with create_new (#1438); the enclave proxy resolves nitro-cli by absolute path with a scrubbed environment (#1441); /auth/challenge no longer discloses enrolment (#1405); a plugin icon renders as an image, not markup (#1442). CI: every action pinned to a full commit SHA — 58 refs, none pinned, and the log showed a @stable ref drifting within nine days on a job holding id-token: write (#1440); workflow inputs validated through env (#1434); rustls 0.23.45 for RUSTSEC-2026-0285 (#1468).
Authorization surface — ACL capabilities, persona-holder, memory grants, rate limits — September 2026
AclEntry::capabilities was stored, echoed and enforced nowhere; now an entry narrows within its role and can never widen beyond it, operators can set it, and entries are created already narrowed (#1279, #1280, breaking). A persona-holder capability grants authority over the holder’s own identity without granting every context — the case OpenVTC’s context-scoped admin found (#1286) — and whoami reports the capabilities it enforces (#1298). Agent memory gets MemoryRead/MemoryWrite so read-only grants exist server-side rather than as an MCP-side glob (#1234), with pnm memory (#1222). Provisioning can ask for an unrestricted admin and is told what it got (#1303); pnm clients self-provision their mediator ACL (#1292); a step-up can record a bound approval without elevating the session (#1316). Rate limiting (rate-limiting.md): did.jsonl gets its own limiter so a self-hosted DID’s log fetch cannot starve auth, and every VTA/VTC 429 says which limiter refused (#1510, #1514, breaking); quotas are tunable at runtime (#1519); refusals are typed in the SDK and CLI (#1511, #1521). Seed reads are cached so KMS traffic scales with time, not requests — prompted by a 58.1M-request Secrets Manager line item on a VTA doing ~22 req/s (#1290, breaking).
Signed responses, verified replies, and the mock that had to learn to sign — 2026-09-09
265 specs require a signed success response and none got one: both services now sign success responses (#1334, #1335), and — the surface that matters — VtaClient verifies every reply it receives (#1341), because “a reply is bytes off a socket” and without the proof an intermediary can rewrite an ACL listing or flip a policy decision. MockVta had to learn to sign (#1350, #1355), the Trust-Task proof verifier moved down from vti-common into the SDK (#1340) and resolves did:peer with no I/O (#1364), and a Trust-Task proof can be verified against the typed document (#1500). The mobile core verifies the consent prompt, not only the step-up one (#1324 — a document from anyone rendered identically to a real one).
New crates: data rooms, persona, vetting — September 2026
Covered in depth on their own pages as they land; the shape here. Data rooms (docs/02-vta/data-rooms.md, docs/05-design-notes/data-rooms*.md, two security reviews) — a shared record space “readable and writable by exactly the parties its credentials admit, for the humans in it and for their AI agents”: a room has its own DID, issues its own credentials, keeps its group key under MLS, and can move between hosts without reissuing a credential. #1237 landed storage, dispatch, verification, MLS and a host in one day (09-03); the following week added lifecycle where “the host never decides” (#1242), audit without learning who (#1244), group custody and succession (#1248, #1251), the presentation oracle so an agent never holds its human’s credentials (#1247), read mirrors (#1289), a Merkle record commitment and tree heads (#1346, #1375), epoch anchoring where a host cannot reach it (#1391), a browser member via vti-rooms-wasm (#1347, #1382), and a VTC console for the rooms a community hosts (#1325, #1330). vta-persona (#1255 → #1342) — the holder’s own attributes, the profiles that project over them, bindings to persona DIDs, disclosure history — a one-way boundary a context cannot read across, release: stepUp enforced on disclosure (#1304), a deployment-declared claim-type registry (#1315, #1327), and persona/facet (#1338); it moved to trust-tasks-rs 0.18 (#1266). Peer identity vetting (docs/03-vtc/vetting.md, #1425 → #1432, #1439): an applicant is vetted by existing members who sign Vetting Statements the community counts in the join decision — a vetter role credential, a vetter registry with automatic grants, community branding in the manifest, cnm admin commands and a PGP web-of-trust vetter bootstrap; the wire types are generated from the published specs. Also new: docs/02-vta/vta-mcp.md and an agent-memory-with-MCP example.
VTC, webvh, backup, TEE, mobile — September 2026
- VTC: a failed registry-sync job is visible and recoverable (#1487, #1493) and the registry mirror can be compared against the registry (#1489); the recognition graph is browsable from either side (#1495); Trust Tasks accepted in the DIDComm binding envelope with the proof verified in the spine “where the bytes still exist” (#1497, #1501); rooms tasks routed by payload type (#1503); the admin UI authors vetter-eligibility policy, admission criteria and statement types, charts what a policy decides by evaluating it, and lets the join dry-run reach the vetting routes (#1465–#1473);
build.rsstops writing to the source tree (#1254).vtc-client: any DID method can hold, and a session carries it (#1399). - did:webvh: key records follow the document’s verification-method ids, with a repair (#1466); realign-keys as a Trust Task (#1470);
versionTimeclamped against the previous entry (#1456); refuse to delete a DID whose host is unregistered (#1518);addTspServicehonoured on a template-rendered DID (#1424); each DID resolved once per process through a shared resolver (#1517); pre-rotation can be turned on for a DID that never had it (#1203). - Backup: a DIDComm/TSP-only VTA can be backed up via the
chunkedTrustTaskalgorithm —backup/get-chunk,put-chunk,initiate-*1.1 (#1522, breaking; trust-tasks-rs 0.21.1 was cut for it), with typed refusals and the password validated at import too (#1516, #1484). - TEE / Nitro:
allow_kms_reinitis explicit authorization regardless of KMS failure class, and an authorized reinit clears the integrity manifest so the result actually boots (#1249); pnm anchors a TEE bootstrap connect by DID and pinned PCR0 with mutually exclusive--vta-did/--vta-url(#1454); the Nitro image copies every workspace member (#1361); aws-smithy-types bounded (#1485). - Mobile: uniffi 0.32, verified consent prompts (#1324), Rev 3 TSP sealing (#1488) —
vta-mobile-core’s manifest stays at 0.6.18 because it is unpublished. - CLI: removal commands are named
deleteand say what a delete leaves behind (#1513);vta removeconfirms unless--force(#1457); persona commands read in the words a person uses (#1291).
Dependency and release-engineering moves — September 2026
trust-tasks-rs 0.17 → 0.18 (#1266) → 0.20.x (exportability, export-secret) → 0.21.3 (chunked backup, did-templates 3.0); affinidi-tdk 0.11 → 0.16 (carrying affinidi-tsp 0.2 / Rev 3); dtg-credentials 0.4 → 0.6 → 0.9.1 (#1356 — a presentation is bound to its presenter); didwebvh-rs 0.7; regorus 0.12 (#1295); getrandom 0.4, ulid 3, chacha20poly1305 0.11, jsonschema 0.55, dirs 7; rand held at 0.8 while rPGP needs it (#1450). Release engineering: the semver report became a gate (#1256) and grew memory, time and a fallback for unbuildable baselines (#1491, #1492, #1496, #1520); a release baseline was moved off an unresolvable commit and 18 crates unstuck (#1406, #1414); the newest push wins Release-PR regeneration (#1453); Cargo.lock must satisfy the manifests (#1302) and members must agree on sibling versions (#1409); Test split into workspace/VTC halves gated on what changed (#1265, #1267); every crate has a README because a missing one blocked a publish (#1272); every documented build passes --locked (#1481); root design notes relocated into docs/ (#1541); RELEASING.md says what actually counts as a breaking change (#1264).
The July–August cycle (≈340 commits, PRs #625 → #1008 since 2026-07-06) is the VTI’s third consecutive record month, and it ends in the coordinated Cypress release (coordinated-releases) — the first tree-named milestone to go through formal release candidates (VTI-Cypress-RC-0 08-02, VTI-Cypress-RC-1 08-11) and the first cut as a crates.io-published snapshot under the new release-plz process. Thematically the month was about converging: every wire operation folded onto the canonical ToIP Trust Tasks registry URIs; the three approval mechanisms collapsed into one approvals model; the vta-service monolith was decomposed into eleven subsystem crates; and TSP went from opt-in feature to a selectable transport a VTA can run without DIDComm at all. New capabilities arrived too — ISO mdoc receive/present, non-extractable internal keys, a hardened non-TEE mode, Nitro tenant config over vsock — but the dominant signal is a codebase preparing for a 1.0 shape.
Cypress — 2026-08-17 — coordinated release, release-plz, RC process
The annotated Cypress tag points at a release-plz chore: release merge (#997). Snapshot versions are in the Components table above (vta-service 0.17.0, vta-sdk 0.25.0, vtc-service 0.11.58, vti-common 0.12.1, pnm 0.12.6, cnm 0.11.22, vta-mobile-core 0.6.18). Versions at the start of the window for comparison: vta-sdk 0.18.17, vta-service 0.10.23, vtc-service 0.10.13. Two RCs preceded it: RC-0 at #893 (keys import canonical on every transport, 08-02) and RC-1 at #935 (changelog sign-posting, 08-11) — Banyan → Cypress is simply the next letter in the tree-named sequence, but the RC discipline is new.
release-plz (#938, 08-12). Replaces the hand-rolled publish.yml / cut-release.sh / version-bump guards and the changelog.d/ fragments introduced only two weeks earlier (#878): one open Release PR, per-crate tags, git-cliff CHANGELOGs, cargo-semver-checks-derived bumps, trusted publishing (the workflow had to keep the name publish.yml because crates.io pins the filename, #943). #938 first cut the published set 21 → 7; #962 (08-13) reversed most of that because openvtc-core dev-depends on vta-service for MockVta and vti-common re-exports vta_sdk::acl types — a frozen vta-service stopped compiling against a moving vti-common. The rule at the time (RELEASING.md): 20 of 26 publish — 23 of 31 as of September. Fourteen chore: release merges landed between #938 and Cypress. Also: a plain non-TEE Dockerfile + CI (#955), the Nitro image cached with cargo-chef and built in CI (#956), CI diet (#961), a DCO exemption for verified org-member commits so release merges can pass (#944), bounded CI jobs (#1002, #1006).
Trust-Task canonicalization — everything folds onto trusttasks.org/spec/* — July–August 2026
The largest single stream. The VTC program (#710; docs/05-design-notes/vtc-trust-task-registry-migration.md, COMPLETE 2026-07-27): manifest census (#711), config/audit/acl/policy repointed to canonical (#724–#729), join-requests ceremony → spec/vtc (#733, #743, #806), admin passkeys → canonical auth/passkey (#809), admin-login and the legacy /v1/config surface retired (#828), config export/import repointed — retiring the last openvtc/vtc bindings (#834); all 66 entries in the VTC’s trust-tasks/index.json are now retired with supersededBy; decide replaces approve/reject and accept folds into members/vmc (#863). The VTA side: ACL → canonical acl/* (#842) with acl/change-role split out (#855); config + provisioning → canonical, and the VTA stops rewriting its own DID (#841); audit → audit/list/0.1 (#848); webvh dids/get-log → dids/get, servers/{add,update} → servers/register (#849/#850); credential-exchange bound to published URIs (#837); keys → canonical keys/* (#888, #893 = RC-0); did-templates onto the six-task 2.0 family (#864); sign() and every twinned RPC method reach TSP via the Trust-Task bridge (#861); a conformance sweep so every published task matches its schema (#866); a reverse registry-parity harness (#860). Wire level: #1000 (breaking) emits canonical lowerCamelCase on 53 Trust-Task payload structs, accepting snake_case via 126 aliases; #1001 carries Trust Tasks over the HTTPS binding on REST too; #1007 marks every superseded REST route (60 route → task pairs) with a successor Link and a usage metric, gating deletion on zero usage. The plan for the last ~67 unpublished task URIs — fold, generalise, author only what is genuinely new — is docs/05-design-notes/canonical-task-reduction.md (#840). Direction: REST retirement is now measured, not scheduled.
TSP: selectable, and optionally DIDComm-free — July–August 2026
TSP moved from “feature-gated and opt-in” to a selectable transport — TransportChoice::{Tsp, Didcomm} plus Auto, with the documented TSP > DIDComm > REST preference now implemented (#797, 07-25), and a TSP leg per surface rather than a whole-client switch (#810). A VTA can speak TSP without DIDComm (#937): the connect supervisor runs on didcomm || tsp, the cargo features are decoupled, CI builds the tsp-only combination. The setup wizard offers TSP and advertises it at mint (#933/#934, transport-neutral-mediator.md); a minted did:webvh can advertise TSP at the VTA’s mediator (#959). On the VTC side: Trust Tasks accepted over TSP (#833), self-remove and member-VMC dispatched as Trust Tasks (#838), communities choose their transports at setup and publish which they offer and whether they answer (#926/#929, #923 “serve the TSP we advertise, and refuse to pretend otherwise”), the admin UI shows which transport each service is actually reached on (#965), and the trust registry is reached by DID over TSP or DIDComm (#963; probe order fixed #981, retriable at boot #966). webvh DID mutations and the domain relay reach TSP (#885, #891); mobile gained a TSP receive session and device push over TSP (#694–#697). What’s still open (per #937’s “what this does not do”): a fully transport-neutral mediator config, per-protocol mediator registration, and whether TSP-only sessions still need the DIDComm auth leg.
One approvals model — policy gate, consent, break-glass — July–August 2026
Three mechanisms — [auth.step_up] floors, config consent rules, and the Rego Policy Decision Point — collapsed into one. The PDP on regorus landed with opt-in enforcement (#637, 07-12) and became the single step-up authority (#641); task-execution consent (DTTE, #645–#653) pushes a human-readable, site-named, dry-run-effects consent request to approver devices; approvers are least-privilege (“may approve” ≠ “may act”, #684, #779) and need not hold VTA authority (#907). Then the convergence: one approval model manageable at runtime (pnm approvals, #909), the gate enforced on REST routes (#912) and the webvh update route (#913), step-up floors and config consent rules retired — rules are the only trigger (#914, breaking), and an offline break-glass vta approvals/policy {list,remove,disable} for a rules lockout (#915). Canonical request bodies with witnesses built from them (#925, #928); the step-up approve-request itself is now signed (#870) and minted as 0.2 (#873). Docs: docs/02-vta/approvals.md, task-consent.md, approvals-convergence.md (status: landed). Related: DID-keyed sessions unify REST with intrinsic-sender transports (#627–#629).
Signing oracle: authorization model, allowedKeys, non-extractable keys — July–August 2026
#814 turns the signing oracle’s authorization model into a documented guarantee: three gates in operations::keys::sign_payload (caller context scope; resource-bound signable_keys policy binding even super-admin; unscoped keys super-admin-only), one enforcement point for REST/DIDComm/Trust Task. allowedKeys (#865) adds actor-scoped per-key narrowing on ACL entries. A design review of multi-tenant signing (#817) concludes no authorization refinement can distinguish a compromised multi-domain signer — only splitting credentials or a second factor can. Non-extractable internal signing keys (#995, vta-keys 0.2.5): CSPRNG keys with no derivation path, in their own INTERNAL_KEYS keyspace excluded from backup, export refused in code (admin is not a bypass), forbidden as webvh update keys; pnm keys create --internal. Also: import an external Ed25519 key for a deterministic did:key (#953); backup password minimum raised to 15 characters (#875).
ISO mdoc — receive, store, present over OID4VP — 2026-08-16 (#984–#993)
A single-day burst gave the vault a first-class CredentialFormat identity for ISO 18013-5 mdoc (#984); verify-and-store on receive (#986); issuer resolution against configured IACA trust anchors ([vault] mdoc_iaca_trust_anchors, #987); acceptance over vault/credentials/receive as credentialBase64 (#989); an mdoc bound to the VTA key that can present it (#990); and presentation over OID4VP with an ISO 18013-7 SessionTranscript, a P-256 ecdsa-jcs-2019 consent receipt, and HolderIdentity::{Subject, DeviceKey} (#993, breaking). Depends on affinidi-mdoc 0.2.7 and the TDK’s new ecdsa-jcs-2019 cryptosuite. The VTA is now a holder for W3C DI, BBS, SD-JWT and mdoc — the four credential formats that matter for government / travel / age use cases.
vta-service decomposition — 2026-07-24/25 (#780–#791)
Eleven subsystem crates extracted from the monolith in two days (vta-config, vta-keyspaces, vta-audit, vta-keys, vta-vault, vta-webvh, vta-policy, vta-support, vta-tee, vta-backup, vta-sweepers; backup extracted with dependency inversion #790; TEE and policy #791) — 27k lines moved, AWS SDK out of the default build graph with vta-tee, semver checks switched off for the subsystem crates because nobody consumes their APIs directly. The decomposition note sets a stopping rule: extract only if it improves compile granularity, testability, or dependency surface.
Hardened non-TEE + TEE fleet config — July–August 2026
[hardened] at-rest encryption for non-TEE deployments with first-boot auto-migration of existing rows (#835) — closing the gap between “runs in Nitro” and “runs on a VM”. In the enclave, tenant config is no longer baked into the EIF: one image / one PCR0 per fleet, a config envelope delivered over vsock:5800, a TEE-mode floor, the config digest anchored in the NSM attestation, and POST /attestation/config-report (#939). The TEE security-model doc was re-grounded against current code (KMS Recipient attestation, seven vsock channels, #832); a dual-unlock design note (#736); NSM ioctl len must be u64 (#819); per-crate COPY layering for the Nitro image (#826).
Mobile, messaging, webvh, VTC — July–August 2026
- Mobile: device Trust-Task submission with no REST over DIDComm and TSP (#792); mobile as a second device for task-consent (#690); request proofs verified on-device before prompting (#871, mobile-core 0.6.17); DID→name display seam (#800); 0.6.18 for the
digestMultibaseconsent digest (#911). - Messaging: both services cut over to the TDK’s reliable
affinidi-messaging-deliverylayer (MessagingService+DidCommTransport+VtiOutboxStore; #675–#691), Guaranteed-send state visible (#899), one ATM per process with a shutdown that actually closes the socket (#844), websocket leak fixes (#846/#847), mediator ACL auto-provisioning (#652), DID-routing mediators via--mediator-did(#952), unknown inbound protocols dropped not fatal (#905),affinidi-tdkhard-pinned at the enforced-authcrypt line (#908). - did:webvh: agent-name Trust Tasks end-to-end (
pnm did-mgmt agent-names …,alsoKnownAsclaim; #718–#723, #758, #777); wedge/recovery fixes — unpublished local head, missing confirmed-publish marker, partial edit serialising nulls (#894–#896), sign with the update keys in force (#972);--did-log-filefor offline publishing (#932); host reconcile task aligned with the spec published upstream (#976/#977); stop sending URIs did-hosting retired in 0.8.3 (#879); find DIDs a host serves that this VTA has no record of (#976). - VTC: headless two-phase setup with an explicit secrets backend (#625); signed audit checkpoints so truncating the log stops being invisible (#798, #808); REST-only auth refresh for mediator-less clients (#796);
submit_joinsigns its own document and needs no token (#882); an applicant can poll a join without knowing its request id (#985, breaking — the VTC half of OpenVTC v0.3.0’s async join work); “step-up means recent” for admin promotion (#811); a community advertises the registry authoritative for it (#877);clianswers a consent gate instead of dying on it (#897). - Design notes worth reading: governance policy as a credential (#859, Option A
GovernancePolicyCredential, deferred pending sign-off),sdk-session-hub.md,registry-drift-triage.md,vtc-audit-checkpoints.md.
Dependency moves — July–August 2026
trust-tasks-rs 0.2 → 0.9 (0.4 #911 with the consent digest becoming digestMultibase; 0.6 #979; 0.9 #996; error doc 0.3 → 0.5); dtg-credentials 0.1.3 → 0.2 (#916 — DTG Core Credentials WD01, see dtg-credentials; VMC/VEC/VIC bytes unchanged); curve25519-dalek 5 / ed25519-dalek 3 (#887, breaking) with SLIP-0010 derivation moved in-tree, dropping ed25519-dalek-bip32 (#890); didwebvh-rs 0.6 (#712); messaging-sdk 0.19.8 / mediator 0.18.18 / delivery 0.1.14 / affinidi-tsp 0.1.14; the long-standing [patch.crates-io] vta-sdk self-pin finally dropped (#958). Configurable unauth rate limit (#936); payload validation against published schemas (#657); reactive re-auth on 401/403 (#633).
The June–July cycle (289 commits, PRs #313–#624 since 2026-06-07) matched the record May–June push and pivoted the workspace twice: first a systematic P0–P3 security and architecture campaign capped by the Banyan milestone tag (2026-06-22), then a sharp turn to TSP enablement with transport preference officially flipped to TSP > DIDComm > REST. Two new product thrusts emerged alongside: personal AI agents (the VTA as the trust anchor under agent runtimes, with a new vta-mcp MCP server) and enterprise fleet management (owner/user separation of duty).
TSP enablement — late June–July 2026 — transport preference flips to TSP > DIDComm > REST
The headline of the cycle. A 2026-06-22 decision record (docs/05-design-notes/messaging-routing-and-tsp.md) initially deferred TSP because the Affinidi mediator lacked TSP routing and affinidi-tsp was inert scaffolding — then both blockers cleared upstream within three days, and an SDD (docs/05-design-notes/tsp-enablement.md, #579) reversed the decision and flipped transport preference guidance to TSP > DIDComm > REST, effective immediately. Spec basis: ToIP TSP Specification Rev 2 (Nov 2025 Experimental Implementer’s Draft).
Key locked decisions: DIDs are TSP VIDs reusing existing Ed25519/X25519 keys (no new key material); one dual-protocol mediator serves both TSP and DIDComm; capability discovery is DID-document-driven, matched by service type not #id fragment (ids renamed to #didcomm / #tsp / #rest); a tsp cargo feature mirrors didcomm, off by default initially.
Implementation landed as a stacked PR train (2026-06-25 → 07-03): tsp feature across sdk/service/enclave/vtc (#580); TSPTransport service patchers with TSP-first canonical ordering (#581) and advertisement in DID templates (#584); a peer-matching engine for capability discovery + protocol selection (#583); TSP as a first-class managed service — ServiceState::Tsp with enable/update/disable/rollback over REST, DIDComm, and CLI (#585–#589) and declarative setup (#598); a TSP inbound listener over the shared mediator websocket (#595, #601 — no second socket, no mediator flapping); vault unsealing of tsp-message sealed envelopes (#594); a TSP round-trip health probe (pnm health TSP ping, #610–#618); and an operator guide (docs/02-vta/tsp.md). Strictly additive at the time — feature-gated and opt-in; by August TSP had become a selectable transport with TSP-only VTAs supported (see above).
Security hardening + architecture campaign (P0–P3) — 2026-06-10 → 06-16
Roughly 100 commits executing numbered remediation plans (tasks/vta-architecture-plan.md, tasks/vtc-architecture-plan.md, tasks/test-harness-plan.md) — reads like the output of a full security review of both services.
- P0 critical fixes: AES-GCM AAD binding of keyspace values to their (keyspace, key) location, defeating ciphertext cut-and-paste by an untrusted Nitro parent instance (breaking on-disk format, magic
VAE1, #346); TEE anti-rollback — local MAC’d integrity manifest + boot verify (#380), external DynamoDB anti-rollback counter with CAS (#383), attestation-gated anchor writer (#386); DIDComm sender authentication rather than trusting plaintextfrom(#350); VMC-subject↔VEC-subject binding + holder proof-of-possession in recognise (#351, #354); SSRF/DoS hardening of foreign status-list fetches (#357); master-seed zeroization (#353); encrypted-at-rest install/audit-key/passkey keyspaces (#364); step-up enforcement on vault release / proxy-login / sign-trust-task (#362); TOCTOU serialization on last-admin admit (#385); fail-closed secret backends (#381); Rego evaluation bounds (#372). - P1/P2 structural refactors: single token-mint path and single DI-proof verifier in both services (#399, #402); central keyspace-name registries; setup engine split into pure prompting over
apply_inputs; VTC ceremony orchestration unified underceremony/with one facts-assembly path (#453–#462); a route-posture backstop test — every unauthenticated route must be classified (#457). - P3 defense-in-depth + features: per-surface host isolation for VTC websites (#465/#466); client-side PCR pinning for bootstrap connect (#388); encrypted full-state VTC backup/restore (#494) with
cnm backup export/import(#497); non-interactivevtc setup --from <toml>(#491); Kubernetes Secret backend (#486); a real device kill-switch —device/wipeenforced at auth (#493). - Test/fuzz infra: MockVta/MockVtc in-process harnesses with e2e seams (26 fixtures migrated, #348); a new
fuzz/workspace member with IO-free parse cores and fuzzable Nitro-attestation parsing (#443, #450, #477); OpenAPI 3.1 specs for the full VTA and VTC surfaces (#447/#448). - Audit trail: coverage extended across authorization/invitation/session/backup/admin ops, with payload enrichment and a tamper-evidence hash chain (#535–#555).
A coordinated pre-1.0 legacy strip landed on 2026-06-09: legacy affinidi.com/atm/1.0 auth aliases, pre-spec passkey-vms/1.0 URIs, webvh-*/did-hosting-* template aliases, and the deprecated pnm webvh CLI alias all removed (#330–#334).
VTC join ceremony, VIC, and the new vtc-client crate — June 2026
- Spec-first Trust-Task join verbs:
join-requests/{accept,manifest,status}specs + implementations, including the reciprocal member VMC — the VTC receives a member-issued credential back (#315–#320, #549), making membership bidirectional in practice. - Automatic VTC join via Verifiable Invitation Credential (#522), with role-on-invite, revocation, linkage proof (#526), QR-sized invitations in the admin UI (#527), and invitation-facts logging behind join verdicts (#546). The join-request ceremony became a Trust Task document flow (#541). This is the server side of the VIC join flow that openvtc shipped in the same window.
- New
vtc-clientOSS crate (#567): auth + member listing, admin ops (join approval/rejection, member removal), policy management, submit_join. - Admin UI: member-relationship connections graph (#530); departed-member purge/re-invite (#532).
Personal AI agents — AgentSession + vta-mcp — June 2026
A clear new product thrust: the VTA as the trust anchor for personal AI agent runtimes (runbook: docs/02-vta/personal-ai-agents.md).
AgentSessionon vta-sdk — a high-level personal-AI-agent runtime helper (#496).vta-mcp(#489) — an MCP stdio server bridging the VTA’s capabilities (signing oracle, secrets vault, device check-in, discovery) to any MCP host (Claude Desktop is named explicitly); expanded to the full VTA surface via genericvta_call+ catalog,resolve_did,issue_vp(#499).- Supporting VTA features: an
ai-agentDID template (#482); per-context KV store for agent memory (#574); issue + revoke scoped Verifiable Credentials (#573); an ephemeral derive-and-sign trust task (#575–#577); self-contained did:peer agent identities viavta create-did-peer/ provision-integration (#590, #612).
Step-up, consent, and mobile-core — June–July 2026
- Step-up policy became runtime-manageable (delegatedAny, per-entry
stepUp.require, policy CLI, #324–#329;docs/02-vta/step-up-policy.md). - Consent architecture in two tracks: a VTA consent store as first-gate for inbound bridged messaging (#524), per-platform approver registry (#528), wake-route approvers for did-signed decisions (#529).
vta-mobile-core0.3.1 → 0.6.11: adopted the push gateway model (push/register + device/set-wake, #314); DID-basedresolve_vta_endpointsdiscovery (#319); iOS TLS + diagnostics fixes. The July burst (#621–#624, v0.6.9–0.6.11) upgraded the mobile approver into a full cryptographic party to authorization decisions: step-up approve-response 0.2, structuredauthorizationContextcarried to the approver, and signed denial.
did:webvh maturation — June–July 2026
The VTA now serves its own did:webvh log at canonical did.jsonl paths with correct content type (#559, #561 — the Banyan tag commit); preloads its self-DID into the resolver cache and keeps it in sync after runtime DID-log mutations (#603, #616 — fixes serverless/private-network self-resolution); authenticates to the hosting server when publishing agent DIDs (#604); and backdates/spaces versionTime to avoid same-second collisions (#600, #605 — the fix that drove didwebvh-rs 0.5.6’s caller-settable versionTime).
Enterprise fleet management (proposed) + secrets split — June 2026
- New design note
docs/05-design-notes/enterprise-fleet-management.md(Proposed): extends the VTA from individual owner-operator to the enterprise case with hard owner/user separation of duty and fleet management. First primitives landed: per-contextContextPolicy(#566, #570). vti-secretsextracted from vta-service (#503): pluggable secret-store backends (plaintext, HashiCorp Vault, KMS/TEE, Kubernetes Secrets) reusable by external integrations; the VTC migrated onto it in three phases, dropping direct cloud-SDK dependencies from both services (#506–#509).
Dependency moves — June–July 2026
affinidi-tdk0.8 (#525); messaging stack bumped to TSP-capable versions (#593) — the upstream mediator’s dual-protocol TSP↔DIDComm bridging is what unblocked TSP enablement.- Crate movement across the window (no single workspace release; continuous per-crate bumps): vta-sdk 0.10.0 → 0.18.17 (the largest jump — huge API surface growth), vta-service 0.9.0 → 0.10.23, vtc-service 0.8.1 → 0.10.13, vti-common 0.9.1 → 0.11.2, pnm-cli/cnm-cli → 0.10.x.
- VTC admin UI: esbuild dropped for Vite 8 (rolldown); Node 22 pinned.
The May–June cycle below was the previous record push (~530 commits since 2026-05-08), dominated by VTC service maturation, credential exchange end-to-end, and mobile + WebAuthn (the new vta-mobile-core and vti-webauthn crates).
vta-mobile-core v0.3.0 (+ mobile-agent architecture spec) — June 2026
The shared Rust engine for two iOS/Android apps: Authenticator (a holder’s pocket approver for VTA/RP-pushed AAL step-up “approve-request” prompts, returning a passkey- or DID-signed approve-response over DIDComm v2) and PNM mobile (the mobile counterpart of the pnm operator CLI, driving the management surface over the same Trust-Task wire as the CLI). Built as UniFFI: Android AAR, iOS xcframework.
- Architectural rule (
docs/05-design-notes/mobile-agent-architecture.md): shared engine, native edges. Everything cryptographic and wire-shaped lives in the Rust core; everything stateful or platform-bound (Secure Enclave / StrongBox custody, sockets, push receipt, UI) stays native. DIDCommSession::receive_next(timeout_secs)onvta-sdk— receives unsolicited inbound messages from the mediator’s live stream, the foundation for the mobile approver receiving VTA-pushedauth/step-up/approve-request/0.1.- Set delegated step-up approver at ACL
grantandupdatetime (closes the previously test-only-set path). - v0.2.0 / v0.2.1 / v0.3.0 tags shipped in sequence.
Hierarchical contexts — May 2026 — slices 1–4
A context ID is its /-separated path (max depth 8) at the VTA, with ancestry-aware ACL semantics (parent-admin authority covers the subtree). This drops the “convention-only” sub-context model OpenVTC’s multi-community design originally assumed and replaces it with a server-enforced one.
- Slice 1 (
vti-common) —context_pathmodule:/-separated path IDs, segment-aware ancestry, ancestry-aware ACL gate. - Slice 2 (
vta-service) — nested context creation + BIP-32 nesting +--parentCLI. - Slice 3 — subtree delete (cascade/refuse) + folder-admin authority.
- Slice 4 — ancestry-aware ACL list filter; ACL-gated holder-key resolution (
HolderKeyProvider) for presentation.
MockVta + tests/e2e crate — May–June 2026
A one-call listening VTA harness (vta-service) for downstream consumers’ integration tests. The new tests/e2e crate exercises transient-mediator handshakes and DIDComm session sequential-reuse-no-duel; the harness is consumed from crates.io rather than the local path, so OpenVTC’s T9 work (multi-community integration tests) now has a real target.
Credential exchange end-to-end (close-the-join-loop) — May–June 2026
- OID4VP DCQL
vp_tokenverification — both holder side (present_query: full holder query → present path, consent-gated) and verifier side (vp_tokenmap verification at the VTC join verifier). credential-exchange/presentDIDComm handler + single-use challenge — closes the join loop.- VTC drives the join decision from a verified presentation; the credential-query push is routed via the holder’s own mediator.
- W3C Data-Integrity VP verification + did:webvh / did:web resolution on the verifier side.
- Live status re-check at present time (§14.5); status-list credential issuer signature verified at every check.
- TRQP issuer-trust wired into the join presentation evidence; issuer-bound did:webvh / did:web DI resolution for credentials.
- VTC delivers the membership credential to the holder on DIDComm admit and on admin-approve; re-mints the role VEC on role change with share delivery.
- Consent policy — auto-consent trusted verifiers, else defer.
- Holder offer → request leg — answer a credential offer with a key-binding proof.
BBS-2023 selective disclosure end-to-end — May–June 2026
Feature bbs across VTA and VTC. Built on the new affinidi-bbs crate in the TDK.
- VTA receives BBS credentials into the vault over the credential-exchange/issue path.
- VTA presents BBS credentials with selective disclosure.
- VTC join verifier accepts
bbs-2023presentations in the same evidence pipeline as standard W3C Data Integrity VPs.
Trust Tasks 0.2 — June 2026
Dual-accept envelopes: a single Trust Task envelope can be accepted at one of four authentication ladder rungs (device / vault / passkey / step-up). The 0.1 → 0.2 migration is back-compat — both shapes are accepted for one release.
trust-tasks-rs+trust-tasks-proofbumped to 0.2.1; trust-tasks 0.2 specs with 0.1 back-compat.passkey-vms/0.1 dual-accept + 0.1 error taxonomy;vta-sdk0.10.0 release.provision-integrationdual-accept 0.2; retire legacy FPN URI.- In-band recipient set on every trust-task envelope (0.2 requires it).
VTC service maturation — May–June 2026
- Default community website (no more 503 on a fresh install).
- Admin UX (M5.6 + M5.7) in-tree per D1 — admin console scaffold (React + Vite + plugin API); third-party plugin loader scaffold.
- Ceremony decision pipeline design bundle under
docs/05-design-notes/: catalog, pipeline, protocol, rule IR (withregorusRego examples for join / leave / role-change / directory), executive summary, visual guide. - Phase-3 tail complete: W3C-DI present, deferred approval, sealed issuance, Trust-Task descriptors.
- Documentation reconciled with the post-Phase-5 reality.
Documentation restructure — May–June 2026
Top-level docs reorganised into 01-concepts/ (overview, architecture, security model), 02-vta/, 03-vtc/, 04-reference/, 05-design-notes/ (mobile agent, hierarchical contexts, ceremonies, credential architecture, trust-task migration runbook, webvh REST auth audit). The flat docs/design.md is gone — replaced by a hierarchical book. VTA/VTC split into dedicated chapters.
Dependency moves — May–June 2026
- DIDComm 0.15 across the workspace (
affinidi-messaging-didcomm0.14 → 0.15); resolves the previous two-Message-type split, picks upaffinidi-crypto::josefor key agreement. affinidi-crypto0.2 (vta-sdk 0.9.11); affinidi-tdk 0.7.x line consumed.- vta-sdk 0.9.0 → 0.10.0.
- trust-tasks-rs + trust-tasks-proof 0.2.1.
v0.6.0 (in flight, P0–P5 merged 2026-05-05 to 2026-05-06) — runtime service management
Multi-PR feature campaign adding a unified pnm services … CLI surface for managing runtime services on a live VTA, plus extensions to the webvh and DIDComm provisioning surfaces. P0–P5 merged on main; P6 (e2e matrix), webvh register-did-with-server / edit-did, DIDComm --create-context, and the 0.6.0 workspace bump are on a feature branch.
- Unified
pnm services …CLI: enable, disable, list, list_drain, rollback (REST + DIDComm) - Snapshot-store / fail-forward semantics (per-kind snapshot store; brick-prevention helpers)
- Operator-as-relayer over DIDComm (provision-integration)
- DIDComm-is-holder-driven clarification + Forbidden mapping fix
- TEE deployment hardening continues
v0.5.1 (vta-service) — 2026-05-05 — provision-integration hotfix
vta bootstrap provision-integrationnow produces an actionable error when the target context is missing and--create-contextwasn’t passed (CLI-only behavior change)
v0.5.0 — 2026-05-04 — sealed-bootstrap major release
Every secret-bearing transfer between VTA, integrations, and CLIs now moves as an HPKE-sealed bundle; DID minting is template-driven; the DIDComm protocol surface is mutable on a running VTA without rebuilding it.
- HPKE-sealed bundles for every secret-bearing transfer between VTA, integrations, and CLIs
- Template-driven DID minting
- DIDComm protocol surface can be enabled, disabled, or migrated on a running VTA without rebuilding it
- Six new operator commands:
pnm services {enable,disable} didcomm;pnm mediator {migrate, rollback, drain cancel, report}. All five admin operations available over both REST and DIDComm transport - Mediator changes go through a drain set (persisted to fjall, restart-resilient, 30-day TTL cap) so in-flight messages from senders with stale DID-doc caches keep landing while the new mediator picks up traffic
- WebVH built-in templates renamed by deployment role:
webvh-hosting-server→webvh-daemon(hosting only),webvh-service→webvh-server(DIDComm only), newwebvh-control(hosting + DIDComm) - Multi-agent publish-readiness review folded in:
VtaErrortightened (lossy auto-conversions removed);verify_vta_authorization_credentialreturns a typestate (forgettingparse_claimis a compile error); refresh tokens rotate on every/auth/refresh(RFC 6749 §10.4, single-use);server_internal_super_adminreplaced with a sealedInternalAuthoritymarker - CVE-2026-42327 mitigated via enclave-proxy openssl 0.10.78 → 0.10.79
v0.4.1 — 2026-04-15
- TEE deployment hardening
- Documentation rationalization
- Dockerfile cleanup
v0.4.0 — 2026-04-13 — DIDComm service v0.2
- Production lifecycle management for the DIDComm service
- Message expiry
- Problem-report logging
- Mediator connection unification
v0.3.x — April 2026
- Client DID documents
- Capabilities discovery
- User-specified keys
v0.3.0 — March/April 2026
- SDK integration module
- Imported secrets
- Lightweight DIDComm auth
- Reader role
- Automatic token refresh
v0.2.0 — March 2026
- TEE / Nitro Enclave support
- Signing oracle
- DIDComm migration
- Backup / restore
- P-256 keys
- Prometheus metrics
See also: verifiable-trust-agent, verifiable-trust-community, openvtc, verifiable-git-infrastructure, peer-identity-vetting, trust-tasks, community-git-namespaces, kernel-web-of-trust, keyring-wallet, coordinated-releases