Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions packages/core/package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down Expand Up @@ -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",
Expand Down
40 changes: 7 additions & 33 deletions packages/core/src/provision/send.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,14 +114,6 @@ export function provisionRefusalOf(e: unknown): ProvisionRefusal | undefined {
*/
export type ProvisionIntegrationRequestBody = Omit<ProvisionIntegrationPayload, "request"> & {
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;
};

/**
Expand All @@ -140,8 +132,13 @@ export type ProvisionIntegrationRequestBody = Omit<ProvisionIntegrationPayload,
* `"unrestricted"` is refused unless the ephemeral's own grant is
* unrestricted — no admin confers authority it does not hold — so the grant
* command the operator runs has to match the choice made here.
*
* **Derived from the generated binding, not written out.** A hand-kept union
* would be a second declaration of a registry-owned vocabulary, free to drift
* from it silently; this one cannot. The alias exists only so surfaces that
* never touch the payload type can still name the concept.
*/
export type AdminScope = "context" | "unrestricted";
export type AdminScope = NonNullable<ProvisionIntegrationPayload["adminScope"]>;

/**
* Body of the `provision/integration/0.3#response` reply.
Expand All @@ -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 };

Expand Down
Loading