DelegusDocsv0.3 rc1

Docs/Specification v0.3-rc1

Protocol

Specification v0.3-rc1

Release candidate 1, not frozen. The commitment construction is awaiting an external cryptography review, and v0.2 stays the normative specification until v0.3 is frozen and every v0.3 conformance vector passes. Rendered from the Markdown source.

Status: v0.3-rc1, release candidate, published, NOT frozen. v0.3 ships as this release candidate; v0.2 stays the normative text until v0.3 is frozen. docs/delegus-spec-v0.2-final.md (errata 1–3) remains the sole normative source until this draft is frozen. Freezing requires an external cryptographic review of the dependency commitment (§R) and its byte framing, and every v0.3 conformance vector passing.

What v0.3 changes (summary):

  1. The receipt pins the matched authority by commitment; the plaintext Capability is returned to the relying party in the response and is not part of the signed receipt (receipt v2, §6).
  2. Dependencies (chain of reliance): an Action may declare relies_on[] commitments to prior receipts, each with a mandatory mode (§4.3, §R); each ALLOW receipt carries a pairwise transaction handle as an output (§R.3); the relying party states which dependencies it requires (§5.1); dependency checks D1–D7 run after P21 (§5.2–§5.4). The receipt pins the declared entries and the requirements by hash (§6.3).
  3. Seller-side action types: commerce:quote, commerce:accept, commerce:fulfil (§3.3).
  4. Consumable grants: the budget constraint, check P21 (headroom) and trust check T7 (the atomic spend). The spend is the very last step of an evaluation and happens only when every other check is true, so a DENY never consumes budget (§5.2–§5.4).
  5. Receipt v2, the profile document delegus-base-v3 with trust policy delegus-trust-v3, reason codes for the above, and §6.3 rows for every new field.
  6. Privacy principles as normative text; receipts private by default; an entitled disclosure package for reproducing dependency checks (§0.1, §8, §R.5).

Opt-in. v0.3 applies to a relying party registered on the profile delegus-base-v3. v0.2 (delegus-base-v1, delegus-trust-v1, delegus-receipt-v1) remains the default and is unchanged; every v0.2 conformance vector passes unchanged.

Out of v0.3 core: accepting foreign authority formats (import, mapping), a general disclosure capability, auditor roles, disclosure levels, cross-issuer receipts.


(v0.2 text follows, amended in place. Unchanged sections are v0.2 verbatim.)#

v0.2 status (for reference): Frozen. Errata applied — 1: revocation-read / replay-write ordering (§5.3, §5.4, §7) and acceptance test (§15); 2: historical verification clock (§5.4, §6.4); 3: unusable Ed25519 public keys — non-canonical or small-order keys are rejected (§2.1, §2.2, §5.2 P1/P2/P8, §13.1). This is the sole normative implementation source for v0.2. It consolidates the v0.2 draft, the Freeze Resolutions, the Threat Model, and the Final Freeze Recommendations. Earlier drafts are decision history only; engineers and conformance tests MUST NOT depend on them. Changes after this point require v0.3 or an explicit numbered erratum.

Acceptance criterion: An engineer at Company B who has never seen Company A's agent can integrate Delegus in an afternoon and safely accept or reject its action with one API call.

Normative language: MUST / SHOULD / MAY per RFC 2119.

Licence (non-normative): This specification text is licensed under the Creative Commons Attribution 4.0 International licence (CC BY 4.0, https://creativecommons.org/licenses/by/4.0/). Attribution: "Delegus Specification v0.2, © Delegus (delegus.ai)". The reference engine (@delegus/core, including the conformance vectors) and the SDK (@delegus/sdk) are licensed separately under Apache-2.0. This statement is metadata: it changes no normative text, no profile document and no trust-configuration descriptor.


0. Constitution#

OPEN (published, versioned, reproducible by anyone): Grant semantics · Proof format · protocol verification rules (§5.2) · Receipt format · Outcome Attestation format · portable identity model.

DELEGUS (the service): issuer trust · live verification (replay, revocation freshness) · evidence preservation · decision signing · adapter network · outcome graph · risk models · economic assurance.

Design constraints:

  1. An ordinary backend engineer can implement the protocol layer from this document with a standard JOSE library.
  2. Given the same inputs and the same evidence snapshot, the protocol result is identical everywhere. The Delegus decision is deterministic given inputs + snapshot + trust configuration.
  3. Fail closed. Anything unrecognized, missing, ambiguous, or unavailable is DENY.
  4. No per-transaction state except replay protection and the receipt/outcome ledger.
  5. Someone must be able to verify a Delegus artifact without calling Delegus.

0.1 Privacy principles (normative, v0.3)#

  1. Verifiability does not imply discoverability. Being able to verify a receipt, a dependency or a transaction handle never grants the ability to find, list or retrieve other receipts or evidence.
  2. Evidence should reveal no more than is necessary to verify the claim being made. A receipt carries hashes and commitments where the plaintext is not needed to verify it.
  3. Receipt authenticity is public. Receipt reasoning is entitled. Anyone holding a receipt can check it is authentic and unmodified (§6.4 public verification). Re-deriving why it was decided, including upstream authority, requires the entitled disclosure package (§R.5, §6.4 entitled reproduction, §8).
  4. A receipt may disclose that its decision depended on prior authority decisions. It does not, by itself, confer access to the underlying receipts or evidence.
  5. Transaction handles provide correlation, not discovery. Knowing a transaction handle or a dependency fingerprint never lets a party enumerate or retrieve the receipts it relates to.

Not claimed: Delegus issues and evaluates every receipt in a transaction, so Delegus itself can correlate them. Opaque handles and commitments limit correlation by outsiders, not by Delegus.


1. Terminology#

TermMeaning
PrincipalThe organization on whose behalf an agent acts. Identified by a DID. Verified by domain control in v0.
AgentSoftware holding an Ed25519 private key. Identified by did:key.
Relying Party (RP)The party receiving an action and calling /verify. Registered; identified by a DID.
GrantA Principal's signed statement of what an Agent may do, for whom, until when.
ProofA short-lived Agent signature binding its key to one request, one RP, one Grant.
ActionThe RP's description of what the Agent attempts, in the constraint vocabulary.
DecisionALLOW or DENY with a machine-readable reason.
ReceiptThe Decision plus its evidence snapshot, signed by Delegus.
Outcome AttestationAn append-only event reported after a decision.
Dependency (v0.3)A prior ALLOW receipt that an Action declares it rests on, in relies_on, with a mode: PRIOR_DECISION (the prior decision was genuine and ALLOW) or CURRENT_AUTHORITY (and its authority is still in force now).
Fingerprint (v0.3)The commitment that names a dependency without revealing it: a hash over the prior receipt under a secret opening (§R.1).
Opening (v0.3)The 32-byte secret, returned only to the relying party of an ALLOW receipt, that makes its fingerprint (§R.2).
Requirement (v0.3)A relying party's statement, in requires, of which dependencies an Action must declare (§5.1, D1).
Transaction handle (v0.3)An opaque, pairwise identifier on an ALLOW receipt that lets its relying party correlate its own receipts in one transaction; an output only (§R.3).
Disclosure package (v0.3)The material that lets the receipt's principal or relying party reproduce its dependency checks offline (§R.5).
Budget (v0.3)A consumable limit on a Capability (total, per period, uses), checked by P21 and spent by T7.

2. Canonical domain and identifiers#

2.0 Delegus domain as cryptographic infrastructure#

The canonical domain is delegus.ai. No persistent production artifact (Grant, Receipt, status-list credential, managed identity) may be issued until Delegus controls this domain. Frozen identifiers:

text
did:web:delegus.ai                      Delegus root DID (receipt and status-list signing keys)
did:web:delegus.ai:org:<slug>           managed Principal identities
did:web:delegus.ai:rp:<slug>            managed Relying Party identities
https://delegus.ai/ns/grant/v1          Grant JSON-LD context
https://status.delegus.ai/...           published status-list credentials
did:web:delegus.ai#receipt-<n>          receipt signing key identifiers

2.1 Principal — managed by default, open by design#

2.2 Agent#

2.3 Relying Party#


3. The Grant#

3.0 Semantic model and serialization profiles#

The Grant is defined by the fields and semantics in §3.2–3.3, independent of encoding. A serialization profile maps those fields to bytes and MUST NOT add or remove semantics.

ProfileStatus
VC 2.0 + compact JWS (VC-JOSE-COSE)v0 — the only profile the engine accepts
VC 2.0 + SD-JWTreserved
EUDI dc+sd-jwtreserved (eIDAS "powers and mandates to represent legal persons")
COSEreserved

3.1 JOSE header (v0 profile)#

#FieldValueRule
1algEdDSAMUST. No other algorithm accepted.
2typvc+jwtMUST.
3kidPrincipal key DID URL, e.g. did:web:acme.com#key-1MUST.

3.2 Fields#

#FieldTypeRule
4@contextarrayMUST be exactly ["https://www.w3.org/ns/credentials/v2", "https://delegus.ai/ns/grant/v1"].
5typearrayMUST be exactly ["VerifiableCredential", "DelegusGrant"].
6idstringurn:delegus:grant:<ULID>.
7issuerstringPrincipal DID. MUST equal the DID portion of kid.
8validFromRFC 3339 UTCMUST.
9validUntilRFC 3339 UTCMUST. validUntil − validFrom ≤ 90 days.
10credentialSubject.idstringAgent DID.
11credentialSubject.principalstringRoot Principal DID. v0: MUST equal issuer. Reserved for chains.
12credentialSubject.authorityarray of Capability1–16 entries.
13credentialSubject.audiencearray of RP DIDsOPTIONAL. Absent = any registered RP.
14credentialStatusBitstringStatusListEntryMUST. statusPurpose: "revocation", statusListIndex, statusListCredential.
15parentGrantstringRESERVED. MUST be absent in v0 → DENY UNSUPPORTED_FEATURE. Future: child authority ⊆ parent authority.
16–20iss, sub, jti, nbf, expregistered claimsMUST equal issuer, credentialSubject.id, id, validFrom, validUntil.

Encoded size ≤ 8 KB.

3.3 Capability and constraint vocabulary (v0.3)#

json
{ "action": "commerce:purchase", "constraints": { "maxAmount": 50000, "currency": "USD" }, "resources": ["*"] }
actionConstraints (all REQUIRED unless marked OPTIONAL)Meaning of an authorized action
commerce:purchasemaxAmount (integer, minor units), currency (ISO 4217, single); OPTIONAL budget (total, perPeriod, uses)the actor commits its Principal to buy
api:callmethods (non-empty array of uppercase HTTP methods); OPTIONAL budget (uses only)the actor calls an API on its Principal's behalf
commerce:quotemaxAmount, currency; OPTIONAL budget (uses only)the actor offers, for its Principal, to sell at the stated price
commerce:acceptmaxAmount, currency; OPTIONAL budget (uses only)the actor accepts an order for its Principal at the stated price
commerce:fulfilnone; OPTIONAL budget (uses only)the actor commits its Principal to deliver the named resource

total or perPeriod on any other action type, an empty budget, an unknown member, or an ill-typed value makes the Grant malformed (P1). Money budgets on commerce:quote and commerce:accept are an open question (§16, item 12). Under delegus-base-v1, budget is an unknown key (P17).

3.3.1 Budget arithmetic and the consumption snapshot (v0.3)#

Periods. For a perPeriod budget, the period containing now starts at:

All arithmetic is in UTC milliseconds; a period start is written with whole seconds, YYYY-MM-DDTHH:MM:SSZ.

The consumption snapshot is what the service read for the Grant. A service MAY read it lazily, when P21 is reached with a budgeted P20 survivor, and MUST NOT read or keep one for a request that does not reach P21 with a budgeted survivor. It is a JSON object with exactly these members:

json
{ "grant_id": "urn:delegus:grant:…", "as_of": "<RFC 3339 UTC>", "version": 7,
  "capabilities": [ { "key": "sha256:…", "spent_total": 57300, "spent_in_period": 0,
                      "period_start": "2026-09-08T00:00:00Z" | null, "uses_spent": 3 } ] }

key is the ledger key of a Capability, sha256 over UTF-8 JCS(capability) exactly as it appears in the Grant; it is internal to the service and never appears in a receipt (§R.6). All counts are non-negative integers (minor units for the two spent_ members).

Headroom (P21). For a budgeted Capability with snapshot entry e, an action of amount a (0 for types without an amount) and one use, the dimensions are checked in the order total, period, uses, and the first without headroom fails:

The P20 survivors are tried in Grant order: a survivor without budget, or the first with headroom, is chosen; a budgeted survivor with no snapshot, or with no entry for its key, stops the search with SERVICE_UNAVAILABLE (fail closed). If none is chosen, P21 is false with AUTHORITY_EXHAUSTED. The snapshot's hash in the receipt is evidence.consumption.snapshot_hash (§6.3).

Restoring a spend. A cancelled outcome attestation (§8) on an ALLOW receipt restores what that receipt consumed, once; an attestation that corrects a cancelled one applies the spend again. No other outcome type touches consumption. The trust-configuration descriptor records this as consumable_grants.restore_on.

3.4 Example payload#

json
{
  "@context": ["https://www.w3.org/ns/credentials/v2", "https://delegus.ai/ns/grant/v1"],
  "type": ["VerifiableCredential", "DelegusGrant"],
  "id": "urn:delegus:grant:01J9X4QK7M3Y2Z8N6P5R4T3V2W",
  "issuer": "did:web:acme.com",
  "validFrom": "2026-09-09T00:00:00Z",
  "validUntil": "2026-10-01T00:00:00Z",
  "credentialSubject": {
    "id": "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH",
    "principal": "did:web:acme.com",
    "authority": [
      { "action": "commerce:purchase", "constraints": { "maxAmount": 50000, "currency": "USD" } }
    ]
  },
  "credentialStatus": {
    "id": "https://status.delegus.ai/acme.com/1#4127",
    "type": "BitstringStatusListEntry",
    "statusPurpose": "revocation",
    "statusListIndex": "4127",
    "statusListCredential": "https://status.delegus.ai/acme.com/1"
  },
  "iss": "did:web:acme.com",
  "sub": "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH",
  "jti": "urn:delegus:grant:01J9X4QK7M3Y2Z8N6P5R4T3V2W",
  "nbf": 1788912000,
  "exp": 1790812800
}

4. The Proof#

A compact JWS signed by the Agent key, modeled on DPoP (RFC 9449), binding the key to one request, one RP, one Grant.

4.1 Header#

FieldValue
algEdDSA (MUST)
typdelegus-proof+jwt (MUST)
kidAgent DID URL, e.g. did:key:z6Mk...#z6Mk... (MUST)

4.2 Claims#

FieldRule
issAgent DID. MUST equal Grant sub.
audRP DID. MUST equal the DID of the RP calling /verify.
grantMUST equal Grant id.
jtiSecurity nonce. MUST contain ≥ 128 cryptographically random bits from a CSPRNG. SDKs SHOULD generate 16 random bytes encoded as base64url without padding. MUST be 22–64 ASCII characters. Not a sortable identifier.
iatSeconds since epoch. MUST satisfy |now − iat| ≤ window (default 60 s, RP-configurable, max 300 s).
htmHTTP method, uppercase ASCII.
htuTarget URI per §4.3.
action_hashsha256:<64 lowercase hex> of the UTF-8 bytes of JCS(action) (RFC 8785).

4.3 Canonicalization#

Action wire fields (v0.3). An Action has exactly the fields its type lists, plus the two OPTIONAL fields below; any other field is ACTION_MALFORMED (P15).

typeFields
commerce:purchase, commerce:quote, commerce:accepttype, resource (non-empty string), amount (non-negative integer, minor units), currency (ISO 4217)
api:calltype, method (uppercase HTTP method), resource (absolute http(s) URI)
commerce:fulfiltype, resource (non-empty string)

OPTIONAL on every type (v0.3):

relies_on is part of the Action, so it is inside JCS(action) and bound by the actor's Proof through action_hash (P14). Only the actor declares a dependency. A relying party states what it requires (§5.1); it cannot add, remove or change a declaration without failing P14. The order of relies_on entries is significant: the dependency checks run in array order (§5.4).

The Action has no transaction member. A transaction handle is an output of /verify only, never an input (§R.3), so no handle crosses a trust boundary on the way in. An Action carrying transaction, or any other member not listed above, is ACTION_MALFORMED (unknown member, P15).

4.4 Transport and binding profiles#

The Agent sends Delegus-Grant: <JWS> and Delegus-Proof: <JWS> headers to the RP. The RP forwards both plus its own Action object to /verify, constructing the Action from what it actually received. Only the http binding profile exists in v0; any other → PROOF_MALFORMED.


R. Dependency commitments, openings and transaction handles (v0.3)#

This section is normative for delegus-base-v3. It is subject to the external cryptographic review required before freeze (status header).

R.1 The relies_on fingerprint#

A relies_on entry names a prior receipt by a commitment, never by an address. For a receipt R and a 32-byte opening nonce:

text
fingerprint(nonce, R) = SHA-256( u8(len(tag)) ‖ tag ‖ nonce ‖ u64be(len(R)) ‖ R )

tag    = "delegus:relies-on:v1"          20 bytes, ASCII
nonce  = 32 bytes from a CSPRNG           the opening
R      = the receipt's compact JWS, ASCII bytes, exactly as issued (the `receipt` member, §6.4)
u8     = one unsigned byte; u64be = unsigned 64-bit big-endian integer

The value is written sha256:<64 lowercase hex> (§6.3). Every variable-length field is length-prefixed and the nonce has a fixed length, so the encoding is injective: no two distinct (tag, nonce, R) produce the same preimage. The length-prefixed tag separates these digests from every other hash in this specification, whose inputs are bare bytes, and versions the construction (a successor uses a new tag). The 256-bit nonce hides which receipt a fingerprint names from anyone without the opening; binding rests on SHA-256 collision resistance; authenticity is the named receipt's own signature (D3), which is why this is a plain hash and not a MAC.

R.2 Openings#

R.3 Transaction handles#

R.4 The CURRENT_AUTHORITY evidence commitment#

For a CURRENT_AUTHORITY dependency the receipt commits to the evidence D6–D7 used, without disclosing it:

text
evidence_commitment = SHA-256( u8(len(tag)) ‖ tag ‖ nonce_e ‖ u64be(len(E)) ‖ E )

tag     = "delegus:dependency-evidence:v1"
nonce_e = 32 fresh bytes from a CSPRNG, kept by Delegus
E       = UTF-8 of JCS({ "receipt_hash": sha256 of R's JWS ASCII,
                         "status_credential_hash": the dependency Principal's status-list credential as read,
                         "status_list_index": the dependency Grant's index })

Entitled reproduction (the principal or the relying party, §8) receives E and nonce_e; public verification checks only the receipt's signature.

R.5 Reproducing a dependency decision (entitled disclosure package)#

A receipt's authenticity is public: anyone holding it can check its signature (§6.4). Reproducing why its dependency checks came out as they did is entitled (§0.1): it needs material a third party does not have, and Delegus provides it only to an entitled party, the principal or the relying party of the receipt being reproduced (§8).

For a receipt R2 that declared dependencies, the dependency disclosure package contains, per relies_on entry, in declaration order:

Per entry, every party entitled to R2 receives: checks (D2–D7 as evaluated), transaction_equal (D5, Delegus's attestation), receipt_hash (sha256 of the dependency receipt's JWS) and, for CURRENT_AUTHORITY, status_list_index, status_credential and evidence_nonce, which open evidence_commitment without the dependency receipt. The dependency receipt's JWS (receipt) and its opening (opening) are included only when the requesting party is itself entitled to that dependency receipt (its relying party or principal). Declaring a fingerprint, or being entitled to R2, never entitles a party to the dependency receipt. No package is kept for a DENY receipt.

MemberWho receives itLets the holder re-run
checksevery party entitled to R2nothing: Delegus's per-entry attestation of D2–D7
transaction_equalevery party entitled to R2D5, as Delegus's attestation (internal transactions are never disclosed, §R.3)
receipt_hashevery party entitled to R2with the CURRENT_AUTHORITY members, the §R.4 opening of evidence_commitment
CURRENT_AUTHORITY only: status_list_index, status_credential (as read before P6), evidence_nonceevery party entitled to R2D7 against the credential, and evidence_commitment = the §R.4 commitment over JCS({receipt_hash, status_credential_hash, status_list_index})
receipt: the dependency receipt's compact JWSonly a party also entitled to the dependency receiptD3 (signature, body = payload), D4 (decision), D6 (expires_at), and receipt_hash
opening: its 32-byte nonce, base64urlonly a party also entitled to the dependency receiptD2: fingerprint(opening, receipt) equals the declared fingerprint (§R.1)

With the package, now = R2's evaluated_at (§6.4): D7's revocation part and the evidence commitment are reproducible offline by every party entitled to R2; D7's standing and key-state parts are Delegus's attestation, like D5 (the package carries neither). D2–D4 and D6 are reproducible only by a party entitled to both R2 and the dependency receipt; every other party entitled to R2 relies on Delegus's per-entry attestation (checks) for them. D5 is reproducible by no one outside Delegus: it is Delegus's attestation, like the trust block. Without the package, a third party can verify R2's signature and read which entries were declared, by fingerprint and mode, and nothing more: a fingerprint does not reveal which receipt it names (§R.1).

What a CURRENT_AUTHORITY dependency discloses (stated, accepted). Every party entitled to R2 receives, for each CURRENT_AUTHORITY entry, the dependency Principal's status-list credential and the dependency Grant's status_list_index. The credential's id is the Principal's status-list URL (for example https://status.delegus.ai/acme.com/1), so these parties learn who the dependency's Principal is, even when they are not entitled to the dependency receipt itself. With the index they can read that Grant's revocation bit, and because status-list credentials are public by nature (§8), they can keep observing its revocation status afterwards by fetching the current credential. The credential also carries the bits of the Principal's other Grants in the same list, which this index does not identify. A PRIOR_DECISION entry discloses none of this. An actor that does not want its counterparties to learn the dependency's Principal, or to follow its revocation, declares PRIOR_DECISION (§13.2 NP-15).

The same route also returns, as consumption_snapshot, the consumption snapshot a budgeted receipt pins by evidence.consumption.snapshot_hash, which makes its P21 reproducible (§6.4). The package is delivered only through authorized access (§8). It is never published by hash, and holding R2 does not by itself entitle anyone to it.

R.6 Authority and capability-key commitments#

A Capability has little entropy (an action type, an amount, a currency, a few patterns), so a plain hash of it would let anyone holding a receipt confirm a guess. A v2 receipt therefore pins the matched Capability, and the ledger key of a budgeted one, by commitment under the §R.1 framing, each with its own tag and its own fresh 32-byte nonce:

text
authority_commitment       = SHA-256( u8(20) ‖ "delegus:authority:v1"      ‖ nonce_a ‖ u64be(len(C)) ‖ C )
capability_key_commitment  = SHA-256( u8(25) ‖ "delegus:capability-key:v1" ‖ nonce_k ‖ u64be(len(K)) ‖ K )

C = UTF-8 of JCS(matched Capability), exactly as it appears in the Grant
K = ASCII of the ledger key, "sha256:" ‖ 64 lowercase hex (§3.3.1)

5. POST /verify#

5.1 Request#

json
{ "grant": "<JWS>", "proof": "<JWS>", "action": { "type": "commerce:purchase", "resource": "order_92813", "amount": 42700, "currency": "USD" } }

Authentication: RP API key. action.type is required; other fields per action type; unknown fields → ACTION_MALFORMED.

v0.3: dependency requirements. The body MAY carry requires, the relying party's statement of which prior decisions this action must rest on:

json
{ "grant": "<JWS>", "proof": "<JWS>", "action": { "...": "...", "relies_on": [ { "fingerprint": "sha256:…", "mode": "CURRENT_AUTHORITY" } ] },
  "requires": [ { "fingerprint": "sha256:…", "mode": "CURRENT_AUTHORITY" } ] }

Service gates before evaluation (every profile). Delegus refuses a request with HTTP 403, no receipt and the Proof's jti not consumed, when the relying party itself is not fit to receive a decision: its standing is not good (RP_SUSPENDED, set by staff with a reason, audited), or it is customer-hosted and its organization holds no verified claim on its domain (RP_UNVERIFIED, §2: a claim that lapses turns the refusal on until it is re-verified). These are service refusals, not checks: they precede P1 and appear in no receipt. Registering a customer-hosted relying party or Principal likewise needs a verified claim first (HTTP 403 CLAIM_REQUIRED); a pending claim reserves nothing.

5.2 Protocol layer — delegus-base-v1 (OPEN)#

Reproducible by anyone given: Grant, Proof, Action, RP DID, a clock, the issuer's DID document snapshot, and the status-list credential snapshot. Each check yields true | false | "skipped".

#CheckDENY reason
P1Grant parses and conforms to §3 (incl. parentGrant absent)GRANT_MALFORMED / UNSUPPORTED_FEATURE
P2kid names a verification method present in the DID document snapshotISSUER_KEY_NOT_FOUND
P3Grant signature verifiesGRANT_SIGNATURE_INVALID
P4now ≥ validFromGRANT_NOT_YET_VALID
P5now < validUntilGRANT_EXPIRED
P6Status credential signed by Delegus; bit at statusListIndex is 0AUTHORITY_REVOKED
P7audience, if present, contains RPAUDIENCE_MISMATCH
P8Proof parses and conforms to §4 (incl. jti format)PROOF_MALFORMED
P9Proof signature verifies against Agent keyPROOF_SIGNATURE_INVALID
P10Proof iss == Grant subPROOF_AGENT_MISMATCH
P11Proof grant == Grant idPROOF_GRANT_MISMATCH
P12Proof aud == RP DIDPROOF_AUDIENCE_MISMATCH
P13iat within windowPROOF_EXPIRED
P14action_hash == hash of submitted ActionPROOF_ACTION_MISMATCH
P15Action valid for its typeACTION_MALFORMED
P16Some Capability matches action.typeACTION_NOT_AUTHORIZED
P17All constraint keys knownUNKNOWN_CONSTRAINT
P18Currency authorizedCURRENCY_NOT_AUTHORIZED
P19amount ≤ maxAmountAMOUNT_EXCEEDS_AUTHORITY
P20resource matches a patternRESOURCE_NOT_AUTHORIZED

v0.3 (delegus-base-v3). Every check P1–P20 is as above, with P15 applying the v0.3 Action rules (§4.3) and P16–P20 the v0.3 vocabulary (§3.3). v0.3 adds P21 and D1–D7.

#CheckDENY reason
P21Some P20 survivor has budget headroom for the action, or declares no budget; periods are anchored at validFrom in UTC; counts come from the consumption snapshot. A missing snapshot for a budgeted survivor is SERVICE_UNAVAILABLEAUTHORITY_EXHAUSTED

The first P21 survivor in Grant order is the matched authority (authority_commitment, §R.6) and the Capability the spend decrements. §3.3.1 gives the period arithmetic, the headroom order and the snapshot format.

v0.3 dependency checks. Evaluated for each entry of action.relies_on, in array order, after P20; D1 is evaluated once, before the per-entry checks. Absent relies_on and absent requires: D1–D7 are true (vacuous). A dependency always names a receipt Delegus itself issued; receipts from another issuer are out of v0.3 core.

#CheckDENY reason
D1Every requires entry appears in action.relies_on with the same fingerprint and the same modeDEPENDENCY_NOT_DECLARED
D2The fingerprint resolves, inside Delegus, to a receipt Delegus issued (§R: the fingerprint recomputed from that receipt's bytes and its stored opening equals the declared value). A stored record that is malformed is unavailable state, not a mismatch → SERVICE_UNAVAILABLEDEPENDENCY_COMMITMENT_MISMATCH / SERVICE_UNAVAILABLE
D3That receipt's signature verifies against a Delegus receipt key and its body equals its payloadDEPENDENCY_RECEIPT_INVALID
D4Its decision is ALLOWDEPENDENCY_NOT_ALLOW
D5It belongs to the same internal transaction as the first resolved dependency, in Action order (§R.3)DEPENDENCY_TRANSACTION_MISMATCH
D6CURRENT_AUTHORITY only: the dependency's Grant is still within its validity at now (now < expires_at of the dependency receipt)DEPENDENCY_AUTHORITY_EXPIRED
D7CURRENT_AUTHORITY only: the dependency Grant's Principal is in good standing (as T1 requires of the action's own Principal), the key that signed the dependency Grant (its kid) is not marked compromised (as T3), and the dependency Grant's bit is 0 in its Principal's status-list credential (as P6); all read in the bracketed step before P6 and checked in that order. Standing, key state or credential unavailable → SERVICE_UNAVAILABLEDEPENDENCY_ISSUER_NOT_TRUSTED / DEPENDENCY_ISSUER_KEY_COMPROMISED / DEPENDENCY_AUTHORITY_REVOKED

For PRIOR_DECISION, D6 and D7 are "skipped". PRIOR_DECISION states only that the prior decision was genuine, ALLOW and in this transaction; it says nothing about now. CURRENT_AUTHORITY additionally states that the authority it rested on was still in force at this evaluation.

The normative text of this table, published at a stable URL, is the profile document; its SHA-256 is protocol.profile_hash in every receipt. The v0.3 profile document (delegus-base-v3) covers P1–P21 and D1–D7 and names the order in §5.4.

5.3 Trust and service layer (DELEGUS) — delegus-trust-v1#

#CheckDENY reason
T1issuer is a registered PrincipalISSUER_UNKNOWN
T2Principal is domain-verifiedISSUER_UNVERIFIED
T3kid not marked compromisedISSUER_KEY_COMPROMISED
T5Replay: SET replay:{rp_did}:{jti} 1 NX PX {2 × window_ms} returns OK. Executed only after P1–P14 pass, as a separate internal operation from the revocation-version read that precedes P6 (§7). Store unavailable → SERVICE_UNAVAILABLE.PROOF_REPLAYED / SERVICE_UNAVAILABLE

delegus-trust-v1 is exactly T1, T2, T3, T5. Not configurable per RP. ISSUER_NOT_TRUSTED is reserved under delegus-trust-v1 and delegus-trust-v2; delegus-trust-v3 emits it at T1 (below). (T4 and T6 do not exist; identifiers are not reused.)

v0.3 (delegus-trust-v3) is T2, T3, T5, T1 strengthened as below, and T7. (delegus-trust-v2, the policy of the retired delegus-base-v2 profile, is T1 as in v1, T2, T3, T5 and T7.)

#CheckDENY reason
T1issuer is a registered Principal in good standing. Standing is Delegus trust data: staff mark a Principal suspended or disputed on a misuse report, with a reason, audited, and lift it the same way; an issued receipt is never altered. A Principal not in good standing is not trusted: a signed DENY, and the Proof's jti is consumed as for any DENY that reaches T5.ISSUER_UNKNOWN / ISSUER_NOT_TRUSTED
T7The answer of the [consume] step: the atomic decrement of the matched Capability's budget for this action. consumed → true; exhausted (another request took the headroom first) → false; store unavailable → SERVICE_UNAVAILABLE. A Capability without budget makes T7 true without a write.AUTHORITY_EXHAUSTED / SERVICE_UNAVAILABLE

[consume] runs only when every other check in the order is true. Otherwise T7 is "skipped" and nothing is consumed: a DENY never spends budget.

5.4 Canonical evaluation order and reason selection#

text
P1 → T1 → T2 → T3 → P2 → P3 → P4 → P5
   → [resolve every relies_on fingerprint (v0.3);
      read status_ver for this Grant's Principal and for the Principal of every CURRENT_AUTHORITY dependency;
      for every CURRENT_AUTHORITY dependency, read its Principal's standing and its Grant key's state (D7);
      refresh cached status credentials if needed]
   → P6 → P7 → P8 → … → P14
   → [atomic replay SET NX]
   → T5 → P15 → … → P20
   → P21                                                        (v0.3)
   → D1 → (per relies_on entry) D2 → D3 → D4 → D5 → D6 → D7     (v0.3)
   → [consume: atomic budget decrement, only if every check above is true]  (v0.3)
   → T7                                                         (v0.3)

The bracketed steps are internal operations, not checks; they are shown to fix where the revocation-state reads and the replay write occur relative to the checks. Every revocation-state read, including a dependency's, precedes the replay write (erratum 1). Dependency checks run after P21 so the actor's own reasons are reported first. A dependency check that fails after T5 has consumed the Proof's jti, like any check after T5: the actor needs a fresh Proof to try again. The spend is the last step so that no request consumes budget and is then refused.

Deferred writes (v0.3). An implementation MAY evaluate first with the replay write and the spend deferred, then perform them in this order (the replay write, then the spend) and report their answers as T5 and T7, provided the result is identical to evaluating in the order above. This is how a receipt's size is known before anything is written (§6.4).

Decision is DENY if any check is false; reason is the first false in this order. protocol.result is PASS iff all P-checks are true; trust.result is PASS iff all T-checks are true. Both are reported even on DENY; checks whose prerequisites failed are "skipped". now is Delegus server time, recorded in the receipt as evaluated_at; historical re-verification uses that value as now (§6.4).

5.5 Hot-path constraint#

/verify MUST perform no external DID resolution, status-list retrieval, issuer lookup, DNS lookup, or third-party network request. Resolving a dependency fingerprint (v0.3) is a lookup in Delegus-owned storage. Internal low-latency infrastructure (Redis, Delegus-owned storage) MAY be accessed. Latency objective: p50 ≤ 30 ms, p99 ≤ 200 ms.

5.6 Response#

The response body is the Receipt (§6) as JSON, including the receipt JWS.

v0.3. The response to the relying party also carries, beside the receipt and outside its signed payload:

The transaction handle is not among these: it is the receipt's own signed transaction field, present on ALLOW only (§6.4, §R.3). A relying party that verifies through the AuthZEN evaluation route (POST /access/v1/evaluation) receives the same beside-fields under context.delegus, so its receipts open and can be relied on in the same way. None of the fields above are part of the receipt payload; the receipt stays verifiable on its own (§6.4), and a client MUST treat a response whose beside-fields do not match the hashes the receipt pins as invalid.


6. The Decision Receipt#

6.1 Purpose#

A Receipt lets any party later prove: at time T, Agent A acting for Principal B presented Grant C to RP D for Action E; Delegus evaluated it against this identity state, this revocation state, these rules, and this trust configuration, and decided X. It remains provable after keys rotate, DID documents change, and status lists are replaced.

v0.3 (delegus-receipt-v2). The receipt pins the authority it relied on by a commitment (authority_commitment, §R.6) instead of carrying the Capability in plaintext, pins the per-dependency results by hash (relies_on_hash, each dependency named only by its commitment) and the relying party's requirements by hash (requires_hash), and, on ALLOW, carries a pairwise transaction handle. A v2 receipt is bounded in size whatever the Grant contains or how many dependencies it declares, and discloses no Principal-authored text beyond the identifiers needed to verify it. delegus-receipt-v1 receipts keep verifying under v0.2 rules.

6.2 Schema#

json
{
  "receipt_version": "delegus-receipt-v2",
  "receipt_id": "drc_01K4Q7ZP9X2M3N4R5S6T7V8W9Y",
  "decision": "ALLOW",
  "reason": null,
  "evaluated_at": "2026-09-09T14:03:27.412Z",
  "relying_party": "did:web:delegus.ai:rp:seller",
  "principal": { "id": "did:web:acme.com", "verification": "domain" },
  "agent": "did:key:z6Mk...",
  "grant_id": "urn:delegus:grant:01J9X4...",
  "grant_hash": "sha256:...",
  "proof_hash": "sha256:...",
  "request_hash": "sha256:...",
  "authority_commitment": "sha256:...",
  "transaction": "<pairwise handle, ALLOW only, §R.3>",
  "requires_hash": "sha256:...",
  "relies_on_hash": "sha256:...",
  "expires_at": "2026-10-01T00:00:00Z",

  "evidence": {
    "issuer_resolution": {
      "did": "did:web:acme.com",
      "did_document_hash": "sha256:...",
      "resolved_at": "2026-09-09T14:00:11Z"
    },
    "status": {
      "credential_url": "https://status.delegus.ai/acme.com/1",
      "credential_hash": "sha256:...",
      "valid_from": "2026-09-09T14:03:00Z",
      "checked_at": "2026-09-09T14:03:27.410Z",
      "status_list_index": "4127"
    },
    "consumption": {
      "snapshot_hash": "sha256:...", "as_of": "2026-09-09T14:03:27.400Z",
      "capability_key_commitment": "sha256:...", "capability_index": 0, "consumed": "consumed"
    },
    "trust_config_version": "trust_0003",
    "trust_config_hash": "sha256:..."
  },

  "protocol": {
    "result": "PASS",
    "profile": "delegus-base-v3",
    "profile_hash": "sha256:...",
    "checks": { "P1": true, "P2": true, "P20": true, "P21": true, "D1": true, "D2": true, "D7": true }
  },
  "trust": {
    "result": "PASS",
    "policy_version": "delegus-trust-v3",
    "checks": { "T1": true, "T2": true, "T3": true, "T5": true, "T7": true }
  },

  "receipt": "<compact JWS>"
}

evidence.consumption is present when the matched Capability has a budget (or P21 failed on one): snapshot_hash and as_of always (§3.3.1); capability_key_commitment (§R.6) and capability_index for the Capability chosen; exhausted (total | period | uses), the first dimension without headroom, when P21 refused; consumed records the T7 answer when the spend ran.

6.3 Hash encoding#

One encoding everywhere: sha256:<64 lowercase hex chars>. Inputs are the exact bytes Delegus saw:

FieldBytes hashed
grant_hashASCII of the compact JWS string as received
proof_hashASCII of the compact JWS string as received
request_hash (= Proof action_hash when P14 passes)UTF-8 of JCS(action)
evidence.issuer_resolution.did_document_hashraw response body bytes as fetched
evidence.status.credential_hashASCII of the status-list credential compact JWS
evidence.trust_config_hashUTF-8 bytes of the immutable trust-configuration descriptor named by trust_config_version
protocol.profile_hashraw UTF-8 bytes of the published profile document
authority_commitment (v0.3)not a plain hash: the §R.6 commitment over UTF-8 JCS(matched Capability), exactly as the Capability appears in the Grant, tag delegus:authority:v1, opened by authority_opening; absent when no Capability matched
evidence.consumption.capability_key_commitment (v0.3)not a plain hash: the §R.6 commitment over the ASCII ledger key, tag delegus:capability-key:v1, opened by capability_key_opening; present when the spend's Capability is identified
evidence.consumption.snapshot_hash (v0.3)UTF-8 of JCS(consumption snapshot), the §3.3.1 object exactly as read for this evaluation
requires_hash (v0.3)UTF-8 of JCS(requires), the relying party's requirement array exactly as submitted (§5.1); absent when the request had no requires
relies_on_hash (v0.3)UTF-8 of JCS(relies_on entries): the array of per-dependency results returned beside the receipt (§5.6), each { fingerprint, mode, checks, evidence_commitment? }, in Action order; absent when nothing was declared
relies_on[].fingerprint (v0.3, response entry pinned by relies_on_hash)not a plain hash: the §R.1 commitment over the prior receipt's JWS ASCII under its opening, preimage u8(len(tag)) ‖ tag ‖ nonce ‖ u64be(len) ‖ receipt, tag delegus:relies-on:v1
authority_opening, capability_key_opening (v0.3, response only)not hashes: 32-byte nonces, 43-character base64url (§R.2, §R.6)
relies_on[].evidence_commitment (v0.3, response entry pinned by relies_on_hash)not a plain hash: the §R.4 commitment over JCS(dependency evidence), tag delegus:dependency-evidence:v1; present only for CURRENT_AUTHORITY
transaction (v0.3)not a hash: a random 16-byte handle, 22-character base64url (§R.3); an output only, never an input

6.4 Signature and rules#

text
live /verify:                 now = Delegus server clock  (recorded as evaluated_at)
offline receipt verification: now = signed receipt.evaluated_at

7. POST /revoke#

json
{ "grant_id": "urn:delegus:grant:01J9X4..." }

Authenticated as the issuing Principal.

Consistency guarantee (normative): POST /revoke MUST NOT return success until the revocation is durably recorded and every subsequent successful /verify anywhere is guaranteed to evaluate against revocation state at least as new as that write. Once a successful revoke response has been returned, a later verification MUST NOT return ALLOW for that Grant.


8. Evidence preservation (DELEGUS obligation)#

Delegus MUST retain, addressable by hash and retrievable by any party holding a receipt that references them: every DID document snapshot, status-list credential snapshot, profile document, and trust-configuration descriptor referenced by a receipt; the receipt itself; and its outcome ledger. Retention: 7 years from evaluated_at (open question 8). An evidence object is locked from its first write, and a later receipt may reference an object first stored long before; Delegus therefore extends the retention of every evidence object a receipt references to at least 7 years after the end of that receipt's UTC day, and a retention date only ever moves later, never earlier. GET /evidence/{sha256} returns the exact bytes.

v0.3: receipts are private by default. The artifacts that are public by nature (profile documents, trust-configuration descriptors, DID document snapshots, status-list credentials) stay retrievable by hash by anyone. A receipt itself, the consumption snapshot a v2 receipt pins, and the dependency disclosure package (§R.5) are retrieved only through authenticated access by the receipt's relying party or principal (their own key, a key of their organization, or Delegus staff): GET /decisions/{receipt_id} for the receipt, and GET /decisions/{receipt_id}/disclosure for the dependency package and the consumption snapshot the receipt pins. A request by anyone else, for an unknown receipt, or for a receipt with nothing to disclose gets the same 404, so the answer never confirms that a receipt exists (§0.1). GET /evidence/{sha256} does not serve consumption snapshots. Nothing is listed or searchable by fingerprint or transaction handle. Retention is the same 7 years.


9. Outcome Attestations#

9.1 Principle#

Outcomes are events, not states. Nothing is overwritten. Events record what the reporter observed, never a judgment about the agent; judgments arrive only as dispute_resolved.resolution.

9.2 Endpoint#

POST /decisions/{receipt_id}/outcomes — authenticated as the RP named in the receipt. Append-only.

json
{ "type": "dispute_opened", "occurred_at": "2026-09-14T09:12:00Z", "evidence_ref": "https://seller.example/disputes/8812", "note": "buyer claims goods not ordered" }

Delegus adds attestation_id (oat_<ULID>), reporter (RP DID), recorded_at. Corrections are new events with correction_of: <attestation_id>. No edits, no deletions. Ordering is by recorded_at.

9.3 v0 vocabulary#

fulfilled · cancelled · dispute_opened · dispute_resolved (resolution: agent_at_fault | agent_not_at_fault | indeterminate) · chargeback_received · chargeback_reversed · loss_reported (loss_amount minor units and currency required). Unknown type → 400. loss_amount is per event; Delegus never sums or nets.

9.4 Reporters and signatures#

v0 reporter: the RP named in the receipt only. reporter_signature (JWS over JCS of the event by the RP key) is OPTIONAL in v0 and REQUIRED from the day any assurance product reads the ledger. Reserved reporter roles (Principal, adjudicator) are not accepted in v0.


10. Data provenance and reuse rights#

Every decision and outcome record carries tenant (RP), reporter, principal, and a reuse_rights tag set at contract time. v0 defaults: used for the tenant's own audit and retrieval; for service operation (replay, abuse prevention); and for Delegus-internal, aggregate, non-identifying risk analysis. No cross-tenant disclosure of any Principal's or Agent's history and no per-entity scoring exposed to any party. Any broader reuse requires an explicit rights change recorded against the tenant.


11. Key lifecycle#


12. Out of scope for v0#

Delegation chains and attenuation (field reserved) · RP-side policy · non-HTTP binding profiles · non-JWS Grant profiles · external identity adapters · agent key rotation in place · un-revoke · any bureau, score, or cross-tenant history product · any guarantee, warranty, or economic assurance.

Out of scope for v0.3: dependency requirements by kind rather than exact fingerprint · dependencies on receipts from another issuer · accepting foreign authority formats · a general disclosure capability, auditor roles, disclosure levels · money budgets (total, perPeriod) on action types other than commerce:purchase.


13. Threat Model (normative scope of protection)#

13.1 v0 explicitly protects against#

IDThreatDelivered by
TM-01Forged GrantP2, P3
TM-02Modified GrantP3
TM-03Stolen Grant used without the Agent keyP9, P10
TM-04Proof replay at the same RPP13, T5
TM-05Proof forwarded to or replayed at a different RPP12
TM-06Action tampering between Agent and DelegusP14
TM-07Expired or not-yet-valid authorityP4, P5
TM-08Revoked authorityP6 + §7 guarantee
TM-09Issuer key compromise, after Delegus is notifiedT3
TM-10Stale identity evidence going unrecordedReceipt pins did_document_hash, resolved_at; refresh ≤ 5 min
TM-11Unknown or unverified issuer; (v0.3) issuer not in good standingT1, T2 (delegus-trust-v3: T1 also requires good standing; a Principal staff marked suspended or disputed gets a signed DENY ISSUER_NOT_TRUSTED)
TM-12Status-list substitution or forgeryP6 (Delegus-signed); receipt pins credential_hash
TM-13Grant used at an RP the Principal excludedP7
TM-14Evidence altered after the factContent-addressed store referenced by hash from signed receipts
TM-15(v0.3) Relying on a prior decision that was forged, altered or never issuedD2 (commitment opens to a receipt Delegus issued), D3 (signature)
TM-16(v0.3) Relying on a prior DENY as if it were an ALLOWD4
TM-17(v0.3) Splicing dependencies from unrelated transactions into one actionD5
TM-18(v0.3) Relying on authority that expired, was revoked, whose Principal was suspended, or whose signing key was compromised after the prior decisionD6, D7 (CURRENT_AUTHORITY; standing, key state and status read before the replay write)
TM-19(v0.3) An actor omitting a dependency the relying party requires, or weakening its modeD1; requires_hash records what was required
TM-20(v0.3) An actor inserting or altering declared dependencies after signingP14 (relies_on is inside the signed action_hash)
TM-21(v0.3) Spending beyond a budget, including concurrent requests racing for the last headroomP21, T7 (atomic decrement)
TM-22(v0.3) Budget spent by a request that is then refused[consume] runs last, only when every other check is true
TM-23(v0.3) Discovering or enumerating receipts from a fingerprint or transaction handleCommitments (§R.1), pairwise output-only handles (§R.3), no lookup by either, uniform 404 (§8)
TM-24(v0.3) Learning the plaintext authority or dependency evidence from a receipt alonePinned by hash and commitment; plaintext only beside the receipt and in the entitled package

13.2 v0 explicitly does not protect against#

IDThreatNote
NP-01A malicious Principal intentionally authorizing malicious activityDelegus attests authority, not intent
NP-02A compromised active Principal key before Delegus learns of itBounded by Grant lifetime and key count
NP-03A compromised Agent private keyBounded by Grant scope and expiry; KMS/HSM signer recommended
NP-04Business fraud unrelated to authorityTransaction assurance, not verification assurance (§14)
NP-05Semantic ambiguity in poorly defined actionsMitigated only by the closed constraint vocabulary; RPs own the mapping
NP-06False or mistaken Outcome AttestationsReporter-tagged, append-only; not adjudicated in v0
NP-07A domain-verified Principal impersonating another organization via a lookalike domainDomain control ≠ organizational identity; roadmap: KYB
NP-08An RP misreporting the action it receivedThe receipt records the RP's own submission
NP-09Compromise of Delegus's own signing keysTrust root for the trust block; HSM custody; open-protocol reproducibility
NP-10Compromise of DNS or the web PKIShared with TLS; out of scope
NP-11(v0.3) Correlation by Delegus itselfDelegus issues every receipt in a transaction; handles and commitments limit outsiders only (§0.1)
NP-12(v0.3) An entitled party disclosing what it holdsA relying party or principal may share a receipt, its opening or its package; Delegus does not control that
NP-13(v0.3) Relying on PRIOR_DECISION as a statement about the presentBy design it says nothing about now; use CURRENT_AUTHORITY
NP-14A low-entropy jti chosen by a faulty AgentThe format is checked (P8); entropy cannot be checked per value. Obligation on the Agent
NP-15(v0.3) Parties to a CURRENT_AUTHORITY dependency learning the dependency's Principal and observing its revocation statusAccepted by design: D7 needs the status-list credential and index, which name the Principal and let the holder re-read the bit later (§R.5). PRIOR_DECISION discloses neither

13.3 Relationship to future assurance#

Any verification guarantee MUST be expressed as an explicit subset of §13.1. Nothing in §13.2 is ever covered by verification assurance; NP-04 is the boundary with transaction assurance, a different product.


14. Non-normative: assurance progression#

text
SIGNED       — every decision carries a Delegus signature                       (v0)
RECORDED     — every decision has a pinned evidence snapshot + outcome ledger    (v0)
GUARANTEED   — verification assurance over a stated subset of §13.1             (earned)
RISK-SCORED  — counterparty / action risk from decision + outcome data          (earned)
GUARANTEED   — economic assurance for covered conditions, direct or with a regulated partner (earned)

15. Implementation contract#

Build order:

  1. packages/core — pure Grant/Proof parsing, canonicalization, P-checks, receipt assembly and signing. No I/O; snapshots and clock injected.
  2. Conformance suite — ALLOW plus one deterministic vector for every DENY reason, including htu normalization, JCS, jti format, replay, revocation, and malformed inputs. The engine cannot merge until every vector passes.
  3. apps/api — Principal registry, domain verification, status lists, replay protection, evidence preservation, receipt persistence, Outcome Attestations.
  4. packages/sdk + CLI — delegus.org.create(), agent.create(), grant.create(), agent.sign(), delegus.verify(), outcome reporting.
  5. apps/demo — buyer/seller: authorized 42700 → ALLOW; revoke Grant; same action with a fresh Proof → AUTHORITY_REVOKED. (A fresh Proof isolates revocation from replay; the replay path is covered separately by the conformance suite.)

15.1 v0.3 conformance vectors#

A v0.3 implementation claims conformance only when every v0.3 vector passes and every v0.2 vector still passes unchanged under the v0.2 profiles. The v0.2 vectors are frozen: v0.3 adds vectors, it never edits one. The v0.3 set is published as a new version of the conformance package with its own vector-set hash, and it contains at least:

  1. Seller-side actions. An ALLOW for each of commerce:quote, commerce:accept and commerce:fulfil, and a DENY for each of P18 and P19 on commerce:quote/commerce:accept and P20 on commerce:fulfil. A v0.3 action type in a Grant evaluated under a v0.2 profile gives P1 GRANT_MALFORMED.
  2. Action fields (P15). A relies_on entry without mode, with an unknown mode, or with an extra member; an empty relies_on; more than 8 entries; a repeated fingerprint; a transaction member (handles are output only, §R.3). Each is ACTION_MALFORMED.
  3. Declaration versus requirement. The relying party requires a dependency the actor did not declare (D1); requires a different mode for a declared fingerprint (D1); inserts relies_on into the submitted Action after the actor signed it (P14).
  4. Each dependency check, one vector per reason: a fingerprint that resolves to no issued receipt, including the right receipt under the wrong opening (D2); a dependency receipt whose signature fails (D3); a DENY dependency (D4); two dependencies from different internal transactions (D5); an expired dependency grant (D6); a revoked dependency grant, a dependency grant whose Principal is later suspended, and one whose signing key is later marked compromised (D7).
  5. The worked example. R1 ALLOW; its grant revoked; R2 declaring R1: PRIOR_DECISION → ALLOW with D6–D7 "skipped"; CURRENT_AUTHORITY → DENY DEPENDENCY_AUTHORITY_REVOKED; a control without the revoke → ALLOW.
  6. Ordering. A request failing both P19 and a dependency check reports P19; a dependency failure after T5 has consumed the jti (a second submission of the same Proof is PROOF_REPLAYED), as a pair of vectors that differ only in the recorded jti. Several entries. Each Dn is false if any entry is false, "skipped" never counts as false, and reason is the first false Dn in §5.4 order, not the first failing entry: an entry failing D7 before an entry failing D2 reports D2; a later entry failing D4 leaves the earlier entry's results unchanged; PRIOR_DECISION and CURRENT_AUTHORITY entries mixed give ALLOW. Every vector with dependencies carries the per-entry results, and relies_on_hash in its receipt equals sha256(JCS(those results)). Budgets. ALLOW with one spend; P21 exhausted (nothing spent); the last headroom lost at T7; the spend unavailable (SERVICE_UNAVAILABLE); the snapshot missing; no spend on a DENY at P6, at T5 or at a dependency check. A DENY never calls the spend except when T7 itself decides it.
  7. Framing (§R). Fixed sets, like the v0.2 htu/JCS/jti sets: known-answer fingerprints, evidence commitments, authority commitments and capability-key commitments over published inputs (§R.6: each beside the plain hash it replaces); strict opening and handle decoding; and the domain separation among the four tags.
  8. Size. A receipt with the maximum 8 relies_on entries stays within the signer's input limit (§6.4).

Every vector's bytes are generated by the reference implementation, not written by hand; a vector whose engine result contradicts its stated intent is refused, never written. Each carries the committed receipt, which verifies against the test receipt key and re-runs byte-identically from the vector's JSON alone. The v0.3 set ships in the conformance package beside the frozen v0.2 set, with its own runner (packages/conformance).

The afternoon integration (relying party, complete):

ts
import { Delegus } from "@delegus/sdk";
const delegus = new Delegus({ apiKey: process.env.DELEGUS_API_KEY, publicBaseUrl: "https://seller.example" });

app.post("/orders", async (req, res) => {
  const d = await delegus.verify({
    grant: req.header("Delegus-Grant"),
    proof: req.header("Delegus-Proof"),
    action: { type: "commerce:purchase", resource: req.body.orderId, amount: req.body.amountMinor, currency: req.body.currency },
  });
  if (d.decision !== "ALLOW") return res.status(403).json({ reason: d.reason });
  // proceed; persist d.receipt with the order
});

16. Remaining open questions (non-blocking)#

  1. Minor units vs. decimals — spec: minor units.
  2. Proof window 60 s / 300 s.
  3. Replay scope per-RP.
  4. Grant max lifetime 90 days.
  5. Grant by reference as an alternative to by value.
  6. loss_amount per event (spec) — confirm with first assurance design.
  7. When reporter signatures become REQUIRED.
  8. Evidence retention period and storage model.
  9. decision vs. receipt as the primary API noun.
  10. (v0.3, erratum candidate) "Integers only, no floats" is about the value: a number is a safe integer whatever its JSON literal form. For Actions, JCS makes 5e4 and 50000 identical; a Grant is hashed as its exact signed bytes, so its literal form is pinned anyway.
  11. (v0.3, erratum candidate) jti is 22–64 characters of the base64url alphabet, as the reference engine enforces; the v0.2 text says "22–64 ASCII". Clarify the text rather than loosen the engine.
  12. (v0.3) Money budgets on commerce:quote and commerce:accept.

v0.2 is frozen.