From a82bf8d4aa40e4ce36b757cc5fb177fd913cf2e8 Mon Sep 17 00:00:00 2001 From: Glenn Gore Date: Mon, 7 Sep 2026 22:45:51 +0200 Subject: [PATCH] chore(core): take adminScope from the registry binding, not a local copy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `@openvtc/trust-tasks` 0.17.4 is the first release whose generated `provision/integration/0.3` binding declares `adminScope` and the `context` / `adminScope` summary members. This package shipped ahead of it, so it declared all three by hand with a comment promising they would go the moment the binding caught up. It has, so they go. `AdminScope` stays as an exported name — a dozen wallet surfaces name the concept without ever touching the payload type — but it is now derived (`NonNullable`) rather than written out. A hand-kept union is a second declaration of a registry-owned vocabulary and is free to drift from it silently; a derived one cannot. `ProvisionIntegrationResponseBody` collapses back to the generated response type for the same reason. The two version floors are named in CLAUDE.md now that they are concrete, with the part that is easy to get wrong: `trust-tasks-rs` 0.18.3 is a correctness constraint on the VTA because its dispatch spine validates **outgoing** responses against the embedded schema, not just inbound payloads. Against 0.18.2 the new summary members are emitted and then refused by the agent's own guard, so a provisioning that fully succeeded returns `500 responseSchemaViolation`. That is what the VTA-side PR's `delegated_consent_e2e` failure was, and it is worth recording as something other than a dormant feature. No behaviour change: same wire members, same spellings, one source for them. Signed-off-by: Glenn Gore --- CLAUDE.md | 15 ++++++++--- package-lock.json | 8 +++--- packages/core/package.json | 4 +-- packages/core/src/provision/send.ts | 40 +++++------------------------ 4 files changed, 25 insertions(+), 42 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index cc0fb63..2ea5ce2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -167,9 +167,18 @@ what an ACL reads as a super-admin. Before it existed the VTA wrote out of provisioning as anything but a context admin** and the console had no way to be granted what it needs. The ephemeral relayer's own super-admin-ness was never inherited; it only ever affected context inference and inline context -creation. Floor: the registry release carrying `adminScope`, and a VTA past -`trust-tasks-rs` with that schema — below it, `validate_payload` rejects the -member at the dispatch spine before any handler sees it. +creation. + +**Two floors, and both are correctness constraints rather than version +preferences.** `@openvtc/trust-tasks` **0.17.4** is the first binding declaring +`adminScope` and the `context` / `adminScope` summary members; below it this +package cannot name them. `trust-tasks-rs` **0.18.3** is the first schema the +VTA can carry them under, and the VTA pins it as a floor for a reason worth +knowing here: its dispatch spine validates **outgoing** responses against that +embedded schema, not just inbound payloads. Against 0.18.2 the members are +emitted and then rejected by the agent's own guard, so a provisioning that +fully succeeded comes back `500 responseSchemaViolation` — not a dormant +feature, a broken one. **The order of the two questions differs by scope, and that is forced.** The grant command has to match the scope and only the operator can run it: diff --git a/package-lock.json b/package-lock.json index bf8ff94..71012a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2304,9 +2304,9 @@ "link": true }, "node_modules/@openvtc/trust-tasks": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/@openvtc/trust-tasks/-/trust-tasks-0.17.1.tgz", - "integrity": "sha512-KdOuBThD0AMCd4mBnxxbQK6CcKHGpb9mXh/xyBR6AhkXFIr7yt67avFxNRkXD+OdNus4bDWn8d/87sedO8uUZA==", + "version": "0.17.5", + "resolved": "https://registry.npmjs.org/@openvtc/trust-tasks/-/trust-tasks-0.17.5.tgz", + "integrity": "sha512-0oeNHPsZipos4X2c9SteMsrzesgpwPr8O50fVHeIpAgKBugrO/GyGjzEL1k60aaEX8HY+ZM9mLsoMtikIBhoBQ==", "license": "Apache-2.0" }, "node_modules/@openvtc/vti-didcomm-js": { @@ -8034,7 +8034,7 @@ "dependencies": { "@cfworker/json-schema": "^4.1.1", "@noble/curves": "^2.4.0", - "@openvtc/trust-tasks": "^0.17.1", + "@openvtc/trust-tasks": "^0.17.4", "@openvtc/vti-didcomm-js": "^0.7.0", "@openvtc/vti-tsp-js": "^0.2.0", "@scure/base": "^2.2.0", diff --git a/packages/core/package.json b/packages/core/package.json index ad7fdb6..b467262 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,7 +1,7 @@ { "name": "@openvtc/pnm-core", "version": "0.9.1", - "description": "Browser-side bridge between WebAuthn passkeys and VTA-managed DIDs. Wire types, WebAuthn ceremony helpers, COSE\u2192Multikey conversion, DID verificationMethod builder, REST + DIDComm transports, mediator client, SIOP / RP-login / provision-integration flows.", + "description": "Browser-side bridge between WebAuthn passkeys and VTA-managed DIDs. Wire types, WebAuthn ceremony helpers, COSE→Multikey conversion, DID verificationMethod builder, REST + DIDComm transports, mediator client, SIOP / RP-login / provision-integration flows.", "license": "Apache-2.0", "repository": { "type": "git", @@ -126,7 +126,7 @@ "dependencies": { "@cfworker/json-schema": "^4.1.1", "@noble/curves": "^2.4.0", - "@openvtc/trust-tasks": "^0.17.1", + "@openvtc/trust-tasks": "^0.17.4", "@openvtc/vti-didcomm-js": "^0.7.0", "@openvtc/vti-tsp-js": "^0.2.0", "@scure/base": "^2.2.0", diff --git a/packages/core/src/provision/send.ts b/packages/core/src/provision/send.ts index 6e64b94..deb2005 100644 --- a/packages/core/src/provision/send.ts +++ b/packages/core/src/provision/send.ts @@ -114,14 +114,6 @@ export function provisionRefusalOf(e: unknown): ProvisionRefusal | undefined { */ export type ProvisionIntegrationRequestBody = Omit & { request: BootstrapRequestVp; - /** How wide the ACL entry the VTA writes for the minted admin should be. - * - * Declared here rather than taken from the generated binding because the - * registry release carrying it is newer than the `@openvtc/trust-tasks` - * this package pins; the spelling is the registry's, verbatim, and this - * member disappears the moment the binding catches up. It is not a - * compatibility fold — there is one spelling, and it is this one. */ - adminScope?: AdminScope; }; /** @@ -140,8 +132,13 @@ export type ProvisionIntegrationRequestBody = Omit; /** * Body of the `provision/integration/0.3#response` reply. @@ -156,30 +153,7 @@ export type AdminScope = "context" | "unrestricted"; * canonicalization — re-armoring the same ciphertext need not reproduce the same * bytes, so re-deriving it from a round-tripped bundle can legitimately disagree. */ -export type ProvisionIntegrationResponseBody = Omit< - ProvisionIntegrationResponsePayload, - "summary" -> & { - summary: ProvisionSummary & { - /** The context the admin was actually provisioned into — sent or, for a - * caller that omitted `context`, inferred by the VTA. - * - * Absent from agents that predate the member. A wallet that named the - * context can fall back to what it asked for; one that did not has no - * honest answer and must not invent one. */ - context?: string; - /** The scope of the ACL entry the VTA actually wrote. - * - * Read rather than assumed: an agent that does not implement - * `adminScope` ignores an `"unrestricted"` ask and writes a - * context-scoped entry, and its success reply is otherwise - * indistinguishable from one that honoured it. **Absent means - * `"context"`** — the wallet did not get what it asked for, and a - * surface that displayed the ask instead would claim authority the - * holder does not have. */ - adminScope?: AdminScope; - }; -}; +export type ProvisionIntegrationResponseBody = ProvisionIntegrationResponsePayload; export type { ProvisionSummary };