Skip to content
Merged
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
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ proxy. It checks mission, resource, budget, and delegation constraints before
that integration dispatches the call, then emits an issuer-signed,
hash-linked receipt for the decision.

For issuer-selected dangerous tools, an optional signed `risk_budget` claim
binds authenticated tool schemas to typed per-action impact caps and atomic
session, agent, and lineage ceilings. The executor must explicitly close every
permitted reservation as committed once execution may have started, or as
released only when execution never started. This does not infer semantic risk
or hidden side effects; see the
[typed risk-budget reference](docs/reference/risk-budgets.md).

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Status](https://img.shields.io/badge/status-pre--release-blue)](STATUS.md)
[![Discussions](https://img.shields.io/badge/GitHub-Discussions-181717?logo=github)](https://github.com/ArdurAI/ardur/discussions)
Expand Down Expand Up @@ -216,6 +224,7 @@ Concretely — these are the design principles the repo is being built to meet,
- **Composable with what already exists.** Designed around SPIFFE for workload identity, Biscuit for first-party-attenuation credentials, Cedar for policy, the individual AAT Internet-Draft for delegation-token semantics, and EAT (RFC 9711) for attestation-token semantics. We didn't reinvent the substrate.
- **Cryptographically bound by design.** Mission credentials are designed to be signed by an issuer key and produce signed receipts chain-hashed to the previous one. The Python Biscuit path reports SPIFFE holder binding only when the proxy has a server-owned Biscuit issuer key, JWT-SVID trust bundle, and audience and the presented credentials verify against them; request payloads cannot choose those verifier inputs. JWT-SVID itself remains a replayable bearer credential, so this is bounded holder evidence rather than universal replay prevention. The design is documented in the [ADRs](docs/decisions/README.md); the public code that implements it is being curated in phases.
- **Delegation that narrows, never widens.** Child sessions get strictly narrower authority than their parent — fewer tools, smaller resource scope, smaller budget. The narrowing discipline is formalised in [ADR-017](docs/decisions/ADR-017-biscuit-attenuation-narrowing-semantics.md).
- **Impact caps before dangerous actions.** Opted-in Mission Passports bind trusted tool contracts to typed action caps and atomically conserved session/agent/lineage ceilings. Crash reservations quarantine instead of silently refunding authority; the design is recorded in [ADR-026](docs/decisions/ADR-026-typed-dangerous-action-risk-budgets.md).
- **No authority by omission.** An absent or empty `resource_scope` grants no resource authority. Operators who intentionally permit every resource must sign the sole explicit wildcard `resource_scope: ["**"]`; issuance and governed-run surfaces warn when they do. The decision and format-specific attenuation rules are documented in [ADR-023](docs/decisions/ADR-023-explicit-resource-scope-authority.md).
- **Explicit about what it doesn't do.** Scope-level governance can't catch semantic misuse — if an allowed tool is used on an allowed resource for the wrong reason, that's a different layer's job.
- **MIT licensed.** The research foundation (the Silence Theorem, the protocol formalism, the benchmark methodology) will be linked from this repo when the paper's public identifier is assigned. Articles in this repo paraphrase the research in original prose; they do not reproduce paper content.
Expand All @@ -229,6 +238,7 @@ This repo currently includes:
- current status and what is still being resolved
- public v0.1 specs for mission declarations, execution receipts, verifier contracts, conformance profiles, and related protocol surfaces, plus a draft-10-pinned DRP mapping and executable profile with RFC 8785/P-256 emit, external-trust full-chain and critical-bound verification, and a portable seven-scenario implementation self-test bundle/report (not an IETF or independent interoperability claim), the v0.2 Execution Receipt hardening profile with versioned RFC 8785 payloads and legacy verification, a transparency-anchor sidecar profile with offline-verifiable Rekor v1 and separately keyed self-hosted proofs, a receiver-attestation profile with a two-key offline verifier and MCP shim fixture, a full offline-verification bundle/profile with redacted CLI/JSON/static HTML explorer reports, and a verified-receipt governance telemetry profile with redacted JSONL plus OTLP/HTTP trace/log export
- Python governance runtime under `python/`; Go eBPF/K8s packages and version-dispatched JWT AAT credential attenuation under `go/`: the existing draft-00 DG v0.1 contract plus the explicit `ardur.dg.aat-draft-01.v0.2` profile with chain-position roles, audience-bound PoP, fresh per-hop holder keys, approval gates, and a deterministic self-test fixture (CWT and independent interoperability are not claimed)
- optional Python typed dangerous-action risk budgets with authenticated schema/extractor digests, signed attenuation, fsync-backed multi-scope reservations, explicit executor outcomes, and privacy-bounded signed receipts; the existing DRP profile does not project this extension
- a Linux governance-overhead harness with a closed report schema, PR smoke workflow, manual stress profile, owner-only artifacts, and an opt-in shell-free paired-sensor mode
- the Ardur Personal Hub service and CLI under `python/vibap/` (`ardur hub`, `ardur setup`, `ardur status`, `ardur protect claude-code`, `ardur profile init`, `ardur doctor-claude-code`, full offline evidence verification, verified redacted receipt telemetry export, receiver-envelope verification, detached normalized/Tetragon/Falco runtime-evidence correlation, static non-executing MCP/tool-server preflight, and no-key DRP/receiver/offline-verification fixtures), plus deterministic `ardur-drp-fixtures` and `ardur-policy-conformance` runners
- the Claude Code plugin under `plugins/claude-code/` with `PreToolUse`, `PostToolUse`, `SubagentStart`, and `SubagentStop` hooks emitting signed receipts
Expand Down
103 changes: 103 additions & 0 deletions docs/decisions/ADR-026-typed-dangerous-action-risk-budgets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# ADR-026: Typed dangerous-action risk budgets

**Status:** Accepted

**Date:** 2026-07-14

## Context

Tool allowlists and total call counts limit which operations an agent may
invoke and how often, but not the impact of one permitted invocation. One
allowed deletion could address one object or one million; one allowed send
could remain private or disclose regulated data publicly. Prompt-based risk
labels and MCP annotations are caller/server assertions, not a trustworthy
pre-action enforcement input.

Impact caps must also survive concurrent agents and delegated sessions. A
read-check-write counter per process lets siblings simultaneously observe the
same remaining authority. Charging only after execution allows irreversible
actions to oversubscribe before the runtime records them. Automatically
returning a timed-out charge can race an executor that is still running.

Primary inputs to the decision were RFC 8785, JSON Schema 2020-12, the current
MCP tools specification, OAuth Attenuating Agent Tokens draft-01, Agent
Delegation Receipt Protocol draft-10, and Python's `flock`/`os.replace`
contracts. See
the [risk-budget reference](../reference/risk-budgets.md#protocol-boundary-and-primary-sources).

## Decision

1. Mission Passports may carry an optional versioned `risk_budget` claim.
Absence preserves existing runtime behavior.
2. Each governed tool is bound to a trusted `ToolRiskContract` digest over the
authenticated tool name, JSON Schema, and a closed declarative extractor
program. The registry freezes at proxy startup.
3. Contracts derive mandatory typed facts locally. Numeric facts are additive;
categorical facts use closed ordered vocabularies. Missing, unknown,
malformed, negative, non-integral, oversized, or schema-invalid input fails
closed.
4. Signed policy contains per-action fact caps plus numeric session, agent, and
lineage ceilings. Delegation preserves lineage and contract/fact identity
while allowing only tool subsets and lower/equal caps. The first governed
call freezes a normalized session snapshot; reservations retain the exact
accounting ceilings used at authorization. Tools removed during delegation
also remove numeric ceilings that no retained tool references.
5. `evaluate_tool_call` atomically reserves all numeric facts across all three
scopes before ordinary policy can return `PERMIT`. A unique request ID is
mandatory for governed actions; active or terminal replay cannot re-permit.
6. The executor explicitly records `committed` once execution may have started
or `released` only when it did not start. Session finalization refuses active
or quarantined reservations and resolved lifecycle events whose receipts are
not yet durable. Exceptions never imply release, and quarantined reservations
may only reconcile as committed.
7. Stale active reservations quarantine while retaining authority. Explicit
reconciliation is preferred. After expiry and a bounded quarantine window,
pruning conservatively archives uncertainty as spent. Terminal compaction
preserves request/fingerprint tombstones for a bounded replay window and
never refunds committed authority.
8. Action and lifecycle receipts contain only a canonical fact digest, bounded
remaining counters, and stable denial classes. Raw facts, request IDs,
targets, paths, URLs, and secrets are excluded. Ledger lifecycle state and
persisted session receipt material form a retry-safe outbox; compaction does
not discard a terminal record before receipt delivery.
9. The existing DRP profile does not project `risk_budget`. Emitters must fail
closed instead of silently dropping the extension.

## Consequences

- Configured dangerous tools can enforce per-action and cumulative impact caps
before dispatch, including across processes and delegated agents.
- The proxy adds JSON Schema validation and an fsync-backed reservation plus
outcome transaction for each governed action. All lineages serialize on one
global ledger lock so agent ceilings remain atomic across lineage boundaries;
this favors safety over high-throughput authorization.
- An executor crash conservatively consumes/quarantines authority until an
operator or recovery controller explicitly reconciles it, or bounded
post-expiry maintenance archives the uncertainty as spent.
- Contract authors become part of the trusted computing base: a schema or
extractor that understates impact cannot be repaired by the ledger.
- The feature does not discover hidden side effects, validate the truth of
tool arguments, classify semantic intent, or govern calls that bypass the
configured adapter.
- The runtime adds no network dependency or cloud-service cost, but durable
receipt/ledger/tombstone storage and high-throughput lineage contention are
operating costs. Receipt-sink failure intentionally retains outbox records
and can exhaust the bounded ledger rather than lose audit evidence.

## Alternatives considered

- **Trust a caller-provided risk object.** Rejected because the actor seeking
authorization could choose its own impact label.
- **Use MCP annotations directly.** Rejected because the MCP specification
treats annotations as untrusted unless the server is trusted; annotations
also do not provide cumulative atomic accounting.
- **Charge after tool completion.** Rejected because concurrent irreversible
actions could all pass before any charge was recorded.
- **Return stale reservations automatically.** Rejected because timeout does
not prove the executor stopped.
- **Use the existing delegation-call ledger.** Rejected because its single
call-count dimension cannot atomically conserve multiple typed facts across
session, agent, and lineage scopes.
- **Store raw facts and identifiers for easier debugging.** Rejected because
targets, destinations, and secret classifications are sensitive audit data;
digests and bounded counters are sufficient for enforcement evidence.
7 changes: 6 additions & 1 deletion docs/decisions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,14 @@ ADRs are migrated from the private research repo with the two-pass cleanup appli
| 022 | [SPIFFE mTLS identity for operator telemetry](./ADR-022-operator-telemetry-spiffe-mtls.md) | Accepted | 2026-07-11 |
| 023 | [Explicit resource-scope authority](./ADR-023-explicit-resource-scope-authority.md) | Accepted | 2026-07-12 |
| 024 | [Self-asserted owner identity assurance](./ADR-024-self-asserted-owner-identity-assurance.md) | Accepted | 2026-07-12 |
| 026 | [Typed dangerous-action risk budgets](./ADR-026-typed-dangerous-action-risk-budgets.md) | Accepted | 2026-07-14 |

ADR-025 is reserved by a concurrently reviewed spend-gate decision. Parallel
issue branches may therefore show ADR-026 before ADR-025 lands in `dev`; the
reservation avoids a guaranteed rename conflict between focused changes.

## Conventions

- **Status**: `Proposed`, `Accepted`, `Superseded by ADR-NNN`, `Deprecated`. A `Proposed` status means the design is documented but not yet landed in code; it can still change.
- **Numbering**: sequential, no gaps. The formal ADR-file practice began at ADR-015 in the private research repo; earlier design decisions were captured in running decision logs rather than individual ADR files. Public numbering preserves the original sequence so cross-references stay stable.
- **Numbering**: sequential with no gaps on `dev`. Concurrent branches may reserve the next number when the reservation is documented; an abandoned reservation must be reclaimed or later ADRs renumbered before merge. The formal ADR-file practice began at ADR-015 in the private research repo; earlier design decisions were captured in running decision logs rather than individual ADR files. Public numbering preserves the original sequence so cross-references stay stable.
- **Scope**: ADRs record decisions about the protocol (MCEP), the runtime (Ardur), and deployment shapes. They do not duplicate spec content — the v0.1 specs live in [`docs/specs/`](../specs/).
4 changes: 4 additions & 0 deletions docs/reference/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ walkthroughs see [`../guides/`](../guides/); for protocol semantics see
- [Advisory AI Controls](advisory-ai-controls.md) — semantic-judge and
behavioral-fingerprint defaults, non-authoritative status, failure policy,
cost, and integration requirements
- [Typed Dangerous-Action Risk Budgets](risk-budgets.md) — authenticated tool
contracts, signed impact caps, atomic session/agent/lineage accounting,
executor outcomes, receipts, and crash recovery
- [Agent Recognition Evaluation](agent-recognition-evaluation.md) — versioned
maintained corpus, deterministic metrics, Wilson intervals, CI thresholds,
and claim boundaries
Expand All @@ -37,5 +40,6 @@ These pages mirror the public source. When the underlying surface changes
`go/pkg/kernelcapture/testdata/agent_recognition_corpus.json`,
`go/pkg/kernelcapture/testdata/agent_recognition_thresholds.json`,
`python/vibap/semantic_judge.py`, `python/vibap/behavioral_fingerprint.py`,
`python/vibap/risk_budget.py`,
`Dockerfile.proxy`, or its release workflow), update the matching page in the
same change. They are deliberately mechanical so the diff is easy to review.
Loading
Loading