# Delegus Specification v0.3 — RELEASE CANDIDATE 1 (not frozen)

**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

| Term | Meaning |
|---|---|
| **Principal** | The organization on whose behalf an agent acts. Identified by a DID. Verified by domain control in v0. |
| **Agent** | Software 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. |
| **Grant** | A Principal's signed statement of what an Agent may do, for whom, until when. |
| **Proof** | A short-lived Agent signature binding its key to one request, one RP, one Grant. |
| **Action** | The RP's description of what the Agent attempts, in the constraint vocabulary. |
| **Decision** | ALLOW or DENY with a machine-readable reason. |
| **Receipt** | The Decision plus its evidence snapshot, signed by Delegus. |
| **Outcome Attestation** | An 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:

```
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
```

- Development and test environments MUST use explicitly non-production identifiers (e.g. `did:web:dev.delegus.ai`) that are visually and cryptographically distinct from production. Test signing keys MUST never appear in the production DID document.
- Production credentials MUST NOT be issued against a temporary domain with intent to migrate. Changing the canonical domain after launch is a protocol migration, not a branding change.
- `https://delegus.ai/.well-known/did.json` is production infrastructure with the same availability and TLS discipline as `/verify`. Receipt keys are rotated with overlapping validity and are never removed, only retired.

### 2.1 Principal — managed by default, open by design
- DID method in v0: `did:web`. Two sources, equal in the protocol:
  - **Managed:** `did:web:delegus.ai:org:<slug>`, created by `delegus.org.create()`.
  - **Customer-hosted:** `did:web:acme.com`, accepted at onboarding.
- DID documents are resolved at onboarding and refreshed asynchronously at most every 5 minutes. The engine evaluates against the cached snapshot; the receipt records that snapshot's hash.
- Migration from managed to customer-hosted is a new DID; existing Grants remain valid to `validUntil`.
- Key type Ed25519, verification method `Multikey`, max 5 keys. A key whose encoding is non-canonical (RFC 8032 §5.1.3: y ≥ p, or x = 0 with the sign bit set) or which is one of the eight points of small order MUST be rejected and is treated as absent (erratum 3). Key states `active | retired | compromised` are Delegus trust data, not DID-document data.
- **Domain verification:** DNS TXT at `_delegus.<domain>` or `https://<domain>/.well-known/delegus.json` containing a Delegus-issued token. The file counts only when the exact host serves it: a redirect to any other host, `www.` included, proves nothing. Re-verified every 24 h. Failure sets the Principal `unverified`.
- Reserved for adapters (not v0): Entra, Google, SPIFFE, enterprise PKI, other VC issuers. Adapters MUST materialize their verification state before it reaches the hot path.

### 2.2 Agent
- `did:key`, Ed25519 (`did:key:z6Mk...`). The key is the identifier; no resolution. A `did:key` whose key is non-canonical or of small order is malformed (erratum 3).
- Generated locally by the SDK. `POST /agents` registers the public key for inventory and the agent-level kill switch (`POST /agents/{did}/disable` revokes every Grant naming that Agent). Registration is **not** consulted by `/verify`; the Grant's signature is the binding.
- SDK signer interface MUST be pluggable: in-memory (dev default), KMS, HSM/TPM.

### 2.3 Relying Party
- Registered at onboarding; `did:web:delegus.ai:rp:<slug>` or customer-hosted `did:web`.
- Authenticates to `/verify` with an API key that resolves to the RP DID — the audience for Proofs and the reporter identity for Outcome Attestations.

---

## 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.

| Profile | Status |
|---|---|
| **VC 2.0 + compact JWS** (VC-JOSE-COSE) | v0 — the only profile the engine accepts |
| VC 2.0 + SD-JWT | reserved |
| EUDI `dc+sd-jwt` | reserved (eIDAS "powers and mandates to represent legal persons") |
| COSE | reserved |

### 3.1 JOSE header (v0 profile)

| # | Field | Value | Rule |
|---|---|---|---|
| 1 | `alg` | `EdDSA` | MUST. No other algorithm accepted. |
| 2 | `typ` | `vc+jwt` | MUST. |
| 3 | `kid` | Principal key DID URL, e.g. `did:web:acme.com#key-1` | MUST. |

### 3.2 Fields

| # | Field | Type | Rule |
|---|---|---|---|
| 4 | `@context` | array | MUST be exactly `["https://www.w3.org/ns/credentials/v2", "https://delegus.ai/ns/grant/v1"]`. |
| 5 | `type` | array | MUST be exactly `["VerifiableCredential", "DelegusGrant"]`. |
| 6 | `id` | string | `urn:delegus:grant:<ULID>`. |
| 7 | `issuer` | string | Principal DID. MUST equal the DID portion of `kid`. |
| 8 | `validFrom` | RFC 3339 UTC | MUST. |
| 9 | `validUntil` | RFC 3339 UTC | MUST. `validUntil − validFrom ≤ 90 days`. |
| 10 | `credentialSubject.id` | string | Agent DID. |
| 11 | `credentialSubject.principal` | string | Root Principal DID. v0: MUST equal `issuer`. Reserved for chains. |
| 12 | `credentialSubject.authority` | array of Capability | 1–16 entries. |
| 13 | `credentialSubject.audience` | array of RP DIDs | OPTIONAL. Absent = any registered RP. |
| 14 | `credentialStatus` | BitstringStatusListEntry | MUST. `statusPurpose: "revocation"`, `statusListIndex`, `statusListCredential`. |
| 15 | `parentGrant` | string | RESERVED. MUST be absent in v0 → DENY `UNSUPPORTED_FEATURE`. Future: child authority ⊆ parent authority. |
| 16–20 | `iss`, `sub`, `jti`, `nbf`, `exp` | registered claims | MUST 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": ["*"] }
```

- `action` — `<namespace>:<verb>`. v0.3 (`delegus-base-v3`) defines exactly five. The first two are unchanged from v0.2; the last three are the seller-side commitments, new in v0.3:

| `action` | Constraints (all REQUIRED unless marked OPTIONAL) | Meaning of an authorized action |
|---|---|---|
| `commerce:purchase` | `maxAmount` (integer, minor units), `currency` (ISO 4217, single); OPTIONAL `budget` (`total`, `perPeriod`, `uses`) | the actor commits its Principal to buy |
| `api:call` | `methods` (non-empty array of uppercase HTTP methods); OPTIONAL `budget` (`uses` only) | the actor calls an API on its Principal's behalf |
| `commerce:quote` | `maxAmount`, `currency`; OPTIONAL `budget` (`uses` only) | the actor offers, for its Principal, to sell at the stated price |
| `commerce:accept` | `maxAmount`, `currency`; OPTIONAL `budget` (`uses` only) | the actor accepts an order for its Principal at the stated price |
| `commerce:fulfil` | none; OPTIONAL `budget` (`uses` only) | the actor commits its Principal to deliver the named resource |

- `constraints` — keys MUST all be defined for that action type in this spec version; under `delegus-base-v3`, `budget` is defined for every type as in the table (a v3 implementation MUST NOT reject it at P17). **Unknown key → DENY `UNKNOWN_CONSTRAINT`** (P17). A missing or ill-typed REQUIRED key makes the Grant malformed (P1). `merchantCategory`, listed as OPTIONAL in v0.2, is **not defined** in v0.3: the §4.3 action has no field it could be compared with, so it cannot be evaluated; a Grant carrying it fails P17 (as v0.2 engines already do).
- `resources` — OPTIONAL glob array matched against `action.resource` (`*` any run of characters except `/`, `**` any run of characters; everything else literal; whole-string match). Absent = `["*"]`.
- Authorization: at least one Capability matches; a Capability matches iff `action` is equal and every constraint is satisfied. `currency` (P18) and `maxAmount` (P19) apply to every action that carries an amount (`commerce:purchase`, `commerce:quote`, `commerce:accept`); `methods` applies to `api:call` with the resource patterns at P20; `commerce:fulfil` is matched on `resources` alone. These are per-action limits; cumulative limits are the OPTIONAL `budget` constraint below. All amounts are integers in minor units; no floats anywhere in the protocol.
- A Grant evaluated under a v0.2 profile (`delegus-base-v1`, `delegus-base-v2`) MUST NOT contain the v0.3 action types: under those profiles they are unknown action types and the Grant is malformed (P1).
- `budget` (v0.3) — OPTIONAL on every action type: a consumable limit on the Capability, checked at P21 and spent by T7 (§5.2, §5.3). An object with at least one of these members and no other:
  - `total` — non-negative integer, minor units of the Capability's `currency`: the most that may be spent over the Grant's life. `commerce:purchase` only.
  - `perPeriod` — `{ "amount": <non-negative integer, minor units>, "period": "day" | "week" | "month" }`, exactly those two members: the most that may be spent in one period. Periods are anchored at the Grant's `validFrom` in UTC; a `month` period that starts on a day the target month lacks starts on its last day. `commerce:purchase` only.
  - `uses` — positive integer: how many ALLOW decisions the Capability may authorize. Every action type.

  `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:

- `day`: `validFrom + floor((now − validFrom) / 86 400 s) × 86 400 s`;
- `week`: the same with 604 800 s;
- `month`: `validFrom` advanced by *k* calendar months in UTC, for the largest *k* ≥ 0 whose start is ≤ `now`. Advancing keeps the time of day and the day of month; a day the target month lacks becomes that month's last day (a Grant valid from 31 January has periods starting 28 or 29 February, 31 March, 30 April, …).

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:

- total: `e.spent_total + a ≤ total`;
- period: the effective spend is `e.spent_in_period` when `e.period_start` equals the start of the current period, and 0 when `e.period_start` is `null` or earlier; a `period_start` later than the current period start is inconsistent state and gives `SERVICE_UNAVAILABLE`. Then `effective + a ≤ perPeriod.amount`;
- uses: `e.uses_spent + 1 ≤ uses`.

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

| Field | Value |
|---|---|
| `alg` | `EdDSA` (MUST) |
| `typ` | `delegus-proof+jwt` (MUST) |
| `kid` | Agent DID URL, e.g. `did:key:z6Mk...#z6Mk...` (MUST) |

### 4.2 Claims

| Field | Rule |
|---|---|
| `iss` | Agent DID. MUST equal Grant `sub`. |
| `aud` | RP DID. MUST equal the DID of the RP calling `/verify`. |
| `grant` | MUST equal Grant `id`. |
| `jti` | Security 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. |
| `iat` | Seconds since epoch. MUST satisfy `|now − iat| ≤ window` (default 60 s, RP-configurable, max 300 s). |
| `htm` | HTTP method, uppercase ASCII. |
| `htu` | Target URI per §4.3. |
| `action_hash` | `sha256:<64 lowercase hex>` of the UTF-8 bytes of `JCS(action)` (RFC 8785). |

### 4.3 Canonicalization

- `htu`: per RFC 9449 §4.3 — lowercase scheme `://` lowercase host [`:` port only if non-default] path. No query, no fragment. Percent-encoding normalized per RFC 3986 §6.2.2 (uppercase hex in escapes; unreserved characters decoded). Path otherwise exact: no trailing-slash normalization. RPs behind proxies MUST configure `publicBaseUrl` in the SDK so `htu` reflects the URL the Agent targeted.
- `action`: RFC 8785 JCS over the exact JSON the RP submits to `/verify`. All numbers MUST be integers. Strings are hashed as given; no Unicode normalization. Both SDKs construct `action` from the same wire fields.
- Proof signing input is the standard JWS signing input.

**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).

| `type` | Fields |
|---|---|
| `commerce:purchase`, `commerce:quote`, `commerce:accept` | `type`, `resource` (non-empty string), `amount` (non-negative integer, minor units), `currency` (ISO 4217) |
| `api:call` | `type`, `method` (uppercase HTTP method), `resource` (absolute `http(s)` URI) |
| `commerce:fulfil` | `type`, `resource` (non-empty string) |

OPTIONAL on every type (v0.3):

- `relies_on` — the prior decisions this action is taken in dependence on: an array of **1 to 8** entries, each an object with exactly two members, `fingerprint` (`sha256:<64 lowercase hex>`, §R.1) and `mode` (`PRIOR_DECISION` or `CURRENT_AUTHORITY`). Fingerprints within one array MUST be distinct. **There is no default mode**: an entry without `mode`, with an unknown mode, or with any other member is `ACTION_MALFORMED`. An empty array is `ACTION_MALFORMED` (omit the field instead).

`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`:

```
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

- Delegus generates the opening when it issues an ALLOW receipt (a DENY receipt has none, so the service never resolves one; D4 remains as a fail-closed check), keeps it, and returns it **only** to that receipt's relying party, in the `/verify` response (§5.6): `{ "fingerprint": "sha256:…", "nonce": "<base64url>" }`. The nonce's wire form is the canonical unpadded base64url encoding of the 32 bytes: exactly 43 characters; any other form is invalid.
- A relying party passes the **fingerprint**, never the nonce, to a party that will declare the dependency. A fingerprint confers no access to the receipt (§0.1).
- D2 resolves a declared fingerprint inside Delegus: it holds the value only if recomputing `fingerprint(nonce, R)` from a receipt R Delegus issued and that receipt's stored opening yields exactly the declared value. Resolution is a lookup in Delegus-owned storage (§5.5).
- **Resolution precedes P15, so it is restricted.** The lookup in the bracketed step before P6 (§5.4) happens before P15 validates the Action. It MUST consider only entries whose `fingerprint` already has the §6.3 form (`sha256:` and 64 lowercase hex characters), at most the first 8 entries, by exact fingerprint only (an equality lookup, never a prefix, range or scan), and it MUST have no effect other than reading: it reads the issued-receipt records (each receipt with its opening), then the internal-transaction records of the receipts found and, for `CURRENT_AUTHORITY` entries, each dependency Principal's standing, the key state of each dependency Grant's `kid` and its Principal's status-list credential, and nothing else: no write, no counter, no allocation. Its answer is used only if P15 later holds; if P15 fails, the answer is discarded and D1–D7 are `"skipped"`. A malformed entry is therefore never looked up, and a lookup can never change the outcome of a request that P15 refuses.

### R.3 Transaction handles

- Delegus keeps an internal transaction identifier; it never appears in a receipt, a response or an API.
- **On ALLOW**, the receipt carries a **pairwise handle** for its relying party: 16 bytes from a CSPRNG, written as exactly 22 characters of canonical unpadded base64url (`transaction`, §6.2), returned in the response (§5.6). A DENY receipt carries no handle and no opening.
- A handle is per internal transaction and relying party. The same relying party acting again in the same internal transaction receives the same handle; a different relying party receives a fresh one. A handle is never reused across relying parties, so handles held at different trust boundaries cannot be linked by comparing them.
- A handle is an **output only**. It is never accepted as an input: the Action has no `transaction` member (§4.3), and no request field takes one.
- An action with no `relies_on` starts a new internal transaction (on ALLOW, the receipt is its first member). An action that declares `relies_on` joins the internal transaction of its first resolved dependency, in Action order. D5 holds for an entry only if that dependency belongs to the same internal transaction; if the declared dependencies belong to different internal transactions, D5 is false.
- Transaction handles give correlation, not discovery: no API or evidence lookup by handle is offered to anyone who is not a party to the receipt it came from (§0.1, §8).

### R.4 The CURRENT_AUTHORITY evidence commitment

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

```
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.

| Member | Who receives it | Lets the holder re-run |
|---|---|---|
| `checks` | every party entitled to R2 | nothing: Delegus's per-entry attestation of D2–D7 |
| `transaction_equal` | every party entitled to R2 | D5, as Delegus's attestation (internal transactions are never disclosed, §R.3) |
| `receipt_hash` | every party entitled to R2 | with the CURRENT_AUTHORITY members, the §R.4 opening of `evidence_commitment` |
| CURRENT_AUTHORITY only: `status_list_index`, `status_credential` (as read before P6), `evidence_nonce` | every party entitled to R2 | D7 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 JWS | only a party also entitled to the dependency receipt | D3 (signature, body = payload), D4 (decision), D6 (`expires_at`), and `receipt_hash` |
| `opening`: its 32-byte nonce, base64url | only a party also entitled to the dependency receipt | D2: `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:

```
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)
```

- `nonce_a` and `nonce_k` are independent, from a CSPRNG, and never reused. Their wire form is the 43-character opening encoding (§R.2).
- The response returns `authority` with `authority_opening` (§5.6), and `capability_key_opening` when `evidence.consumption` carries `capability_key_commitment`. Entitled parties can retrieve both openings again with the receipt (§8). The ledger key itself is never returned; an entitled party recomputes it from `authority`.
- The relying party checks `authority_commitment` by recomputing it from `authority` and `authority_opening`. A party holding only the receipt learns nothing about the Capability beyond the fact that one matched.
- The four tags (`relies-on`, `dependency-evidence`, `authority`, `capability-key`) are distinct, so no value computed under one can be presented as another.
---

## 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" } ] }
```

- `requires` is chosen by the relying party; `action.relies_on` is declared by the actor and signed through `action_hash` (P14). Delegus checks that the declaration covers every requirement (D1) and evaluates every declared entry (D2–D7).
- Each entry names a `fingerprint` (§R) and a `mode`, `PRIOR_DECISION` or `CURRENT_AUTHORITY`. There is no default mode: an entry without a known mode is malformed (P15 for `relies_on`; for `requires`, HTTP 400 `REQUIREMENTS_MALFORMED` before evaluation, no receipt, the `jti` not consumed).
- `requires` has the §4.3 `relies_on` entry shape exactly: 1 to 8 distinct entries, each exactly `{ fingerprint, mode }`. Because the shape is exact, the array as validated is the array as submitted; `requires_hash` is computed over it, in submitted order (§6.3), and a client computes the same value from what it sent.
- `requires` is accepted only for a relying party on `delegus-base-v3`; any other relying party that sends it gets HTTP 400 `REQUIREMENTS_NOT_SUPPORTED`, before evaluation.
- At most 8 entries in `relies_on`.
- A requirement names exact fingerprints. A requirement by kind ("a `commerce:purchase` receipt in this transaction") is future work.

**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"`.

| # | Check | DENY reason |
|---|---|---|
| P1 | Grant parses and conforms to §3 (incl. `parentGrant` absent) | `GRANT_MALFORMED` / `UNSUPPORTED_FEATURE` |
| P2 | `kid` names a verification method present in the DID document snapshot | `ISSUER_KEY_NOT_FOUND` |
| P3 | Grant signature verifies | `GRANT_SIGNATURE_INVALID` |
| P4 | `now ≥ validFrom` | `GRANT_NOT_YET_VALID` |
| P5 | `now < validUntil` | `GRANT_EXPIRED` |
| P6 | Status credential signed by Delegus; bit at `statusListIndex` is 0 | `AUTHORITY_REVOKED` |
| P7 | `audience`, if present, contains RP | `AUDIENCE_MISMATCH` |
| P8 | Proof parses and conforms to §4 (incl. `jti` format) | `PROOF_MALFORMED` |
| P9 | Proof signature verifies against Agent key | `PROOF_SIGNATURE_INVALID` |
| P10 | Proof `iss` == Grant `sub` | `PROOF_AGENT_MISMATCH` |
| P11 | Proof `grant` == Grant `id` | `PROOF_GRANT_MISMATCH` |
| P12 | Proof `aud` == RP DID | `PROOF_AUDIENCE_MISMATCH` |
| P13 | `iat` within window | `PROOF_EXPIRED` |
| P14 | `action_hash` == hash of submitted Action | `PROOF_ACTION_MISMATCH` |
| P15 | Action valid for its `type` | `ACTION_MALFORMED` |
| P16 | Some Capability matches `action.type` | `ACTION_NOT_AUTHORIZED` |
| P17 | All constraint keys known | `UNKNOWN_CONSTRAINT` |
| P18 | Currency authorized | `CURRENCY_NOT_AUTHORIZED` |
| P19 | `amount ≤ maxAmount` | `AMOUNT_EXCEEDS_AUTHORITY` |
| P20 | `resource` matches a pattern | `RESOURCE_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.

| # | Check | DENY reason |
|---|---|---|
| P21 | Some 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_UNAVAILABLE` | `AUTHORITY_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.

| # | Check | DENY reason |
|---|---|---|
| D1 | Every `requires` entry appears in `action.relies_on` with the same fingerprint and the same mode | `DEPENDENCY_NOT_DECLARED` |
| D2 | The 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_UNAVAILABLE` | `DEPENDENCY_COMMITMENT_MISMATCH` / `SERVICE_UNAVAILABLE` |
| D3 | That receipt's signature verifies against a Delegus receipt key and its body equals its payload | `DEPENDENCY_RECEIPT_INVALID` |
| D4 | Its `decision` is ALLOW | `DEPENDENCY_NOT_ALLOW` |
| D5 | It belongs to the same internal transaction as the first resolved dependency, in Action order (§R.3) | `DEPENDENCY_TRANSACTION_MISMATCH` |
| D6 | `CURRENT_AUTHORITY` only: the dependency's Grant is still within its validity at `now` (`now < expires_at` of the dependency receipt) | `DEPENDENCY_AUTHORITY_EXPIRED` |
| D7 | `CURRENT_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_UNAVAILABLE` | `DEPENDENCY_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`

| # | Check | DENY reason |
|---|---|---|
| T1 | `issuer` is a registered Principal | `ISSUER_UNKNOWN` |
| T2 | Principal is domain-verified | `ISSUER_UNVERIFIED` |
| T3 | `kid` not marked `compromised` | `ISSUER_KEY_COMPROMISED` |
| T5 | Replay: `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.)

| # | Check | DENY reason |
|---|---|---|
| T1 | `issuer` 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` |
| T7 | The 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

```
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:
- `authority` and `authority_opening`: the matched Capability in plaintext and the 32-byte opening (base64url, 43 characters), present exactly when the receipt pins `authority_commitment` (on ALLOW, and on a DENY decided after a Capability matched). The relying party recomputes `authority_commitment` from them (§R.6).
- `capability_key_opening`: present exactly when `evidence.consumption` pins `capability_key_commitment` (a budgeted Capability); it opens that commitment to the ledger key, which an entitled party recomputes from `authority` (§R.6).
- `relies_on`: the per-dependency results, present exactly when the receipt pins `relies_on_hash`: an array, in Action order, of `{ fingerprint, mode, checks: { D2 … D7 }, evidence_commitment? }`. The relying party checks `sha256(JCS(relies_on))` = `relies_on_hash`.
- `opening`: on ALLOW only, `{ "fingerprint": "sha256:…", "nonce": "<base64url, 32 bytes>" }`, the commitment to this receipt that a later action may declare, and its opening (§R.2). It is returned only in this response, only to this relying party; Delegus keeps the nonce to resolve the fingerprint internally (D2). A downstream agent is given the fingerprint by the relying party, out of band, never the nonce.

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:

| Field | Bytes hashed |
|---|---|
| `grant_hash` | ASCII of the compact JWS string as received |
| `proof_hash` | ASCII 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_hash` | raw response body bytes as fetched |
| `evidence.status.credential_hash` | ASCII of the status-list credential compact JWS |
| `evidence.trust_config_hash` | UTF-8 bytes of the immutable trust-configuration descriptor named by `trust_config_version` |
| `protocol.profile_hash` | raw 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

- `receipt` is a **non-detached** compact JWS: `alg: EdDSA`, `typ: delegus-receipt+jwt`, `kid: did:web:delegus.ai#receipt-<n>`, payload = `JCS(receipt object minus "receipt")`. The JWS string is the canonical receipt; the JSON body is a decoded convenience and MUST equal the payload after JCS.
- On DENY where P3 did not pass, `principal`, `agent`, `grant_id`, `authority_commitment`, `expires_at` are omitted. Delegus never echoes an unauthenticated attribution.
- **v0.3 fields:**
  - `authority_commitment` is present when a Capability matched (on ALLOW, and on a DENY decided after P16); its opening is returned beside the receipt (§5.6) and again to entitled parties (§R.6).
  - `transaction` is present on ALLOW only: this receipt's pairwise handle (§R.3), an output only. A DENY receipt carries none.
  - `relies_on_hash` is present when the Action declared dependencies. It pins the per-entry results returned beside the receipt (§5.6): each entry's `fingerprint`, `mode`, per-entry results `D2`–`D7` and, for `CURRENT_AUTHORITY`, an `evidence_commitment` to the dependency evidence it used (§R.4).
  - `requires_hash` is present exactly when the request carried `requires` (§5.1).
  - `protocol.checks.D1`–`D7` are the conjunction over entries, and `reason` is the first `false` in §5.4 order, entries taken in Action order.
- **Size (normative):** the receipt's JWS signing input MUST NOT exceed 4,096 bytes. Delegus determines the complete receipt before any write: it evaluates with the replay write and the spend deferred (§5.4), with the transaction handle's fixed length counted for an ALLOW, and measures the signing input. If it fits, the writes follow in order. If it does not: when the `jti` is already recorded the result is the `PROOF_REPLAYED` receipt, and when the replay store is unavailable the `SERVICE_UNAVAILABLE` receipt (both small, as the §5.4 order would give); otherwise Delegus refuses with HTTP 422 `RECEIPT_TOO_LARGE`, no receipt, nothing written, the `jti` not consumed and no budget spent. With the authority, dependencies and requirements pinned by hash, a v2 receipt's size does not grow with the Grant or the number of dependencies.
- **Public verification (v0.3):** anyone holding a receipt can check its signature, that its body equals its payload, and that the hashes it pins are well formed. This shows the receipt is authentic and unmodified; it does not reveal why it was decided.
- **Entitled reproduction (v0.3):** re-deriving the decision needs, in addition, the Grant, Proof and Action, the plaintext Capability and dependency results returned beside the receipt (to open `authority_commitment` and check `relies_on_hash`), the `requires` sent (to check `requires_hash`), and the dependency disclosure package (§R.5). With them, P1–P21, D1–D4, D6 and D7's revocation part are reproducible offline with `now` = `evaluated_at`. D5, and D7's standing and key-state parts, are Delegus's attestation, stated per entry in the package; P21 is reproducible given the consumption snapshot pinned by `evidence.consumption.snapshot_hash`. Only the receipt's principal and relying party are entitled in v0.3 (§8).
- **Offline verification (v1 receipts, and the protocol layer of v2 receipts given the entitled material):** given the receipt, the Delegus receipt public key, and the artifacts named by `did_document_hash`, `credential_hash`, `profile_hash`, and `trust_config_hash` (retrievable by hash — §8), a third party can re-run `delegus-base-v1` and confirm `protocol.result`. The `trust` block is Delegus's attestation and is not reproducible by design.
- **Historical verification clock (normative):** when reproducing the protocol result represented by a Receipt, the verifier MUST use the Receipt's authenticated `evaluated_at` value as `now` for every time-dependent protocol check (P4, P5, P13). It MUST NOT use its own wall-clock time. Because `evaluated_at` is inside the signed payload, the historical evaluation time cannot be altered without invalidating the Delegus signature.

```
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.

- `POST /revoke` MUST be idempotent. Revoking an already-revoked Grant returns success with the existing revoked state.
- Revocation is permanent; there is no un-revoke.
- Delegus MUST fail closed (`SERVICE_UNAVAILABLE`) if it cannot establish current revocation state.
- Internal coordination MAY use any implementation that preserves the guarantee. Reference design: one Bitstring Status List per Principal (131,072 bits); `/revoke` sets the bit, re-signs the Principal's status-list credential, and increments `status_ver:{principal}` in the internal revocation store; each engine node caches `{principal → (version, signed credential)}`.
- **Ordering within `/verify` (normative):** before P6, the engine obtains the current `status_ver:{principal}` from the internal revocation store and ensures its cached signed status credential is at least that version. After P1–P14 pass, T5 atomically consumes the Proof `jti`. These are two distinct internal operations; in the worst case `/verify` performs two internal round trips. They MAY be optimized or coalesced (e.g. reading `status_ver` at request start, which still observes every revocation that completed before the request began) provided the canonical evaluation order (§5.4), the rule that T5 executes only after P1–P14, and the global read-after-write revocation guarantee are all preserved. Implementations MUST NOT reorder P6 after T5 or execute T5 before P14 to save a round trip.
- Published credentials at `statusListCredential` URLs serve the latest signed list with `Cache-Control: max-age=30`, for third-party verifiers only.

---

## 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

- **Rotate:** add key N+1 `active`, mark N `retired`. Grants signed by N remain valid to `validUntil`; new Grants MUST use an `active` key.
- **Compromise:** `POST /keys/{id}/compromise` marks the key; every Grant it signed DENIES (T3) immediately.
- **Agent compromise:** revoke its Grants or use the kill switch; a new key is a new Agent.

---

## 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

| ID | Threat | Delivered by |
|---|---|---|
| TM-01 | Forged Grant | P2, P3 |
| TM-02 | Modified Grant | P3 |
| TM-03 | Stolen Grant used without the Agent key | P9, P10 |
| TM-04 | Proof replay at the same RP | P13, T5 |
| TM-05 | Proof forwarded to or replayed at a different RP | P12 |
| TM-06 | Action tampering between Agent and Delegus | P14 |
| TM-07 | Expired or not-yet-valid authority | P4, P5 |
| TM-08 | Revoked authority | P6 + §7 guarantee |
| TM-09 | Issuer key compromise, after Delegus is notified | T3 |
| TM-10 | Stale identity evidence going unrecorded | Receipt pins `did_document_hash`, `resolved_at`; refresh ≤ 5 min |
| TM-11 | Unknown or unverified issuer; (v0.3) issuer not in good standing | T1, T2 (`delegus-trust-v3`: T1 also requires good standing; a Principal staff marked `suspended` or `disputed` gets a signed DENY `ISSUER_NOT_TRUSTED`) |
| TM-12 | Status-list substitution or forgery | P6 (Delegus-signed); receipt pins `credential_hash` |
| TM-13 | Grant used at an RP the Principal excluded | P7 |
| TM-14 | Evidence altered after the fact | Content-addressed store referenced by hash from signed receipts |
| TM-15 | (v0.3) Relying on a prior decision that was forged, altered or never issued | D2 (commitment opens to a receipt Delegus issued), D3 (signature) |
| TM-16 | (v0.3) Relying on a prior DENY as if it were an ALLOW | D4 |
| TM-17 | (v0.3) Splicing dependencies from unrelated transactions into one action | D5 |
| TM-18 | (v0.3) Relying on authority that expired, was revoked, whose Principal was suspended, or whose signing key was compromised after the prior decision | D6, 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 mode | D1; `requires_hash` records what was required |
| TM-20 | (v0.3) An actor inserting or altering declared dependencies after signing | P14 (`relies_on` is inside the signed `action_hash`) |
| TM-21 | (v0.3) Spending beyond a budget, including concurrent requests racing for the last headroom | P21, 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 handle | Commitments (§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 alone | Pinned by hash and commitment; plaintext only beside the receipt and in the entitled package |

### 13.2 v0 explicitly does not protect against

| ID | Threat | Note |
|---|---|---|
| NP-01 | A malicious Principal intentionally authorizing malicious activity | Delegus attests authority, not intent |
| NP-02 | A compromised active Principal key before Delegus learns of it | Bounded by Grant lifetime and key count |
| NP-03 | A compromised Agent private key | Bounded by Grant scope and expiry; KMS/HSM signer recommended |
| NP-04 | Business fraud unrelated to authority | Transaction assurance, not verification assurance (§14) |
| NP-05 | Semantic ambiguity in poorly defined actions | Mitigated only by the closed constraint vocabulary; RPs own the mapping |
| NP-06 | False or mistaken Outcome Attestations | Reporter-tagged, append-only; not adjudicated in v0 |
| NP-07 | A domain-verified Principal impersonating another organization via a lookalike domain | Domain control ≠ organizational identity; roadmap: KYB |
| NP-08 | An RP misreporting the action it received | The receipt records the RP's own submission |
| NP-09 | Compromise of Delegus's own signing keys | Trust root for the `trust` block; HSM custody; open-protocol reproducibility |
| NP-10 | Compromise of DNS or the web PKI | Shared with TLS; out of scope |
| NP-11 | (v0.3) Correlation by Delegus itself | Delegus issues every receipt in a transaction; handles and commitments limit outsiders only (§0.1) |
| NP-12 | (v0.3) An entitled party disclosing what it holds | A 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 present | By design it says nothing about now; use `CURRENT_AUTHORITY` |
| NP-14 | A low-entropy `jti` chosen by a faulty Agent | The 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 status | Accepted 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

```
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.**
