diff --git a/README.md b/README.md index c23dd19..85b18d3 100644 --- a/README.md +++ b/README.md @@ -2,54 +2,274 @@ **Proof that the rule you shipped is actually supported by the source.** -AuthContract is CI for the rules your agents act on. +AuthContract turns an institutional rule into a canonical, testable artifact — then keeps that artifact bound to the actions your software takes, and issues evidence anyone can independently recompute. -It checks whether a rule in your code is actually supported by its source, catches behavior changes in pull requests, and keeps the rule that passed connected to the actions your agent takes after you ship. +```text +source → rule → PR → check → merge → runtime → proof +``` -If the source does not support the rule, the check fails. +**Implementation status:** experimental reference implementation (TRL 4). The current code tests the mechanical trust chain for one synthetic banking specimen; the automated natural-language source-to-rule comparison shown in the examples below is target behavior and is not yet implemented end to end. Nothing here is production-ready, audited, or certified. See [What is *not* implemented](#what-is-not-implemented) — read it before forming expectations — and [Current status](#current-status) for the full boundary. -If the source is ambiguous or incomplete, AuthContract does not guess. +--- -If the rule passes, AuthContract preserves the evidence needed to show what source supported it, what behavior was tested, what version was shipped, and what rule governed a later action. +## The problem this addresses -```text -source → rule → PR → check → merge → runtime → proof +Software increasingly does consequential things on an institution's behalf: sends payments, approves transactions, changes customer state. Developers translate requirements into executable rules — and a rule can look reasonable, pass ordinary unit tests, and still introduce a threshold or exception the source never established. + +Ordinary tests ask *did the code run correctly?* AuthContract adds a second question: **which rule authorized this action, what version applied, and can anyone else verify that independently?** + +--- + +## Quick start + +Requires **Python 3.10+** and `git`. No credentials, no services, no network beyond the clone and dependency install. + +```bash +git clone https://github.com/veraxis-protocol/AuthContract.git +cd AuthContract +python3 -m venv .venv && source .venv/bin/activate +pip install -e ".[test]" +``` + +Confirm the install: + +```bash +pytest -q # expect: 342 passed +``` + +> Not on PyPI. Install from source, as above. + +--- + +## A successful end-to-end run + +This exercises the whole implemented chain — contract → validation and digest binding → projection → runtime facts → authorization → receipt: + +```bash +authcontract run-specimen \ + fixtures/banking_payment_specimen.json \ + fixtures/actions/send_payment_valid.json \ + fixtures/runtime/facts_valid.json \ + --execution-result SIMULATED_SUCCESS +``` + +Actual output (reformatted for reading; the CLI emits one line of JSON): + +```json +{ + "status": "PASS", + "decision": "ALLOW", + "reason_code": "OK", + "artifact": "banking_payment_specimen.json", + "action_file": "send_payment_valid.json", + "facts_file": "facts_valid.json", + "receipt": { + "activation_id": "act:banking-specimen-001:v1", + "contract_digest": "sha256:d94a65607e756a2d4e3c92fc1de4a23d7cf614dd1dbe8f3fd20fe6e459c9b842", + "projection_digest": "sha256:c7f6dccb5f3f6b7c70b305e838ad87877805d19bd6d3e225f41a40151aa5659b", + "runtime_fact_set_digest": "sha256:57fe990d8320793f92894d31d16d55598e8f30cb63d227bd35e9b2bb3daaab09", + "exact_action_digest": "sha256:55bc4dd36b705f58a123c110bc4d5b398182524cb51c4fae499c2c1455d6470f", + "admission_digest": "sha256:1c631041f126de51afeeb5838d47501616083cd31d4ad0ca9691c71a74ec2a68", + "decision": "ALLOW", + "execution_result": "SIMULATED_SUCCESS", + "decision_time": "2026-08-23T00:10:00+00:00", + "receipt_digest": "sha256:2cfa754d40ed2b9df4a4be7dcc0082bbc1097e2b6a88cb73ea9f9af950bc5a9a" + } +} +``` + +Exit code `0`. The action was permitted, and you now hold a receipt describing exactly what authorized it. + +### Independently verify that receipt + +The receipt is only worth something if someone else can check it without trusting you. It trusts no field in the receipt itself. + +Re-run the evidence — independently recompute the receipt bindings from the raw artifact, action, and fact inputs and compare: + +```bash +authcontract verify-receipt \ + fixtures/runtime/receipt_valid.json \ + fixtures/banking_payment_specimen.json \ + fixtures/actions/send_payment_valid.json \ + fixtures/runtime/facts_valid.json +``` + +```json +{"status": "PASS", "reason_code": "OK", "receipt": "receipt_valid.json", + "artifact": "banking_payment_specimen.json", "action_file": "send_payment_valid.json", + "facts_file": "facts_valid.json"} ``` +Exit code `0`. Tamper with any bound field and this refuses — see below. + +--- + +## A deliberate refusal + +**A refusal is a successful demonstration.** The system is designed to fail closed, and you should see it do so. + +### Refusal 1 — an action outside the declared rule + +The specimen declares exactly one mediated action, `send_payment`. Here we propose `issue_refund`: + +```bash +authcontract run-specimen \ + fixtures/banking_payment_specimen.json \ + fixtures/actions/send_payment_unknown_action_type.json \ + fixtures/runtime/facts_valid.json \ + --execution-result SIMULATED_SUCCESS +``` + +```json +{"status": "REFUSED", "reason_code": "RUN_UNCLASSIFIED_ACTION", + "message": "RUN_UNCLASSIFIED_ACTION: 'issue_refund' is not in the closed mediated-action universe for this projection"} +``` + +Exit code `1`. **In plain language:** the rule never granted authority to issue refunds, so AuthContract refuses rather than improvising. An undeclared action is not a permitted action. + +### Refusal 2 — a runtime fact too old to rely on + +The contract requires a secondary approval no older than 15 minutes. This bundle supplies a stale one: + +```bash +authcontract run-specimen \ + fixtures/banking_payment_specimen.json \ + fixtures/actions/send_payment_valid.json \ + fixtures/runtime/facts_stale.json \ + --execution-result SIMULATED_SUCCESS +``` + +```json +{"status": "REFUSED", "reason_code": "RUN_FACT_STALE", + "message": "RUN_FACT_STALE: secondary_approval.present is older than 0:15:00"} +``` + +Exit code `1`. **In plain language:** the approval existed, but not recently enough to satisfy the rule. Stale evidence is not treated as current permission. + +Note that **no receipt is issued on refusal** — a refused decision never produces evidence claiming a decision was made. + +--- + +## What the result means + +| Field | Meaning | +|---|---| +| `status` | `PASS` or `REFUSED` — the outcome of the check | +| `decision` | `ALLOW` on a permitted action; absent on refusal | +| `reason_code` | Stable machine-facing identifier (e.g. `RUN_FACT_STALE`). Safe to branch on | +| `message` | Human-readable explanation of a refusal | +| `contract_digest` | Canonical identity (RFC 8785 JCS + SHA-256) of the rule that applied | +| `projection_digest` | Identity of the action domain the rule was projected into | +| `runtime_fact_set_digest` | Identity of the exact facts relied on | +| `exact_action_digest` | Identity of the exact action authorized | +| `admission_digest` | Identity of the admission state bound to the decision | +| `decision_time` | Bound to the fact bundle's declared `now`, **not** wall-clock — so replays are byte-identical | +| `receipt_digest` | Identity of the receipt as a whole | + +Every digest is recomputable from raw inputs. That is what makes the receipt checkable by a third party rather than merely assertable by you. + +Exit codes: `0` pass · `1` refusal. + --- -## Why AuthContract? +## How to integrate it today + +Only interfaces that **actually exist** are listed. Each was executed while writing this section. + +### CLI — available -Agentic software is increasingly allowed to do consequential things: +``` +authcontract verify Verify one rule artifact's canonical identity +authcontract project Project a rule into its declared runtime action domain +authcontract check-action Check an action against a rule's declared domain +authcontract git-gate Check a CI result against the version that would actually merge +authcontract run-specimen Run the rule/fact/action check end to end; issue a receipt on PASS +authcontract verify-receipt Re-run the evidence: recompute the receipt bindings and compare +``` + +Structured JSON on stdout, non-zero exit on refusal — so shell and CI integration is straightforward. -- send payments -- approve transactions -- change customer state -- execute operational workflows -- make decisions from regulated or contractual requirements -- act on behalf of institutions +### Python library — available -Developers turn requirements into executable rules. +```python +import json +from authcontract.veip import run_specimen, verify_receipt + +artifact = json.load(open("fixtures/banking_payment_specimen.json")) +action = json.load(open("fixtures/actions/send_payment_valid.json")) +facts = json.load(open("fixtures/runtime/facts_valid.json")) -That creates a simple problem: +result = run_specimen(artifact, action, facts, execution_result="SIMULATED_SUCCESS") +print(result.decision, result.reason_code) # ALLOW OK -**How do you know the rule in the code actually says what the source says?** +if result.decision == "ALLOW": + check = verify_receipt(result.receipt, artifact, action, facts) + print(check.status, check.reason_code) # PASS OK +``` -A rule can look reasonable, pass ordinary unit tests, and still introduce a threshold, exception, permission, or interpretation that the source never established. +`run_specimen` returns a `RunResult` (`decision`, `reason_code`, `message`, `receipt`) and does **not** raise on an ordinary refusal — refusals are return values, not exceptions. -AuthContract makes that relationship testable. +### GitHub merge-gate workflow — available -Instead of asking: +The repository includes `.github/workflows/authcontract-gate.yml`, which runs `authcontract git-gate` on pull requests. It re-resolves the base ref live and proves the evaluated commit really contains both the base and the PR head, so a stale or isolated-head result cannot pass the gate's own check. -> Did the code run correctly? +**The workflow's presence does not mean GitHub requires it.** Whether a check is *enforced* — that is, whether branch protection blocks a merge when it fails — is a separate GitHub repository-configuration concern, set in branch protection or rulesets rather than in the workflow file. Do not infer enforcement from the fact that this workflow exists; verify the repository's own branch-protection settings if you need to know what is actually required to merge. -AuthContract also asks: +### Runtime invocation and receipt verification — available -> Is the rule the code is running actually supported by the source? +`run-specimen` and `verify-receipt`, via CLI or library, as shown above. + +### Not available + +- **No published package.** Not on PyPI; install from source. +- **No HTTP/gRPC service.** In-process library and CLI only. +- **No persistence layer.** Stateless over in-memory inputs; receipts are not stored for you. +- **No multi-contract registry.** One artifact is evaluated per invocation; there is no cross-contract selection. +- **No replay protection.** Replayed identical requests produce identical receipts — this is determinism, not protection. No nonce or single-use semantics exist. --- -**Implementation status:** experimental reference implementation. The current code tests the mechanical trust chain for one synthetic banking specimen; the automated natural-language source-to-rule comparison shown in the examples below is target behavior and is not yet implemented end to end. See [Current status](#current-status) for the full boundary. +## What is *not* implemented + +Read this before forming expectations. The worked conceptual examples later in this document describe **target** behavior. + +- **Automated natural-language source-to-rule comparison is not implemented end to end.** This is the product's defining target capability and does not exist yet. Rules are authored as `.ac` artifacts today. +- Not production-ready. Not audited. Not security-certified. Not regulatory-approved. No formal proof. +- No universal policy correctness, general legal correctness, or arbitrary-domain compatibility. +- No production-grade institutional identity or PKI. +- No concurrency, distribution, or measured multi-core scaling. +- Evidence scope is one synthetic banking specimen family — not a general solution. + +Measured evidence and its limits: [`docs/BENCHMARKS.md`](docs/BENCHMARKS.md) · maturity assessment: [`docs/TRL-ASSESSMENT.md`](docs/TRL-ASSESSMENT.md). + +--- + +## License + +**No license is currently declared.** This repository contains no `LICENSE` file and `pyproject.toml` declares no license field. Absent an explicit grant, default copyright applies and no usage rights are conferred — so treat this as source-available for evaluation and reading, not as open source. If you need licensed use, ask the repository owner. + +--- + +## Where to go next + +| If you want to… | Go to | +|---|---| +| See measured performance and correctness evidence | [`docs/BENCHMARKS.md`](docs/BENCHMARKS.md) | +| Reproduce the benchmarks yourself | [`benchmarks/README.md`](benchmarks/README.md) | +| Understand current maturity honestly | [`docs/TRL-ASSESSMENT.md`](docs/TRL-ASSESSMENT.md) | +| See what is planned and why | [`docs/ROADMAP.md`](docs/ROADMAP.md) | +| Validate a clean clone yourself | [`docs/CLEANROOM-VALIDATION-RUNBOOK.md`](docs/CLEANROOM-VALIDATION-RUNBOOK.md) | +| See how this repository was usability-tested | [`docs/REPOSITORY-USABILITY.md`](docs/REPOSITORY-USABILITY.md) | +| Understand the terminology | [`docs/DEVELOPER-LANGUAGE.md`](docs/DEVELOPER-LANGUAGE.md) | +| See how this compares to other systems | [`docs/SOTA.md`](docs/SOTA.md) | +| Understand the full conceptual model | keep reading below | + +--- +--- + +# How it works — the full model + +Everything above is runnable today. Everything below explains the model the implementation is built toward, including worked examples of the **target** source-to-rule comparison that is not yet implemented end to end. --- @@ -415,84 +635,6 @@ The `.ac` artifact exists so that the meaning being reviewed does not disappear --- -## Quick start - -Install the current reference implementation: - -```bash -pip install -e ".[test]" -``` - -Run the test suite: - -```bash -pytest -q -``` - -Verify an AuthContract fixture: - -```bash -authcontract verify fixtures/valid.json -``` - -Project the banking specimen into its declared runtime domain: - -```bash -authcontract project fixtures/banking_payment_specimen.json -``` - -Check an action: - -```bash -authcontract check-action \ - fixtures/banking_payment_specimen.json \ - fixtures/actions/send_payment_valid.json -``` - -Run the check that ties a rule, its runtime facts, and an action together, and get a proof receipt back on PASS: - -```bash -authcontract run-specimen \ - fixtures/banking_payment_specimen.json \ - fixtures/actions/send_payment_valid.json \ - fixtures/runtime/facts_valid.json \ - --execution-result SIMULATED_SUCCESS -``` - -Re-run the evidence — independently recompute the receipt bindings from the raw artifact, action, and fact inputs and compare: - -```bash -authcontract verify-receipt \ - fixtures/runtime/receipt_valid.json \ - fixtures/banking_payment_specimen.json \ - fixtures/actions/send_payment_valid.json \ - fixtures/runtime/facts_valid.json -``` - -The CLI prints structured JSON and exits non-zero on refusal. - -Example: - -```json -{ - "status": "PASS", - "reason_code": "OK" -} -``` - -or: - -```json -{ - "status": "REFUSED", - "reason_code": "RUN_UNCLASSIFIED_ACTION" -} -``` - -Reason codes are stable machine-facing identifiers. - -The surrounding developer experience should explain what they mean in plain language. - --- ## Example: action outside the rule diff --git a/docs/REPOSITORY-USABILITY.md b/docs/REPOSITORY-USABILITY.md new file mode 100644 index 0000000..12f195e --- /dev/null +++ b/docs/REPOSITORY-USABILITY.md @@ -0,0 +1,166 @@ +# Repository usability test (AC-036) + +A clean-room test of the repository as a **product surface**: can an unfamiliar +developer, following only `README.md`, understand what this is and get a +meaningful result quickly? + +Performed against commit `35e2bef2060d113cefaf13f8c1da2a08c47559ac` from a +genuinely fresh `git clone` — no reused virtualenv, no undocumented local +state, no commands invented outside the README. + +This document records observations. **It does not self-certify Gate E**; that +adjudication belongs to the Engineering Lead. + +--- + +## Environment + +| | | +|---|---| +| OS | Linux 6.18.44 x86_64 | +| Python | 3.11.15 (README requires ≥3.10) | +| git | 2.43.0 | +| Network | clone + PyPI dependency install only | +| Credentials | none required, none supplied | + +--- + +## Results + +### A. ~30-second comprehension — PASS + +The first screen (11 lines before the first `---`) delivers, in order: a +one-sentence definition, the `source → rule → PR → check → merge → runtime → +proof` chain, and an implementation-status note stating TRL 4, the +one-synthetic-specimen scope, and — unambiguously — that automated +natural-language source-to-rule comparison **is not implemented end to end**. + +A reader who stops after 30 seconds leaves with a correct impression rather +than an inflated one. That was the specific failure mode before this change: +the prior README opened with ~400 lines of worked examples of the *target* +capability, so the most prominent material described behavior that does not +exist yet. + +### B. Install + first meaningful success ≤ 5 minutes — PASS (~35 s) + +| Step | Elapsed | Cumulative | +|---|---:|---:| +| `git clone` + `git checkout` | 1 s | 1 s | +| `python3 -m venv` + `pip install -e ".[test]"` | 11 s | 12 s | +| `pytest -q` → **342 passed** | 11 s | 23 s | +| `authcontract run-specimen …` → PASS/ALLOW + receipt | <1 s | **~24 s** | + +Roughly **24 seconds** to a first meaningful end-to-end success, against a +five-minute budget. Even allowing for a cold PyPI cache and slower hardware, +the margin is large. + +No undocumented state was required. Every command came from the README. + +### C. Deliberate refusal reproducible — PASS (both cases) + +**Refusal 1 — undeclared action.** Exit code `1`: + +```json +{"status": "REFUSED", "reason_code": "RUN_UNCLASSIFIED_ACTION", + "message": "RUN_UNCLASSIFIED_ACTION: 'issue_refund' is not in the closed mediated-action universe for this projection"} +``` + +**Refusal 2 — stale runtime fact.** Exit code `1`: + +```json +{"status": "REFUSED", "reason_code": "RUN_FACT_STALE", + "message": "RUN_FACT_STALE: secondary_approval.present is older than 0:15:00"} +``` + +Both match the README byte-for-byte in `status`, `reason_code`, and `message`. +Both correctly issue **no receipt** — a refused decision does not emit evidence +claiming a decision was made. + +### D. Receipt verification reproducible — PASS + +```json +{"status": "PASS", "reason_code": "OK", "receipt": "receipt_valid.json", …} +``` + +Exit code `0`, matching the README. Verification recomputes every bound value +from the raw artifact, action, and fact files and trusts no field in the +receipt itself. + +### E. Integration path findable and understandable — PASS + +`## How to integrate it today` names four available interfaces (CLI, Python +library, GitHub merge gate, runtime invocation/receipt verification) and one +explicit **Not available** list. + +The Python example was executed verbatim from the README in the clean clone: + +``` +ALLOW OK +PASS OK +``` + +The "Not available" list is as load-bearing as the available one — it states +plainly that there is no published package, no HTTP/gRPC service, no +persistence layer, no multi-contract registry, and no replay protection. + +--- + +## Findings + +**U1 — No license is declared.** There is no `LICENSE` file and +`pyproject.toml` declares no license field, so default copyright applies and +no usage rights are granted. This is now stated truthfully in the README +rather than left for a reader to discover. **Not resolved here:** choosing a +license is an owner decision with legal effect, not an executor decision. + +**U2 — The README is long (1,215 lines).** The runnable product surface is the +first ~250; the remainder is conceptual material describing target behavior, +now explicitly fenced under "How it works — the full model". A future pass +could move that body into `docs/`, but doing so here would have exceeded +AC-036's product-surface scope and risked disturbing accepted +developer-language content. + +**U3 — Documentation guards constrain README structure, correctly.** The +AC-021/AC-021A/AC-021B tests pin specific README sentences and one structural +invariant (deep ontology vocabulary must sit below `## Quick start`). The first +draft of this restructure broke three of them; all were restored, none was +weakened. Anyone editing the README should expect these guards to fire — they +are a feature, and they caught genuine claim-ceiling drift here. + +**U5 — The benchmark's DUT guard treats `README.md` as a protected surface, so +documentation-only changes trip it.** Running `benchmarks/run_benchmarks.py` on +this branch exits `2` with `DUT DRIFT: 1 file(s) differ` — that file being +`README.md`. This is the guard behaving **correctly**: it refuses to publish +results claiming to describe `e4e1a975` when any declared device-under-test +path differs, and `README.md` is in that list because AC-035A protected it. + +It was deliberately **not** worked around. Weakening the guard, or trimming +`README.md` out of `DUT_PATHS`, to make a documentation PR go green would +defeat the property the guard exists to provide. + +No behavioral regression is possible from this change: `authcontract/`, +`tests/`, `fixtures/`, `.github/`, `pyproject.toml`, and `benchmarks/` are +**byte-identical** to merged main `4c90aa79dd922888a8beb3aa9d886c44ecc28c7c`, +where the full benchmark last ran 7/7 end-to-end and 38/38 adversarial with +exit `0`. The only file changed against that commit is `README.md`. + +A future refinement could split `DUT_PATHS` into behavioral and documentary +sets, so doc changes are reported without blocking. That is a benchmark-design +decision, out of scope for AC-036. + +**U4 — CLI output is single-line JSON.** Readable via `jq` or `python3 -m +json.tool`, but not pretty-printed natively. The README reflows it for +readability and says so explicitly, rather than implying prettier output than +the CLI produces. + +--- + +## Not tested + +- Windows and macOS. Linux only. +- Python 3.10 and 3.12 were not exercised here, though repository CI covers both. +- Cold-cache install timing on constrained networks. +- Comprehension by an actual unfamiliar human. This test was performed by the + same party that wrote the README, which is a real limitation: it can + establish that the documented path *works*, but not that the explanation + *lands* for a genuine newcomer. Independent review remains the gap.