From a92db92676abf255ee2b1292aebc97fdeecedba9 Mon Sep 17 00:00:00 2001 From: Koishore Roy Date: Thu, 11 Jun 2026 10:46:17 +0530 Subject: [PATCH] spec prose: status bookkeeping catches up with the 0.3.0 query-fold MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A conformance review found spec.md contradicting itself after the reference shipped the §4.2 query-fold (delego 0.3.0, CTK regenerated in #9): §2.1's version table/tag legend, the §10 preamble, and the §10 'deferred (breaking)' block still called the fold deferred and said the reference implements 0.2, while §4.2 (normative), the CTK README, and conformance.py all treat it as 0.3 and reference-backed. - §2.1: 0.3 row is reference-complete/CTK-backed; the query-fold is the 0.3 breaking change (reference-backed since 0.3.0, hashing vectors on the 0.3 preimage, 0.2 preimage preserved as hashing-v0.2.json); the reference implements 0.3. Inline '(0.3, draft — additive)' tags become '(0.3 — additive)'. - §4.1: worked example regenerated on the 0.3 preimage (canonical JSON carries "query":[]; fingerprints match ctk/vectors/hashing.json). - §4.2/§10: the Broker query obligation is scoped to the ≤0.2 preimage (the fold supersedes it on 0.3); the §10 deferred block becomes the 0.3 breaking conformance line; the token line quotes §9's actual TTL rule (SHOULD ≤60s, MUST NOT exceed 300s). - examples/authorization-token.{md,json}: drop the stale 'not yet minted' note (reference-backed since 0.3.3, #11); fpr updated to the place-order's 0.3 fingerprint. - README: status section rewritten to match; repaired spec.md anchors (§7.1 retag; two §9 links broken since #11's heading change). No normative design text changed — same precedent as #11's §9 status flip. validate.py + conformance.py green; all spec.md anchors resolve. --- CHANGELOG.md | 27 ++++++++++++ README.md | 23 +++++----- examples/authorization-token.json | 2 +- examples/authorization-token.md | 11 ++--- spec.md | 72 ++++++++++++++++++------------- 5 files changed, 87 insertions(+), 48 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5dff9d..4602494 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,33 @@ reference implements it. See [`spec.md` §2.1](spec.md) for the version matrix. ## [Unreleased] +### Fixed (spec prose — status bookkeeping caught up with the 0.3.0 query-fold) +- `spec.md` had not been updated when the reference shipped the §4.2 query-fold + (delego 0.3.0, CTK regenerated in #9) and contradicted itself: §2.1's version + table and tag legend, the §10 preamble, and the §10 "deferred (breaking)" + block still called the query-fold *deferred / not part of 0.3* and said *"the + reference implements 0.2"*, while §4.2 (normative), the CTK README, and + `conformance.py` all treat the fold as 0.3 and reference-backed. All status + prose now states: the reference implements **0.3**; the query-fold is the 0.3 + **breaking** conformance line with `hashing.json` wired (≤ 0.2 preimage + implementations use `hashing-v0.2.json` and the §4.2 Broker query obligation, + which the fold supersedes on the 0.3 preimage). Inline *(0.3, draft — + additive)* tags became *(0.3 — additive)*. **No normative design text + changed** — same precedent as #11's §9 status flip. +- §4.1 worked example regenerated on the 0.3 preimage (the shown canonical JSON + now carries `"query":[]`; all three fingerprints updated to match + `ctk/vectors/hashing.json`). +- §10 token line now quotes §9's actual TTL rule (SHOULD ≤ 60 s, MUST NOT + exceed 300 s) instead of "≤ 60 s `exp`". +- `examples/authorization-token.{md,json}`: dropped the stale "not yet minted by + the reference" note (reference-backed since 0.3.3, #11) and updated `fpr` to + the place-order's 0.3 fingerprint so the example again matches + `ctk/vectors/hashing.json` as its prose claims. +- `README.md` "Status & versioning" rewritten to the same state (reference + implements 0.3; query-fold is the 0.3 breaking track, not deferred). Repaired + `spec.md` anchor links: the §7.1 link tracks the retagged heading, and the two + §9 links broken since #11's heading change now point at the current slug. + ### Changed (§9 authorization token — now reference-backed) - The reference implements the **§9 authorization-token profile** as of **delego 0.3.3**. §9's status flips from *draft / not yet in reference* to diff --git a/README.md b/README.md index f6dce86..8fef126 100644 --- a/README.md +++ b/README.md @@ -51,9 +51,9 @@ byte-for-byte. - [Proposed Action & hashing](spec.md#4-proposed-action) - [Policy & decisions](spec.md#5-policy) - [Approval binding — the confused-deputy guard](spec.md#7-approval-binding-the-confused-deputy-guard) -- [Authorization properties (P1–P4)](spec.md#71-authorization-properties-normative-03-draft--additive) +- [Authorization properties (P1–P4)](spec.md#71-authorization-properties-normative-03--additive) - [Receipt & audit chain](spec.md#8-receipt--audit-chain-normative) -- [Authorization Token](spec.md#9-authorization-token-optional-profile) +- [Authorization Token](spec.md#9-authorization-token-optional-profile-03--reference-backed-since-delego-033) ## Ecosystem @@ -77,16 +77,17 @@ reproduce them; see [§10 Conformance](spec.md#10-conformance). ## Status & versioning **v0.3 — frozen.** The spec/protocol is versioned `0.x` (the reference *package* -is `0.x.y`). 0.1–0.2 are reference-backed; each prior protocol version has a -standalone document of record in [`versions/`](versions/README.md) -([0.1](versions/spec-v0.1.md), [0.2](versions/spec-v0.2.md) — what the reference -implements today). 0.3 adds **additive hardening clauses** -— the §4.2 Broker query obligation, policy-schema validation (§5.1), the -authorization properties P1–P4 (§7.1), head-anchoring (§8.3), and the +is `0.x.y`). The reference implements **0.3** (delego ≥ 0.3.0; the §9 token +profile since 0.3.3); each prior protocol version has a standalone document of +record in [`versions/`](versions/README.md) ([0.1](versions/spec-v0.1.md), +[0.2](versions/spec-v0.2.md)). 0.3 has two tracks: **additive hardening clauses** +— the §4.2 Broker query obligation (≤ 0.2 preimage), policy-schema validation +(§5.1), the authorization properties P1–P4 (§7.1), head-anchoring (§8.3), and the authorization-token profile (§9) — which tighten obligations **without changing -any hashed or signed bytes** and so MAY be adopted on the 0.2 preimage. One item -is **deferred**: folding the URL query into the `action_fingerprint` preimage -(§4.2) is a breaking change held for a later draft. See the +any hashed or signed bytes** and so MAY be adopted on the 0.2 preimage; and one +**breaking** change — folding the canonicalized URL query into the +`action_fingerprint` preimage (§4.2), reference-backed since delego 0.3.0 with +the `hashing` CTK vectors regenerated on the 0.3 preimage. See the [§2.1 version matrix](spec.md#21-protocol-versions). A breaking change to the receipt fields bumps the version (see [§8.2](spec.md#82-schema-versioning)). diff --git a/examples/authorization-token.json b/examples/authorization-token.json index 1ca91e1..fe43204 100644 --- a/examples/authorization-token.json +++ b/examples/authorization-token.json @@ -5,7 +5,7 @@ "exp": 1759000045, "jti": "01JBQK9Z6X8N3M2P0R5T7V9W1Y", "cns": "01JBQK9Z6X8N3M2P0R5T7V9W2Z", - "fpr": "c70d4ee57957202087887cb5e9d32222977b728bd06947b7761c283b6d4ed394", + "fpr": "4327df2637072bf058622d1f8baea6e431726f7332a50cd12ec970d6e43c2fd2", "iht": "76f8eef1b97e1213a59eec28cedf15bb999fdb00a3fd17f8343bc4676fdbb4f3", "apr": "apr_4c9183f7606f", "sub": "agent:onecli/session-7f3a", diff --git a/examples/authorization-token.md b/examples/authorization-token.md index e4238c3..f96469c 100644 --- a/examples/authorization-token.md +++ b/examples/authorization-token.md @@ -1,10 +1,11 @@ # Authorization Token — example -> **Illustrative.** The authorization token (spec [§9](../spec.md#9-authorization-token-optional-profile)) +> **Illustrative.** The authorization token (spec [§9](../spec.md#9-authorization-token-optional-profile-03--reference-backed-since-delego-033)) > is an **optional 0.3 profile** for carrying a PDP→PEP decision across a process -> or network boundary; it is **not** the protocol's load-bearing control and is -> **not yet minted by the reference implementation**. The compact form below uses -> a placeholder signature. +> or network boundary; it is **not** the protocol's load-bearing control. The +> reference mints and verifies it since **delego 0.3.3** (see +> [`../ctk/vectors/token.json`](../ctk/vectors/token.json) for signed verifier +> vectors). The compact form below uses a placeholder signature. A delego authorization token is a compact **JWS / JWT** (`header.payload.signature`, each segment base64url-encoded) signed with `alg = EdDSA` (Ed25519). @@ -25,7 +26,7 @@ each segment base64url-encoded) signed with `alg = EdDSA` (Ed25519). "exp": 1759000045, "jti": "01JBQK9Z6X8N3M2P0R5T7V9W1Y", "cns": "01JBQK9Z6X8N3M2P0R5T7V9W2Z", - "fpr": "c70d4ee57957202087887cb5e9d32222977b728bd06947b7761c283b6d4ed394", + "fpr": "4327df2637072bf058622d1f8baea6e431726f7332a50cd12ec970d6e43c2fd2", "iht": "76f8eef1b97e1213a59eec28cedf15bb999fdb00a3fd17f8343bc4676fdbb4f3", "apr": "apr_4c9183f7606f", "sub": "agent:onecli/session-7f3a", diff --git a/spec.md b/spec.md index 68d3163..1a99f37 100644 --- a/spec.md +++ b/spec.md @@ -74,13 +74,13 @@ of this document. |---------|--------|------| | **0.1** | reference-complete, CTK-backed | Canonical JSON (§3); intent hash + action fingerprint (§4); deterministic policy & decision, first-match-wins, fail-closed (§5–§6); fingerprint-bound approval / confused-deputy guard (§7); append-only, hash-linked, Ed25519-signed audit chain + verification (§8). | | **0.2** | reference-complete, CTK-backed | Approval & audit hardening. Approvals are additionally bound to the `intent_hash` and made **single-use** (§7); an approved action's `execution` receipt carries the rule it was parked under, so `rate_limit` counts it (§5, §8); verification treats a malformed or partial receipt as a *failure* rather than aborting the walk (§8.1). | -| **0.3** | **draft** | Two distinct tracks. **Additive hardening clauses** — the §4.2 Broker query obligation, policy-schema validation & fail-closed (§5.1), the authorization properties P1–P4 (§7.1), head-anchoring as the required truncation defense (§8.3), and the authorization-token profile (§9) — are **additive on the 0.2 preimage**: they tighten obligations without changing any hashed/signed bytes, and **MAY** be adopted by a 0.2 implementation. **One deferred breaking item** — folding the URL query string into the `action_fingerprint` preimage (§4.2) — changes the preimage and is therefore **DEFERRED** to a future draft; it is described as a forward-looking note only and is **not** part of 0.3. | +| **0.3** | reference-complete, CTK-backed | Two distinct tracks. **Additive hardening clauses** — the §4.2 Broker query obligation (≤ 0.2 preimage), policy-schema validation & fail-closed (§5.1), the authorization properties P1–P4 (§7.1), head-anchoring as the required truncation defense (§8.3), and the authorization-token profile (§9, reference-backed since delego 0.3.3) — are **additive on the 0.2 preimage**: they tighten obligations without changing any hashed/signed bytes, and **MAY** be adopted by a 0.2 implementation. **One breaking change** — folding the canonicalized URL query into the `action_fingerprint` preimage (§4.2) — changes every fingerprint; it is reference-backed since delego 0.3.0, with the `hashing` CTK vectors regenerated on the 0.3 preimage (the 0.2 preimage is preserved as `hashing-v0.2.json`). | -This document is **frozen at 0.3**; the reference implements **0.2**. Clauses -introduced after 0.1 are tagged inline — *(since 0.2)* for reference-backed -behaviour, *(0.3, draft — additive)* for the additive-hardening frontier that may -be layered on the 0.2 preimage, and *(0.3, draft — deferred, breaking)* for the -query-fold that changes the preimage and is held for a later draft. +This document is **frozen at 0.3**; the reference implements **0.3** (since +delego 0.3.0; the §9 token profile since 0.3.3). Clauses introduced after 0.1 +are tagged inline — *(since 0.2)* for 0.2 behaviour, *(0.3 — additive)* for the +additive-hardening clauses that may be layered on the 0.2 preimage, and +*(NORMATIVE, 0.3 — breaking)* for the query-fold that changes the preimage. ## 3. Canonicalization (NORMATIVE) @@ -139,20 +139,23 @@ For `GET https://api.example.com/accounts/me`, instruction `"read my account det ``` canonical_json for fingerprint: -{"host":"api.example.com","method":"GET","params":{},"path":"/accounts/me"} +{"host":"api.example.com","method":"GET","params":{},"path":"/accounts/me","query":[]} intent_hash = ec949034e985a92f3bcd9f9ab8313a80005157698f748f2b8df6163c04af4619 -action_fingerprint = 497e02606ea157ca8ca885cbbd33d1a8a70c40fdaf6f15c5154d858f870b8b61 +action_fingerprint = 051bae652ac13aa7eda6389a289e084c3414f9cd2b229544a679b47b21003d06 ``` +(`query` is the canonicalized URL query per §4.2 — `[]` for a bare URL. On the +≤ 0.2 preimage the key is absent; see `ctk/vectors/hashing-v0.2.json`.) + Adding one parameter changes the fingerprint completely — this is what makes the confused-deputy guard (§7) work: ``` order {amount:2400,currency:USD,destination:internal} - → c70d4ee57957202087887cb5e9d32222977b728bd06947b7761c283b6d4ed394 + → 4327df2637072bf058622d1f8baea6e431726f7332a50cd12ec970d6e43c2fd2 order {…, recipient:"attacker"} (one param added) - → dabddc8fc7e8fb30bdec6fb796a336b7897d4a2a12ae386727e2110d7e0e9572 + → 43aa4af8115a932a72f30da058b7396427a6f69f98cffbe4df8ce78017b89123 ``` See [`ctk/vectors/hashing.json`](ctk/vectors/hashing.json) for the full set. @@ -165,9 +168,9 @@ fingerprint**. Two requests that differ only in their query (e.g. `/orders?to=me` vs `/orders?to=attacker`) therefore share one fingerprint — a confused-deputy gap if decision-relevant data rides the query. -**Broker query obligation (NORMATIVE)** *(0.3, draft — additive)*. Because the -query is outside the fingerprint preimage, the Broker (PEP) **MUST** neutralise -it at enforcement time rather than trusting it: +**Broker query obligation (NORMATIVE)** *(0.3 — additive, ≤ 0.2 preimage)*. On +the ≤ 0.2 preimage the query is outside the fingerprint, so the Broker (PEP) +**MUST** neutralise it at enforcement time rather than trusting it: - A Broker **MUST NOT** transmit any query parameter to the Service that is not derivable from the authorized action. @@ -179,7 +182,9 @@ it at enforcement time rather than trusting it: This is an obligation on the *enforcement* point and changes no hashed or signed bytes; it is therefore additive on the 0.2 preimage and **MAY** be adopted by a -0.2 implementation. See the §10 conformance line. +0.2 implementation. On the 0.3 preimage the query-fold below supersedes it: the +whole query of an authorized action is fingerprint-bound, so the Broker forwards +it and refuses only a `#fragment`. See the §10 conformance line. **Folding the query into the fingerprint (NORMATIVE, 0.3 — breaking).** The stronger defense, normative in 0.3, makes the query part of the @@ -219,8 +224,9 @@ policy-evaluated. Decision-relevant values **MUST** therefore be carried in `params`, not solely in the query. This changes the fingerprint preimage and is therefore a **breaking** change -(§8.2); it bumps the protocol version and ships with updated `hashing` CTK vectors -when the reference implements it. +(§8.2); it bumped the protocol version to 0.3 and shipped with the `hashing` CTK +vectors regenerated on the 0.3 preimage when the reference implemented it +(delego 0.3.0). The 0.2-preimage vectors are preserved as `hashing-v0.2.json`. ## 5. Policy @@ -261,7 +267,7 @@ an empty match matches nothing. time; an action parked for approval is counted only once released, so several actions parked before any release MAY each execute even past `max`. - **Consistency class (NORMATIVE)** *(0.3, draft — additive)*. The `rate_limit` + **Consistency class (NORMATIVE)** *(0.3 — additive)*. The `rate_limit` count is **exact** only under a **serialized single-writer** audit ledger: every counted receipt is committed before the next decision reads the window. Under **concurrent writers** the count is **best-effort** — two decisions may @@ -275,7 +281,7 @@ an empty match matches nothing. > and both span `/`. Per-segment globbing is a planned refinement; a conformant > v0.1 implementation MUST reproduce the coarse behavior. -### 5.1 Policy validation (NORMATIVE) *(0.3, draft — additive)* +### 5.1 Policy validation (NORMATIVE) *(0.3 — additive)* Before evaluating any action, an Authorizer **MUST** validate its policy document against [`schema/policy.json`](schema/policy.json) and **fail closed** on @@ -361,7 +367,7 @@ Every refusal in this section **MUST** be recorded as an `execution`/`deny` receipt (§8). Authoritative vectors: [`ctk/vectors/resolve.json`](ctk/vectors/resolve.json). -### 7.1 Authorization properties (NORMATIVE) *(0.3, draft — additive)* +### 7.1 Authorization properties (NORMATIVE) *(0.3 — additive)* The §7 guards above and the §8 audit chain together provide four testable authorization invariants. A conformant Authorizer **MUST** uphold all four; they @@ -466,7 +472,7 @@ The set of payload fields is part of the wire format. Any change to it is a **breaking** change: it MUST bump this specification's version and the receipt `schema` version together, or previously-signed chains stop verifying. -### 8.3 Head-anchoring (NORMATIVE) *(0.3, draft — additive)* +### 8.3 Head-anchoring (NORMATIVE) *(0.3 — additive)* Head-anchoring is the **REQUIRED** defense against truncation and rollback (the §8.1 caveat). It changes no hashed or signed bytes — it adds a small commitment @@ -609,7 +615,7 @@ or `deny` outcome represents `REQUIRE_APPROVAL` / `DENY`. An implementation declares the highest protocol version (§2.1) it implements, and **MUST** satisfy every clause at or below that version and reproduce that version's -CTK vectors. The reference implements **0.2**. +CTK vectors. The reference implements **0.3**. **0.1** - A conformant **Authorizer** MUST implement §3–§8 and reproduce the CTK @@ -627,10 +633,12 @@ CTK vectors. The reference implements **0.2**. **0.3 — additive hardening** (additive on the 0.2 preimage; MAY be adopted by a 0.2 implementation) -- A **Broker** MUST satisfy the §4.2 query obligation: it MUST reconstruct the - outgoing URL from the authorized `host`/`path`/`params`, MUST NOT forward the - agent-supplied query verbatim or any query parameter not derivable from the - authorized action, and MUST refuse an action whose URL it cannot reconstruct. +- A **Broker** on the ≤ 0.2 preimage MUST satisfy the §4.2 query obligation: it + MUST reconstruct the outgoing URL from the authorized `host`/`path`/`params`, + MUST NOT forward the agent-supplied query verbatim or any query parameter not + derivable from the authorized action, and MUST refuse an action whose URL it + cannot reconstruct. (On the 0.3 preimage the query-fold supersedes this: the + query is fingerprint-bound, and the Broker refuses only a `#fragment`.) - An **Authorizer** MUST validate its policy against `schema/policy.json` and fail closed on an invalid policy, including rejecting unknown `match`/`constraints` keys, and MUST NOT skip an unknown constraint (§5.1). @@ -641,15 +649,17 @@ CTK vectors. The reference implements **0.2**. an **Auditor** MUST reject a chain that does not match a held anchor and MUST report that truncation cannot be ruled out when it holds no anchor (§8.3). - An **Authorizer/Broker** that participates in the authorization-token profile - MUST implement §9 / §9.1 (exact `aud`, ≤ 60 s `exp`, unique `jti`, single-use - `cns`, fingerprint re-check). + MUST implement §9 / §9.1 (exact `aud`; short `exp` — SHOULD ≤ 60 s, MUST NOT + exceed 300 s; unique `jti`; single-use `cns`; fingerprint re-check). - An implementation MUST document the `rate_limit` consistency class it provides (§5, §11). -**0.3 — deferred (breaking)** -- Folding the URL query into the `action_fingerprint` preimage (§4.2) is a - **breaking** change held for a future draft; it is **not** required by 0.3 and - has no CTK vector wired against the current reference. +**0.3 — breaking** +- An **Authorizer** MUST fold the canonicalized URL query into the + `action_fingerprint` preimage (§4.2) and reproduce the CTK `hashing` vectors + (0.3 preimage; reference-backed since delego 0.3.0). An implementation that + remains on the ≤ 0.2 preimage checks itself against `hashing-v0.2.json` and is + subject to the §4.2 Broker query obligation instead. ## 11. Security considerations