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/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. --- diff --git a/cycles-spec-index.yaml b/cycles-spec-index.yaml index ddc9b93..dc9590c 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: 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 + 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..671667a --- /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) +as `conformance: reference` (non-normative) with `conformance_status: 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). 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