From bc06d9be102aedcaf86d888a385f933cce9a3158 Mon Sep 17 00:00:00 2001 From: Albert Mavashev Date: Sun, 14 Jun 2026 09:26:32 -0400 Subject: [PATCH 1/4] docs: make CyclesEvidence discoverable in the spec repo Evidence was invisible in the spec-repo docs (README, CONFORMANCE, spec-index) even though the runtime base ships the cycles_evidence ref + getEvidence and a draft envelope spec exists. - README: add a CyclesEvidence note to the companion-specs section (the cycles_evidence ref + getEvidence on the runtime base, the draft envelope companion, additive/optional). - cycles-spec-index.yaml: register drafts/cycles-evidence-v0.1.yaml as a document (id evidence_envelope, role companion, conformance: draft) with owns/integration_points/readers. - CONFORMANCE.md: a MAY bullet for emitting cycles_evidence + getEvidence, noting the envelope draft is not yet part of the conformance target. - drafts/README.md: new index of the drafts (evidence + aps-denial-mapping) with where the producing/signing code lives. Docs/metadata only; no spec wire change, no merged drift, changelog valid. --- CONFORMANCE.md | 1 + README.md | 2 ++ cycles-spec-index.yaml | 36 ++++++++++++++++++++++++++++++++++++ drafts/README.md | 18 ++++++++++++++++++ 4 files changed, 57 insertions(+) create mode 100644 drafts/README.md diff --git a/CONFORMANCE.md b/CONFORMANCE.md index e50e787..9e4cfdc 100644 --- a/CONFORMANCE.md +++ b/CONFORMANCE.md @@ -125,6 +125,7 @@ A conformant implementation MAY: - Replace any or all of those reference portions with an alternative provisioning mechanism: OAuth/OIDC for auth, GitOps YAML for policies, internal admin console for tenants, direct DB writes for budget allocation, etc. (The reference portions only — the eight normative cross-plane operations listed under §MUST still apply.) - Skip `audit_log` entirely. Not required by the protocol. - Expose additional endpoints beyond those specified, as long as they use a non-`/v1` path prefix or a vendor-namespaced extension path (e.g., `/v1/x-runcycles/...`). +- Emit **CyclesEvidence**: populate the optional `cycles_evidence` ref (`CyclesEvidenceRef`) on decide / reserve / commit / release / error responses and serve `GET /v1/evidence/{id}` (`getEvidence`). Both are additive and optional in `cycles-protocol-v0.yaml`; the signed envelope they reference is specified in the draft companion [`drafts/cycles-evidence-v0.1.yaml`](drafts/cycles-evidence-v0.1.yaml) (see [`drafts/README.md`](drafts/README.md)), which is **not yet part of the conformance target**. A client that ignores `cycles_evidence` is fully conformant. --- diff --git a/README.md b/README.md index 42644ed..cb0abf5 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,8 @@ See [`CONFORMANCE.md`](CONFORMANCE.md) for the authoritative MUST / SHOULD / MAY > - **Counter reset** — incident response endpoint for stuck per_run counters > > The suite ships as companion specs: [`cycles-protocol-v0.yaml`](cycles-protocol-v0.yaml) (runtime base), [`cycles-protocol-extensions-v0.1.26.yaml`](cycles-protocol-extensions-v0.1.26.yaml) (runtime extension), [`cycles-action-kinds-v0.1.26.yaml`](cycles-action-kinds-v0.1.26.yaml) (registry), [`cycles-governance-admin-v0.1.25.yaml`](cycles-governance-admin-v0.1.25.yaml) (admin base), and [`cycles-governance-extensions-v0.1.26.yaml`](cycles-governance-extensions-v0.1.26.yaml) (admin extension). See [`cycles-spec-index.yaml`](cycles-spec-index.yaml) for the composition manifest and merge recipes. +> +> **CyclesEvidence** (draft): the runtime base also defines an optional `cycles_evidence` reference (`CyclesEvidenceRef`) on the decide / reserve / commit / release / error responses plus `GET /v1/evidence/{id}` (`getEvidence`) — the consumer surface for tamper-evident, content-addressed, Ed25519-signed audit envelopes. The envelope itself is specified in the draft companion [`drafts/cycles-evidence-v0.1.yaml`](drafts/cycles-evidence-v0.1.yaml) (see [`drafts/README.md`](drafts/README.md)). Additive and optional — `0.1.25` clients that ignore it are unaffected. --- diff --git a/cycles-spec-index.yaml b/cycles-spec-index.yaml index ddc9b93..cb6cb50 100644 --- a/cycles-spec-index.yaml +++ b/cycles-spec-index.yaml @@ -263,6 +263,42 @@ documents: - Platform/operator teams (required for v0.1.26 admin API support) - Admin tooling authors + - id: evidence_envelope + file: drafts/cycles-evidence-v0.1.yaml + version: "0.1" + role: companion + conformance: draft + conformance_status: draft + conformance_note: >- + DRAFT — published under drafts/ for review, NOT yet normative. Specifies + the cycles-evidence/v0.1 envelope (JCS-canonicalized, Ed25519-signed, + content-addressed audit artifact) that wraps the bodies the runtime + endpoints return. The runtime base (cycles-protocol-v0.yaml) already ships + the consumer-facing surface — the optional `cycles_evidence` ref on + decide/reserve/commit/release/error responses and the getEvidence endpoint + — as additive/optional (MAY). The envelope shape promotes to a numbered + normative spec once a production implementation ships and a cross-system + consumer (APS) integrates end-to-end. Signer-key authority resolution + (did:cycles / JWKS / rotation) is tracked separately in runcycles/cycles-protocol#103. + title: Cycles Evidence Envelope + plane: cross-system-audit + owns: + - CyclesEvidence envelope (schema_version cycles-evidence/v0.1) + - evidence_id content-hash recipe (RFC 8785 JCS + sha256) and Ed25519 signature derivation + - Per-artifact payloads (DecidePayload / ReservePayload / CommitPayload / ReleasePayload / ErrorPayload) + - Response mirrors (non-attestation note for the transport-only cycles_evidence ref) + integration_points: + - runtime_base: >- + cycles-protocol-v0.yaml carries the wire surface: CyclesEvidenceRef + (cycles_evidence on the five response types) and getEvidence + (GET /v1/evidence/{id}). The envelope draft is what those refs resolve to. + depends_on: + - runtime_base # mirrors its request/response schemas + readers: + - Cross-system audit / receipt consumers (notably APS) + - Compliance + long-horizon archival tooling + - Cycles server + event-tier implementers (producer/signer) + # --------------------------------------------------------------------------- # MERGE RECIPES # --------------------------------------------------------------------------- diff --git a/drafts/README.md b/drafts/README.md new file mode 100644 index 0000000..6e65b29 --- /dev/null +++ b/drafts/README.md @@ -0,0 +1,18 @@ +# Cycles Protocol — drafts + +Pre-normative specs published here for review. **Drafts are not part of the +conformance target** (see [`../CONFORMANCE.md`](../CONFORMANCE.md)) — they +promote to a numbered spec file at the repo root once a production +implementation ships and a cross-system consumer has integrated against them +end-to-end. They are registered in [`../cycles-spec-index.yaml`](../cycles-spec-index.yaml) +with `conformance: draft`. + +| Draft | What it is | +|---|---| +| [`cycles-evidence-v0.1.yaml`](cycles-evidence-v0.1.yaml) | **CyclesEvidence envelope** — a JCS-canonicalized (RFC 8785), Ed25519-signed, sha256 content-addressed audit artifact that wraps the request/response of each authorization lifecycle event (`decide` / `reserve` / `commit` / `release` / `error`). It lets a cross-system consumer verify *what Cycles decided* offline, without access to the live ledger. The **consumer surface is already in the runtime base** (`cycles-protocol-v0.yaml`): the optional `cycles_evidence` ref (`CyclesEvidenceRef`) on the five response types, and `GET /v1/evidence/{id}` (`getEvidence`). This draft specifies the envelope those refs resolve to: the hash/signature recipe and the per-artifact payload shapes. Signer-key *authority* resolution (did:cycles / JWKS / rotation) is tracked in [#103](https://github.com/runcycles/cycles-protocol/issues/103). Golden fixtures: [`fixtures/cycles-evidence-v0.1/`](fixtures/cycles-evidence-v0.1/). | +| [`cycles-aps-denial-mapping-v0.1.md`](cycles-aps-denial-mapping-v0.1.md) | Mapping of Cycles denial reason codes to the APS (agent-passport-system) integration vocabulary. | + +## Producing & verifying evidence (where the code lives) + +- **`cycles-server`** computes the `evidence_id` content hash *synchronously* and returns the `cycles_evidence` ref on the response; it serves stored envelopes at `GET /v1/evidence/{id}`. It holds only the **public** signer identity. +- **`cycles-server-events`** asynchronously builds, **Ed25519-signs** (the private key lives only here), and stores each envelope; it recomputes the id and dead-letters on producer/worker drift. See its [identity enablement runbook](https://github.com/runcycles/cycles-server-events/blob/main/docs/evidence-identity-enablement.md). From 281153a77e3b9feac60cee502eae36b49f9c346b Mon Sep 17 00:00:00 2001 From: Albert Mavashev Date: Sun, 14 Jun 2026 11:15:43 -0400 Subject: [PATCH 2/4] docs(spec-index): use established conformance: reference for the evidence draft Review nit (3): the evidence_envelope document entry used a non-standard conformance: draft token. Switch to conformance: reference (the established non-normative value per the normative|reference vocabulary); conformance_status: draft + the note already convey it is pre-normative. --- cycles-spec-index.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cycles-spec-index.yaml b/cycles-spec-index.yaml index cb6cb50..dc9590c 100644 --- a/cycles-spec-index.yaml +++ b/cycles-spec-index.yaml @@ -267,7 +267,7 @@ documents: file: drafts/cycles-evidence-v0.1.yaml version: "0.1" role: companion - conformance: draft + conformance: reference # non-normative (per the normative|reference vocabulary); see conformance_status conformance_status: draft conformance_note: >- DRAFT — published under drafts/ for review, NOT yet normative. Specifies From 4457bffe601fa3301466be3fdf574fcd59ff86d6 Mon Sep 17 00:00:00 2001 From: Albert Mavashev Date: Sun, 14 Jun 2026 11:36:06 -0400 Subject: [PATCH 3/4] docs(review): clarify error-artifact domain vs server emission policy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review findings (Low #3 + Medium #1, draft side): - drafts/README.md: the spec-index now registers drafts as conformance: reference + conformance_status: draft (not conformance: draft) — fix the README sentence to match. - cycles-evidence-v0.1.yaml: add a DOMAIN vs EMISSION POLICY note to the error artifact_type — the artifact DOMAIN is any 4xx/5xx (so the mirror enum includes FORBIDDEN etc., which a verifier must accept), but WHICH codes a given server emits is policy: the reference server emits only for budget/lifecycle denials and omits pre-evaluation validation/auth. Resolves the apparent inconsistency between the envelope domain and the server's scoped emission. --- drafts/README.md | 2 +- drafts/cycles-evidence-v0.1.yaml | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/drafts/README.md b/drafts/README.md index 6e65b29..671667a 100644 --- a/drafts/README.md +++ b/drafts/README.md @@ -5,7 +5,7 @@ conformance target** (see [`../CONFORMANCE.md`](../CONFORMANCE.md)) — they promote to a numbered spec file at the repo root once a production implementation ships and a cross-system consumer has integrated against them end-to-end. They are registered in [`../cycles-spec-index.yaml`](../cycles-spec-index.yaml) -with `conformance: draft`. +as `conformance: reference` (non-normative) with `conformance_status: draft`. | Draft | What it is | |---|---| diff --git a/drafts/cycles-evidence-v0.1.yaml b/drafts/cycles-evidence-v0.1.yaml index 60e3371..aa38d8a 100644 --- a/drafts/cycles-evidence-v0.1.yaml +++ b/drafts/cycles-evidence-v0.1.yaml @@ -371,6 +371,17 @@ components: - `release` → POST /v1/reservations/{id}/release (clear without debit; 2xx) - `error` → 4xx/5xx ErrorResponse from any of the above + DOMAIN vs EMISSION POLICY: the `error` artifact's DOMAIN is any 4xx/5xx + `ErrorResponse` (hence the full `ErrorResponseMirror` ErrorCode enum, + including FORBIDDEN/INVALID_REQUEST) — that is what a verifier must be + able to accept. WHICH of those a given server actually emits an envelope + for is that server's policy, not a requirement of this schema. The + reference Cycles server (`cycles-protocol-v0.yaml`) emits `error` + evidence only for budget/lifecycle DENIALS reached after evaluation + (e.g. 409 `BUDGET_EXCEEDED`, 410 `RESERVATION_EXPIRED`) and deliberately + omits `cycles_evidence` for pre-evaluation failures (validation, auth, + malformed body) — nothing was decided, so there is nothing to attest. + The `error` artifact type is normative for v0.1 because non-dry reserve denials are the highest-signal evidence APS receipts carry (per aeoess/agent-passport-system#25) and the canonical From 7d11d37b5707e104d186c4247defa7a86c784f8a Mon Sep 17 00:00:00 2001 From: Albert Mavashev Date: Sun, 14 Jun 2026 13:23:52 -0400 Subject: [PATCH 4/4] docs(review): clarify forbidden/validation decide failures are error-DOMAIN, not server emission Review (Low): the v0.1.25.4 changelog line 'forbidden/validation failures on /v1/decide remain error-artifact territory (e.g. 12-decide-live-forbidden)' read as a reference-server emission promise. Reword to match the draft's domain-vs-emission note: those are the error-artifact DOMAIN (verifier-domain fixture illustrates the shape), and the reference server omits cycles_evidence for pre-evaluation auth/validation failures. --- changelogs/cycles-protocol-v0.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/changelogs/cycles-protocol-v0.md b/changelogs/cycles-protocol-v0.md index 782a557..4a8512a 100644 --- a/changelogs/cycles-protocol-v0.md +++ b/changelogs/cycles-protocol-v0.md @@ -41,8 +41,12 @@ _(revision 2026-06-13 — surface `cycles_evidence` on the decide response)_ `drafts/cycles-evidence-v0.1.yaml` (`DecidePayload` = `{request, response}`, no reservation created) with golden fixtures `01-decide-allow` / `09-decide-risk-points-allow`. Forbidden/validation failures on `/v1/decide` - remain `error`-artifact territory (e.g. fixture `12-decide-live-forbidden`). - Present unless emission is disabled; additive + non-breaking. + are NOT `decide` evidence — they surface as 4xx in the `error`-artifact DOMAIN + (the verifier-domain fixture `12-decide-live-forbidden` illustrates that shape; + the reference server itself omits `cycles_evidence` for such pre-evaluation + auth/validation failures — see the error artifact's domain-vs-emission note in + `drafts/cycles-evidence-v0.1.yaml`). Present unless emission is disabled; + additive + non-breaking. ---