DelegusDocsv0.3 rc1

Docs/Errata

Protocol

Delegus Specification v0.2 — Errata

The specification is frozen (delegus-spec-v0.2-final.md). Changes after the freeze are published here as numbered errata, or as v0.3. An erratum states the sections it touches, what changed, and why. No erratum changes the bytes of the profile document (docs/profiles/delegus-base-v1.md) or of a trust-configuration descriptor; those are frozen separately because receipts pin their hashes.

Neither erratum below carries a date: both were applied before the v0.2 freeze was consolidated into the final document, and the repository's history begins after that (initial commit 2026-09-10). No earlier date is recorded.

Erratum 1 — revocation-read / replay-write ordering#

Sections: §5.3 (T5), §5.4 (canonical order), §7 (ordering within /verify), §15 step 5 (acceptance test).

Before. The freeze resolutions described the engine reading status_ver:{principal} "in the same pipeline as the replay SET", so the revocation-version read and the replay write were one internal operation, and the build-order acceptance test was "revoke, replay → AUTHORITY_REVOKED".

After. The revocation-version read and the replay write are two distinct internal operations at fixed places in the canonical order:

text
P1 → T1 → T2 → T3 → P2 → P3 → P4 → P5
   → [read status_ver; refresh cached status credential if needed]
   → P6 → P7 → P8 → … → P14
   → [atomic replay SET NX]
   → T5 → P15 → … → P20

T5 executes only after P1–P14 pass. Implementations MAY coalesce the status_ver read with the start of the request, but MUST NOT reorder P6 after T5 or execute T5 before P14 (§7). The acceptance test became "revoke the Grant; the same action with a fresh Proof → AUTHORITY_REVOKED", because re-sending the same Proof exercises replay (PROOF_REPLAYED), not revocation.

Why. With the read and the write in one pipeline, a Proof's jti could be consumed for a request that was about to be denied for revocation, and the position of the read relative to P6 was not fixed by the text. Fixing both positions makes the read-after-write revocation guarantee (§7) and the "replay is recorded only for requests that passed P1–P14" property statements about the order rather than about an implementation.

Erratum 2 — historical verification clock#

Sections: §5.4 (definition of now), §6.4 (offline verification).

Before. §5.4 said now is Delegus server time and is recorded in the receipt. §6.4 said a third party can re-run delegus-base-v1 from the receipt and the artifacts it names, without saying which clock to use.

After. §5.4: now is recorded as evaluated_at, and historical re-verification uses that value as now. §6.4 adds the normative rule: when reproducing the protocol result represented by a Receipt, the verifier MUST use the Receipt's authenticated evaluated_at as now for every time-dependent check (P4, P5, P13) and MUST NOT use its own wall-clock time.

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

Why. With the verifier's wall clock, an ALLOW receipt would stop reproducing as soon as the Grant expired or the Proof window passed, so receipts would only be verifiable briefly. evaluated_at is inside the signed payload, so using it makes every receipt reproducible for its whole retention period and cannot be altered without invalidating the signature.

Erratum 3 (2026-09-23) — unusable Ed25519 public keys#

Sections: §2.1 (Principal keys), §2.2 (Agent keys), §5.2 (P1, P2, P8), §13.1 (TM-01, TM-03).

Before. §2.1 and §2.2 required Ed25519 keys but said nothing about which 32-byte strings are acceptable as an Ed25519 public key. An implementation could accept any 32 bytes. For a public key that is a non-canonical encoding or a point of small order, a signature that verifies for every message can be produced without any private key (for the neutral element, R = the neutral element and S = 0). With such a key named as the Agent, anyone who sees the Grant could sign Proofs for it. With such a key in a Principal's DID document, anyone could sign Grants for that Principal.

After. An Ed25519 public key, whether in a did:key or in a DID-document Multikey, MUST be rejected if its encoding is non-canonical (RFC 8032 §5.1.3: the encoded y is ≥ p, or x = 0 with the sign bit set) or if it is one of the eight points of small order. A key that is rejected is treated as absent, with these outcomes:

Registration paths (Agent registration, managed Principal keys, hosted DID documents) reject such keys the same way.

Why. TM-01 (forged Grant) and TM-03 (stolen Grant used without the Agent key) assume that only the holder of a private key can produce a valid signature for its public key. That holds for every canonical Ed25519 public key outside the small-order set and fails for the keys above, which the party that chose the key could also use to deny having signed. Rejecting them at parse time restores both protections without changing any check, reason code, profile document or trust-configuration descriptor. Conformance vectors pinning each outcome were added alongside the existing 36, which are unchanged.

Erratum 4 (2026-10-21): the "domain" label for a managed Principal#

Sections: §2.1 (Principal, domain verification), §5.3 (T2), §6.2 (principal in the receipt), §6.4 (offline verification). A clarification: no check, reason code, receipt byte, profile document or trust-configuration descriptor changes.

Before. §6.2 shows "principal": { "id": …, "verification": "domain" } but no text defines the values of principal.verification. §2.1 says a Principal whose domain verification fails is unverified. Nothing said what the label is for a managed Principal (did:web:delegus.ai:org:<slug>), and a reader could take "domain" to mean that the company named by the DID had proven something about itself.

After. Under delegus-base-v1 (trust policy delegus-trust-v1):

v0.2 receipt bytes are unchanged. Emitting "managed" under delegus-base-v1 would make existing verifiers fail to re-verify new v1 receipts offline (§6.4): they recompute the body from the same inputs, get "domain", and find a difference. v0.3 (delegus-base-v3) defines the values, including "managed", in its §6.2.

Why. A receipt is evidence a stranger relies on. A label that reads as "this company proved its domain" when the operator holds the DID overstates what was checked. Fixing the meaning in text, and giving receivers a rule that depends only on signed data they already have, removes that reading without breaking any receipt already issued or any verifier already running. Tests pin the v1 label for a managed Principal at "domain" (packages/core/test/principal-class.test.ts); the 40 v0.2 conformance vectors are unchanged.

Note (2026-09-15) — licence statement added to the header#

Sections: none. A non-normative "Licence" line was added to the header of delegus-spec-v0.2-final.md, after "Normative language". The specification text is licensed CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/) with the attribution "Delegus Specification v0.2, © Delegus (delegus.ai)"; @delegus/core (with its conformance vectors) and @delegus/sdk are Apache-2.0. No normative text, profile document or trust-configuration descriptor changed, so this is recorded as a note rather than a numbered erratum.