#Verifiable Delegated Agent Credentials (VDAC) — v1 profile
Version 0.1 · Public draft · 2026-08-23
#Status of this document
This is a public working draft, published for review. It describes version 1 of
the VDAC credential profile (vct: urn:otcp:vdac:1) and its verification algorithm,
as implemented and shipped.
The specification is derived from a working implementation, not ahead of one. Where this document and the reference implementation disagree, the implementation is correct and this document has a bug — please report it. The normative surfaces (payload schema, canonical action hashing, the eleven verification steps, and the reason-code vocabulary) are stable enough to build against. §12 lists what is explicitly not settled.
Nothing here is a standards-track document of any standards body. VDAC is a profile over existing standards — SD-JWT VC, OpenID4VP, and IETF Token Status List — not a replacement for any of them.
#1. Introduction
An AI agent that acts for a person needs to answer one question at the moment it acts: on whose authority?
Bearer tokens answer it badly. An API key says "someone who had this key" — it does not say who authorized it, what they authorized, whether that authorization has been withdrawn, or whether the human is accountable for the outcome. When the actor is autonomous and the action moves money or creates an obligation, "someone had the key" is not a satisfying answer to a regulator, a merchant, or the person whose account was drained.
A VDAC is a credential in which a KYC-verified person delegates scoped, attenuating, revocable authority to a specific agent key. It is:
- self-issued by the principal's wallet and signed by the principal's own secure-element key — no issuer sits in the delegation path;
- rooted in a verified identity by embedding the principal's identity credential, so the chain terminates in an accountable human rather than in another token;
- bound to the agent's non-exportable key, so it is useless to anyone who steals the credential without the key;
- bound to the specific action for anything state-changing, so a captured presentation cannot be replayed against a different transaction;
- revocable at three independent levels, so the person can kill one delegation, one agent key, or every delegation at once.
#1.1 What a VDAC is not
- Not a payment instrument. A VDAC authorizes; it never settles. No rail moves money because a VDAC exists. A verifier that accepts one still performs its own settlement under its own licence.
- Not an identity credential. It carries no attributes of its own. It embeds an identity credential to establish accountability, and it discloses nothing from it by default.
- Not a trust list. Accepting a VDAC is a verifier policy decision. This profile defines what a verifier can check, never who it must trust.
#2. Terminology
| Term | Meaning |
|---|---|
| Principal | The verified human who delegates. Holds the identity credential and the key that signs VDACs. |
| Agent | The autonomous software the authority is delegated to, identified by a non-exportable key. |
| Verifier | The party evaluating a presentation against its own policy — typically on behalf of a relying party. |
| Relying party (RP) | The merchant, service, or system relying on the verdict. |
| Root VDAC | The VDAC signed by the principal, embedding the identity credential. |
| Sub-VDAC | A VDAC signed by an agent, re-delegating a subset of its own authority. |
| Chain | A leaf VDAC plus every ancestor up to the root. A lone VDAC is a chain of length 1. |
| Action | The concrete operation being authorized, in canonical form (§5.2). |
| SUA | Step-Up Assertion — the principal's signed approval of one specific action (§5.4). |
The key words MUST, MUST NOT, SHOULD, and MAY are to be interpreted as in RFC 2119.
#3. Design invariants
Seven invariants hold for every VDAC and every verification. They are the reason the format looks the way it does, and a profile that drops one is not this profile.
| Invariant | Enforced by | |
|---|---|---|
| I1 | Attenuation only. A child's authority is a subset of its parent's. Authority never widens on delegation. | §6 steps 4b, 7, 8 |
| I2 | Non-transferable agent key. The agent key is hardware-rooted and non-exportable; it moves only by the principal re-issuing. | §4.2 cnf, §6 step 5 |
| I3 | Action-bound presentation. State-changing classes bind the exact action by hash; read-only classes bind nonce and audience. | §5.2, §6 step 6 |
| I4 | Bounded autonomy. Above a threshold — the principal's or the verifier's — a human approves that specific action. | §5.4, §6 step 10 |
| I5 | Three-level revocation with bounded freshness. Principal root, delegation, and agent key revoke independently, each with a status-age bound. | §4.4, §6 step 9 |
| I6 | Non-repudiation without identification. The principal signs the chain; the verifier learns a pseudonym, not an identity. | §9 |
| I7 | Typed authority, fail-closed. Authority is typed; an unrecognized type denies. | §4.3, §6 step 3 |
#4. Credential format
#4.1 Envelope
A VDAC is an SD-JWT VC (media type dc+sd-jwt). In v1 the payload uses no
selective disclosure of its own — the disclosure machinery matters for the embedded
identity credential, not for the authority claims, which the verifier needs in full.
The JWS protected header:
{ "alg": "ES256", "typ": "dc+sd-jwt", "crit": ["otcp_authority"] }
otcp_authority is a critical extension: a verifier that does not understand
typed authority MUST reject the credential rather than ignore the claim (I7). A
verifier MUST reject any crit member it has not registered (§6 step 3).
#4.2 Payload
| Claim | Type | Req. | Meaning |
|---|---|---|---|
vct |
"urn:otcp:vdac:1" |
✔ | Credential type. |
jti |
string (UUID) | ✔ | Stable VDAC id. The usage-accounting key — cumulative and velocity caps are counted per jti. |
iss |
did:jwk:… |
✔ | The signer. On a root VDAC, the principal's key; on a sub-VDAC, the delegating agent's key. |
sub |
string | ✔ | The agent this authority is granted to (did:jwk: in v1). |
cnf |
{ jwk } |
✔ | The agent's non-exportable holder key (I2). The KB-JWT verifies under this key. |
principal |
{ otcp_id } |
root only | The embedded identity credential, compact SD-JWT (§4.6). |
parent |
string | sub only | The parent VDAC, embedded compact (§4.5). |
otcp_authority |
array | ✔ | Typed authority grants (§4.3). Critical. |
delegable |
false | { depth, narrowing_required } |
✔ | Opt-in re-delegation. Default false (§4.5). |
mode |
"projection" | "mediated" |
✔ | v1 implements projection only. |
linkability_path |
"none" |
✔ | Reserved escrow hook; always "none" in v1 (§9). |
freshness |
{ max_status_age_default } |
✔ | ISO 8601 duration bounding status age (I5). |
iat, exp |
number | ✔ | Unix seconds. A sub-VDAC MUST NOT outlive its parent. |
status |
object | ✔ | The three status entries (§4.4). |
agent_key_attestation |
object | ✖ | Evidence about how the agent key is held (§4.7). |
agent_meta |
object | ✖ | Descriptive agent metadata. Never a trust input. |
#4.3 Authority classes
Authority is a list of typed grants. Five classes exist in v1; an unrecognized
type MUST deny (I7).
present-attribute — disclose a verified claim for a decision. No onward-use
grant. Baseline binding only.
{ "type": "present-attribute", "attributes": ["age_equal_or_over.18"] }
act — operate a resource. verb: "write" requires action-binding; "read" is
baseline.
{ "type": "act", "verb": "write", "resource": "calendar",
"counterparties": { "allow": ["*"], "deny": [] }, "velocity": { "count": 10, "window": "P1D" } }
spend — move funds up to caps. The rail moves the money; the VDAC is a
constrained mandate.
{ "type": "spend", "currency": "EUR", "per_tx_max": "200.00", "cumulative_max": "500.00",
"counterparties": { "allow": ["*"], "deny": [] }, "stepup_over": "100.00",
"recurrence": "one_shot" }
commit-obligate — bind the principal to a future obligation. Carries
total_liability_max. Always triggers step-up (§6 step 10).
sign-attest — a non-repudiable statement in the principal's name, bound to a
document hash. Carries an optional purposes allow-list. Always triggers
step-up.
Common optional fields: counterparties: {allow, deny} (matched on the canonical
payee identifier, not a display name), jurisdictions, velocity: {count, window},
recurrence, stepup_over.
All monetary amounts are string decimals at the currency's scale —
"200.00", never200.0. Floats are not permitted anywhere in this profile. Arithmetic is exact (the reference implementation is BigInt-backed).
#4.4 Status
"status": {
"principal_root": { "status_list": { "uri": "https://status.example/lists/principals", "idx": 42 } },
"delegation": { "status_list": { "uri": "https://status.example/lists/vdacs", "idx": 7 } },
"agent_key": { "status_list": { "uri": "https://status.example/lists/agents", "idx": 3 } }
}
Three independent IETF Token Status List entries (I5), which is what makes revocation useful rather than nominal:
principal_root— revoking it kills every delegation the person ever made. It reuses the identity credential's own status entry, so losing the identity loses the delegations automatically.delegation— kills this VDAC and everything delegated below it.agent_key— kills one agent's key across the delegations that bind it.
Lists MUST be large and shared, never per-principal: a list whose size or URL is unique to one person leaks that person. The principal→index map is private to the status service.
#4.5 Delegability and sub-VDACs
delegable is false unless the principal opts in:
"delegable": { "depth": 1, "narrowing_required": true }
A sub-VDAC omits principal, carries the compact parent in parent, and is signed
by the delegating agent's key — which MUST equal the parent's cnf key. Root
binding is reached by walking up the chain. v1 implements same-principal projection
only: mode MUST be "projection". Chains are bounded at 6 links by the reference
implementation.
Every constraint in §6 step 4b applies per link: signature, delegator linkage, depth, attenuation, and expiry. A sub-VDAC that widens authority, outlives its parent, or exceeds the permitted depth is rejected at both issuance and verification — "no free chains".
#4.6 Root binding by embedding
The root VDAC embeds the principal's identity credential in principal.otcp_id as a
compact SD-JWT. The identity credential's cnf key MUST equal the root VDAC's iss
key — which means the root VDAC's own signature is the holder proof-of-possession
for the identity credential. No separate PoP is transmitted, and the agent never
holds the principal key.
The embedded credential SHOULD carry no disclosures unless the verifier's policy
requires a specific attribute. The wallet embeds <issuer-jwt>~ — issuer JWT only —
so a delegation reveals no name, no date of birth, nothing beyond the assurance level
and the binding key.
#4.7 Agent-key attestation
"agent_key_attestation": { "level": "hardware_tee", "platform": "android", "verified_at": 1787000000 }
level is one of hardware_strongbox, hardware_tee, software (extensible).
This field MUST be absent unless the issuing wallet actually verified the protection of the key it is attesting to. A wallet that cannot inspect the agent key — which is the normal case for a remote agent — MUST NOT emit the field. An absent attestation is a truthful statement about missing evidence; a default-filled one is a lie a verifier's policy might rely on.
Verifiers SHOULD treat this as one input among several, never as a substitute for the status checks.
#5. Presentation
#5.1 The bundle
{ "vdac": "<compact SD-JWT>", "kbJwt": "<compact JWS>", "sua": "<compact JWS, when step-up fired>" }
Self-contained: the identity root travels inside the VDAC, so the verifier needs no prior relationship with the wallet.
#5.2 Canonical actions and action hashing
For state-changing classes the presentation binds the exact action:
action.hash = base64url(SHA-256(JCS(action_object)))
where JCS is RFC 8785 JSON Canonicalization. Canonical action objects carry exactly these fields and no others:
| Class | Fields |
|---|---|
spend |
type, amount, currency, payee, ref, nonce |
commit-obligate |
type, doc_hash, total_liability, currency, counterparty, term, ref, nonce |
sign-attest |
type, doc_hash, purpose, ref, nonce |
act |
type, verb, resource, ref, nonce |
Two properties make this reproducible across languages, which matters because the wallet that signs and the verifier that checks are usually not written in the same one:
- Every value is a string. No numbers appear in a canonical action, so JCS number serialization — its sharpest edge — never applies.
payeeandcounterpartyare canonical machine identifiers (IBAN, pay address, account id), never display names. A display name is spoofable; the whole point of hashing the action is defeated if the identifier can be re-pointed.
Test vector. For
{ "type": "spend", "amount": "180.00", "currency": "EUR",
"payee": "merchant-demo", "ref": "ord-1", "nonce": "n-1" }
the canonical JSON is exactly
{"amount":"180.00","currency":"EUR","nonce":"n-1","payee":"merchant-demo","ref":"ord-1","type":"spend"}
and the hash is NnYIwGeBcVWkjYSM6tmkqHdDD_CDmnmeNQEM9NZIoYQ. An implementation that
does not reproduce this string byte-for-byte is not conformant.
#5.3 Key-Binding JWT
Signed by the agent key (the VDAC's cnf):
{ "nonce": "<verifier nonce>", "aud": "<verifier origin>", "iat": 1787000000,
"action": { "type": "spend", "alg": "sha-256", "hash": "<action hash>",
"amount": "180.00", "currency": "EUR", "payee": "merchant-demo",
"ref": "ord-1", "nonce": "n-1" } }
action carries the hash and the cleartext fields, so the verifier reconstructs
the action from its own record of the transaction and re-hashes — it never trusts
the agent's claimed amount or payee. action is absent for baseline (read-only,
present-attribute) presentations.
#5.4 Step-Up Assertion
When step-up fires, the presentation MUST include an SUA signed by the principal key:
{ "action_hash": "<must equal the KB-JWT action hash>", "method": "biometric",
"loa": "high", "principal": "did:jwk:…", "aud": "<verifier origin>",
"nonce": "<verifier nonce>", "iat": 1787000000,
"biometric": { "verified": true, "method": "biometric_strong",
"provider": "android_keystore", "at": 1787000000 } }
method is one of oob, biometric, fresh_mint. The verifier receives the
wallet's signed assertion that liveness occurred — never biometric data, which
does not leave the device.
Because the SUA is signed by the principal key and bound to action_hash, an agent
that has been fully compromised — holding its own key and a valid VDAC — still cannot
manufacture one. That is the property step-up exists for.
Default freshness is 300 seconds. Wallets SHOULD expire a step-up request no later than that, so an approval cannot outlive the assertion it produces.
#6. Verification
A conformant verifier performs these steps in order, denying at the first failure. Each returns a machine-readable reason code (§7).
1 — Parse and walk. Parse the leaf VDAC and KB-JWT, then follow parent to the
root. A lone VDAC is a chain of length 1. Malformed input, or a chain deeper than the
implementation limit, denies (vdac.malformed).
2 — Algorithm policy. Every declared alg — each chain link, the KB-JWT, the
embedded identity credential, and the SUA — MUST be in the accepted set
(vdac.alg_not_accepted).
3 — Critical extensions. Any crit member on any link that the verifier has not
registered denies (vdac.unknown_crit).
4 — Identity root. On the root link: the embedded identity credential's issuer
signature MUST verify against resolved issuer keys (vdac.root_sig_invalid); every
disclosure MUST be bound to a digest in the payload (vdac.root_disclosure_unbound);
the credential's cnf key MUST equal the root's iss key (vdac.iss_cnf_mismatch);
the root VDAC signature MUST verify under that principal key
(vdac.vdac_sig_invalid); the assurance level MUST meet the policy floor
(vdac.assurance_too_low); and any attributes the policy requires MUST be disclosed
(vdac.attribute_missing).
Assurance is read from kyc_assurance, assurance, or loa, and the ladder is
low < substantial < high. It is fail-closed: a missing or unrecognized level
never satisfies a floor.
4b — Per-link chain walk. For each non-root link: its signature MUST verify under
its own iss key (vdac.link_sig_invalid); that key MUST equal the parent's cnf
key (vdac.chain_link_broken); mode MUST be projection (vdac.mode_unsupported);
the parent's delegable.depth MUST cover this hop (vdac.parent_not_delegable); the
child's authority MUST attenuate the parent's (vdac.scope_widened); and the child
MUST NOT outlive the parent (vdac.exp_exceeds_parent).
5 — Agent proof-of-possession. The KB-JWT MUST verify under the leaf's cnf key
(vdac.pop_invalid), with matching nonce (vdac.nonce_mismatch) and aud
(vdac.aud_mismatch), and an iat not in the future beyond clock skew
(vdac.kb_not_fresh).
6 — Action binding. For state-changing classes the KB-JWT MUST carry an action
(vdac.action_missing) whose nonce matches the presentation nonce
(vdac.action_nonce_mismatch) and whose hash equals the verifier's own recomputation
from the canonical action (vdac.action_mismatch).
7 — Attenuation. The action MUST fall inside a matching grant on the leaf: class, currency, asset, counterparty allow/deny, jurisdiction, per-transaction ceiling, granted verb, granted attributes. Failures are specific — see §7.
8 — Caps. Cumulative and velocity limits are evaluated against usage accounted
per jti (vdac.cumulative_exceeded, vdac.velocity_exceeded,
vdac.total_liability_exceeded). On accept the verifier commits the delta, keyed on
the action's ref so a retried submission is idempotent.
9 — Status and freshness. The root's principal_root, plus every link's
delegation and agent_key, MUST be unrevoked (vdac.revoked.<level>). A missing
entry denies (vdac.status_missing). For state-changing actions an unavailable
status list denies (vdac.status_unavailable) — fail-closed; for
present-attribute and read-only actions it MAY fail open, a deliberate trade of
strictness for availability where nothing changes state.
10 — Step-up. Step-up fires on the union of triggers: the verifier's route
policy, the class defaults (commit-obligate and sign-attest always), and the
principal's own stepup_over threshold on value classes. When it fires, a valid SUA
MUST be present — signed by the principal key (not an intermediate agent), bound to
this action hash, matching aud and nonce, and fresh. Absent or invalid, the
verdict is review with vdac.stepup_required — not a denial. The transaction is
recoverable: obtain the human's approval and re-submit the same action.
11 — Accept. Return the verdict with the assurance label, the usage delta to
commit, and a chain summary (iss, sub, jti, depth, grantType).
#6.1 Verifier policy inputs
acceptedAlgs, registeredCrit, minAssurance, aud, nonce,
requiredAttributes, jurisdiction, maxStatusAge, stepUpRoutePolicy,
stepUpMaxAgeSec (default 300), clockSkewSec (default 60).
Policy belongs to the verifier. This profile defines what can be checked; it never dictates thresholds.
#7. Reason codes
Verdicts are pass, review, or block, with machine-readable reasons. RPs SHOULD
branch on these codes rather than on message text.
| Code | Meaning |
|---|---|
vdac.malformed |
Unparseable, wrong shape, or chain too deep. |
vdac.alg_not_accepted |
A declared algorithm is outside policy. |
vdac.unknown_crit |
Unregistered critical extension. |
vdac.issuer_unresolved |
Identity-credential issuer keys could not be resolved. |
vdac.root_sig_invalid |
Identity credential signature invalid. |
vdac.root_disclosure_unbound |
A disclosure is not bound to a payload digest. |
vdac.iss_not_did_jwk |
An iss is not a resolvable did:jwk. |
vdac.iss_cnf_mismatch |
Identity cnf ≠ root iss — the chain is not rooted in this identity. |
vdac.vdac_sig_invalid |
Root VDAC signature invalid under the principal key. |
vdac.assurance_too_low |
Assurance below the policy floor (fail-closed). |
vdac.attribute_missing |
A policy-required attribute was not disclosed. |
vdac.link_sig_invalid |
A chain link's own signature is invalid. |
vdac.chain_link_broken |
A link is not signed by its parent's cnf holder. |
vdac.mode_unsupported |
Non-projection mode. |
vdac.parent_not_delegable |
Parent forbids this depth of re-delegation. |
vdac.scope_widened |
A child claims authority its parent lacks (I1). |
vdac.exp_exceeds_parent |
A child outlives its parent. |
vdac.pop_invalid |
KB-JWT does not verify under cnf. |
vdac.nonce_mismatch / vdac.aud_mismatch |
Replay or wrong audience. |
vdac.kb_not_fresh |
KB-JWT iat outside the accepted window. |
vdac.action_missing |
State-changing action with no bound action. |
vdac.action_mismatch |
Action hash ≠ the verifier's recomputation. |
vdac.action_nonce_mismatch |
Action nonce ≠ presentation nonce. |
vdac.no_matching_grant |
No grant of the required class. |
vdac.currency_mismatch / vdac.asset_mismatch |
Wrong denomination. |
vdac.counterparty_not_allowed |
Payee outside allow/deny. |
vdac.jurisdiction_not_allowed |
Outside granted jurisdictions. |
vdac.verb_not_granted |
act verb not granted. |
vdac.attribute_not_granted |
Attribute outside the present-attribute grant. |
vdac.per_tx_exceeded |
Above the per-transaction ceiling. |
vdac.cumulative_exceeded |
Above the cumulative ceiling for this jti. |
vdac.velocity_exceeded |
Too many accepted actions in the window. |
vdac.total_liability_exceeded |
Above the obligation ceiling. |
vdac.status_missing |
A required status entry is absent. |
vdac.status_unavailable |
Status list unreachable (denies for state-changing). |
vdac.revoked.principal_root |
The person's root is revoked — every delegation is dead. |
vdac.revoked.delegation |
This delegation (and its subtree) is revoked. |
vdac.revoked.agent_key |
This agent key is revoked. |
vdac.stepup_required |
review — a human must approve this action. |
#8. Security considerations
What a stolen VDAC gets an attacker: nothing. The credential is public-ish data. Presenting it requires signing a fresh KB-JWT under the agent key, which is non-exportable.
What a fully compromised agent gets an attacker: the granted scope, and only until revocation. This is the honest bound and the reason scoping and caps are first-class. An attacker holding the agent key can act within the grant — up to the per-transaction ceiling, up to the cumulative ceiling, only to allowed counterparties, only until any of three status bits flips. They cannot exceed the caps, re-point the payee, widen the scope, or produce an SUA.
Action re-pointing is prevented by hashing canonical machine identifiers and by the verifier reconstructing the action from its own order — never from the agent's claim. A verifier that hashes the agent's asserted amount instead of its own has implemented this profile incorrectly.
Replay is prevented by verifier-chosen single-use nonces bound into both the
KB-JWT and the action object, plus iat freshness on both KB-JWT and SUA.
Delegation-chain abuse is prevented by per-link verification (§6 step 4b) — every link is signed by its delegator, attenuates, and expires no later than its parent.
Revocation latency is bounded by freshness.max_status_age_default and by the
verifier's maxStatusAge. Status is checked at every verification; a cached list
older than the bound is not usable.
Known limits, stated plainly. Agent-key attestation is only as good as the evidence behind it, and this profile explicitly permits its absence (§4.7) rather than encouraging a fabricated claim. Status availability is a liveness dependency for state-changing actions — the fail-closed choice is deliberate, and it means a status outage stops transactions. Sub-delegation is single-principal projection only; nothing here addresses delegation across principals.
#9. Privacy considerations
Delegation discloses no identity attributes. The embedded identity credential
carries no disclosures by default — the verifier learns a pseudonymous did:jwk, the
assurance level, and nothing else. Where a grant needs an attribute, exactly that
attribute is disclosed and no more.
Accountability without identification (I6). The chain is principal-signed, so an action is attributable and non-repudiable, while the verifier holds only a pseudonym. Identifying the person behind it is an out-of-band legal process against the identity issuer, not something a verifier can do from a presentation.
Status lists are herd-privacy structures. Large shared lists, never per-principal — a list whose size or URL identifies one person defeats the purpose.
Audit logs. Verifiers SHOULD record the verdict, reason codes, jti, and
pseudonymous iss/sub — and SHOULD NOT record identity attributes.
Multi-show linkability is a real limitation of v1. An SD-JWT VC presented twice carries the same issuer signature, so two colluding verifiers can correlate. Batch issuance of single-use copies is the intended mitigation and is not yet part of this profile (§12).
linkability_path is always "none" in v1. The field is reserved for a future
opt-in escrow construction. No implementation may treat it as an unmasking hook
today.
#10. Interoperability
VDAC is a profile over standards, not a parallel stack:
- SD-JWT VC for the credential envelope and the embedded identity credential.
- OpenID4VP for the presentation transport.
- IETF Token Status List for revocation.
- RFC 8785 (JCS) for canonical action hashing.
- RFC 9421 where the transport itself is signed (HTTP Message Signatures) — the usual pattern for an agent calling an API.
AP2 Intent Mandate. A VDAC spend grant is a superset of a Google AP2 Intent
Mandate and exports to one losslessly: merchants ↔ counterparty allow-list, intent
expiry ↔ exp, cart-confirmation ↔ step-up reachability. Descriptive AP2 fields with
no authority meaning are carried verbatim. The generated field map ships with the
reference implementation.
#11. Conformance
An implementation is conformant if it:
- produces or accepts payloads per §4;
- reproduces the §5.2 canonical hash byte-for-byte, including the published test vector;
- performs §6 in order, denying at the first failure;
- emits §7 reason codes;
- is fail-closed on assurance, unknown
crit, unknown authoritytype, and status availability for state-changing actions.
The reference implementation is @otcp/vdac — pure TypeScript, no service
dependencies, shared by the verifier and the demo harness, and used as the executable
reference for the native wallet minter.
#12. Open issues for v0.2
Named rather than papered over:
- Batch issuance / unlinkability (§9). The mechanism is decided — N single-use copies rotated per presentation — but is not yet profiled here.
- Agent-key attestation format. §4.7 defines the field; it does not yet define how a remote agent conveys an attestation chain, or how a verifier validates one.
- Mediated mode.
modeadmits"mediated"; onlyprojectionis specified. - Cross-principal delegation. Out of scope in v1; the chain walk assumes one principal.
- Post-quantum agility. The
alg/crit/vct-version discipline is in place; no PQC suite is profiled. - Decentralized revocation. Status lists depend on a reachable service. A principal-controlled kill switch that works when the service is down is wanted.
categoriescounterparty scoping. v1 scopes on canonical identifiers only.
#13. Change log
| Version | Date | Notes |
|---|---|---|
| 0.1 | 2026-08-23 | First public draft. Payload, five authority classes, canonical action hashing with test vector, single-hop projection sub-delegation, eleven-step verification, reason-code vocabulary. |
Feedback is wanted, particularly on §6 and §7 — the surfaces another implementation has to match. The reference implementation is the tie-breaker for any disagreement with this text.