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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
23 changes: 12 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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)).

Expand Down
2 changes: 1 addition & 1 deletion examples/authorization-token.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"exp": 1759000045,
"jti": "01JBQK9Z6X8N3M2P0R5T7V9W1Y",
"cns": "01JBQK9Z6X8N3M2P0R5T7V9W2Z",
"fpr": "c70d4ee57957202087887cb5e9d32222977b728bd06947b7761c283b6d4ed394",
"fpr": "4327df2637072bf058622d1f8baea6e431726f7332a50cd12ec970d6e43c2fd2",
"iht": "76f8eef1b97e1213a59eec28cedf15bb999fdb00a3fd17f8343bc4676fdbb4f3",
"apr": "apr_4c9183f7606f",
"sub": "agent:onecli/session-7f3a",
Expand Down
11 changes: 6 additions & 5 deletions examples/authorization-token.md
Original file line number Diff line number Diff line change
@@ -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).
Expand All @@ -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",
Expand Down
72 changes: 41 additions & 31 deletions spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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).
Expand All @@ -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

Expand Down