Metadata-Version: 2.4
Name: warrant-verify
Version: 0.11.0
Summary: Verify what an AI agent decided — signed, hash-addressed decision records with reasons you can re-run on your own machine.
Author: s0fractal
License: MIT
Project-URL: Homepage, https://github.com/s0fractal/warrant
Project-URL: Specification, https://github.com/s0fractal/warrant/blob/master/SPEC.md
Project-URL: Evidence Pack format, https://github.com/s0fractal/warrant/blob/master/EVIDENCE-PACK.md
Keywords: ai-agents,agent-provenance,audit-trail,eu-ai-act,verifiable,sigstore,decision-record,compliance
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Legal Industry
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Security :: Cryptography
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: cryptography>=41
Dynamic: license-file

# Warrant

**When a machine says something was allowed, can you check why — without
trusting the machine that allowed it?**

Warrant is a small, signed, content-addressed decision record. It names what was
decided, the exact policy bytes in force, the reasons and evidence, the actor,
and the decisions that came before it. An executable reason can be re-run by the
reader instead of trusted as a log entry.

```json
{
  "decision": "reject",
  "subject": {"hash": "d5cf37…", "note": "PR-42"},
  "under": ["cb3a0a…"],
  "because": [
    {"kind": "check", "check": "05d234…", "runtime": "cmd@v1",
     "verdict": "fail", "transcript": "9dc0c3…"},
    {"kind": "prose", "text": "policy clause 1: coverage drops"}
  ],
  "evidence": ["9dc0c3…"],
  "actor": {"id": "agent-b@vendor2"},
  "prior": ["00f79f…"],
  "ts": 1751677200
}
```

The canonical body hash is the record's identity. Changing the decision,
policy reference, reason, evidence or prior edge changes that identity. The
signature says which key signed the body; whether that key belongs to the named
actor is a separate trust-configuration question. A rejection is a durable
record, not an absence.

Warrant is not an agent framework, blockchain or observability system. It is one
file format and five filing verbs, designed to be boring.

The format is written up in *The Reason Runs Again*
([10.5281/zenodo.22172098](https://doi.org/10.5281/zenodo.22172098), paper
v1.0.0, 2026-08-30 — deposited, not peer reviewed; see
[`papers/`](papers/README.md)).

## Try it

**[Open the browser lab](https://s0fractal.github.io/warrant/)** — inspect a signed
repository example, re-run its portable reason locally, change its bytes, or
open your own store. The browser runs the Rust core compiled to WebAssembly;
the report keeps the same schema as the CLI. Its local limits and build identity
are shown on the page. See [`web/README.md`](web/README.md) for implementation
and deployment details.

```bash
pipx install warrant-verify   # or: pip install warrant-verify

warrant init
warrant keygen --out me.key
printf 'demo diff\n' > diff.patch
printf 'clause 1: no coverage drop\n' > policy.txt
printf '#!/bin/sh\necho coverage 71%%; exit 1\n' > check.sh && chmod +x check.sh

POL=$(warrant policy add policy.txt)
P=$(warrant propose --subject diff.patch --under "$POL" \
      --reason "utility functions needed" --actor me@host --key me.key)
./check.sh > run1.txt
R=$(warrant reject "$P" --check check.sh --verdict fail --transcript run1.txt \
      --reason "clause 1: coverage drop" --actor me@host --key me.key)
printf '#!/bin/sh\necho coverage 84%%; exit 0\n' > check.sh
./check.sh > run2.txt
A=$(warrant accept "$R" --check check.sh --verdict pass --transcript run2.txt \
      --actor me@host --key me.key)

warrant why "$A"
warrant verify
```

Every file used above is created above. The final warnings say that the key-to-
actor binding is unverified until a trust configuration supplies that authority;
a valid signature is not silently promoted into a valid identity claim. Supplying
it is two commands — *you* decide which key speaks for `me@host`, and which
record the chain starts from:

```bash
warrant trust add --actor me@host --key me.key     # writes trust.json
warrant trust root "$P"
warrant verify --settlement --trust-config trust.json   # 0 errors, 0 warnings
warrant verify --settlement --trust-config trust.json --require-bound   # same, and an actor's own unbound key would be an ERR (WRT-018)
```

The transcripts matter. Without them the accept carries no new evidence and no
new outcome fingerprint, and settlement grade reports it as a re-litigation that
cites nothing new — a filer who writes a different word is not a new reason
(THREAT-MODEL SA-1). warrant never runs `check.sh` itself: a `cmd@v1` verdict
is the filer's claim, bound to the script and transcript bytes; only `ski@v1`
reasons are re-executed by the verifier.

The `warrant-verify` distribution installs four commands:

- `warrant` — file, inspect and verify decisions;
- `warrant-mcp-server` — let an MCP client file its own decisions;
- `warrant-mcp` — seal calls passing through another MCP server;
- `warrant-anchor` — batch WarrantIDs into a Merkle anchor.

`ski@v1` reasons replay offline through one bundled Σ-GLYPH Book I v0.5
evaluator, pinned by digest and checked before import. Reserved `ski@v2` is not
admitted by any body version and ships no executable candidate bytes.

The longer walkthrough, including negative controls, is
[`docs/try-this-in-fifteen-minutes.md`](docs/try-this-in-fifteen-minutes.md).

## In Claude Code

A plugin witnesses the permission decisions Claude Code makes about your agent's
tool calls: auto-mode denials, and calls that were asked about and then ran. It
keeps them as signed records in a local store and never takes part in the
decision itself.

```text
/plugin install warrant --marketplace s0fractal/warrant
```

Then `/warrant:decisions` lists what was refused, what ran after asking, and
which asks have no observed outcome. See
[`integrations/claude-code-plugin/`](integrations/claude-code-plugin/README.md)
for what a record proves and what it cannot see.

## Machine boundary

For CI, MCP or an agent framework, verify an initialized store with
`--store-mode --json`:

```sh
warrant --store ./evidence-pack/.warrants verify --store-mode --json | jq -e '.ok'
warrant-go verify --store-mode --json ./evidence-pack/.warrants | jq -e '.ok'
```

```json
{"report":"warrant.verify-report@v0","grade":"base","ok":true,
 "records":3,"errors":0,"warnings":1,
 "findings":[{"level":"WARN","subject":"<WarrantID>","message":"..."}]}
```

`--store-mode` is part of the safe predicate: a missing or uninitialized store
fails closed instead of looking like an empty successful verification. Python
takes the store through the global `--store`; Go takes it positionally.

Consumers may rely on these boundaries:

- `ok == (errors == 0)`, and the error/warning counts equal the corresponding
  findings;
- `warrant.verify-report@v0` is closed: seven top-level fields and
  `{level, subject, message}` findings;
- Python and Go agree on normative fields and `(level, subject)` pairs; `message`
  is human prose and may differ;
- `grade` distinguishes `base` from `settlement`;
- the report is unsigned and carries **no Warrant authority**.

`warrant why <WarrantID> --json` emits the same walk `why` prints as one
`warrant.why@v0` object — every record visited, its status (`verified`,
`verify_failed`, `missing`) and its whole body — with `why`'s exit status. It is
a convenience, **not** a SPEC surface: `verified` there means the name recomputes
and the record carries a valid signature by its actor, not that the key is
trusted; ask `verify --settlement` for that.

The normative contract is [`SPEC.md` §11](SPEC.md#11-verification-report--warrantverify-reportv0),
with [`schemas/verify-report-v0.schema.json`](schemas/verify-report-v0.schema.json)
alongside. `tools/check_release_surface.py` fails CI and publishing when the
documented CLI surface is absent from the checkout or built wheel.

## Re-execute a real specimen

The Air Canada specimen is an authored reconstruction of the decision record
the airline did not have; it is not evidence produced by Air Canada.

```bash
pipx install warrant-verify==0.9.0
curl -LO https://github.com/s0fractal/warrant/releases/download/v0.8.0/air-canada-pack.zip
echo '74b36f1d5c7777ea9a3ee240e32f992483a3cd2c0dda0c7d065229c49f1a8249  air-canada-pack.zip' | shasum -a 256 -c
unzip air-canada-pack.zip
warrant --store air-canada-pack/.warrants verify
warrant --store air-canada-pack/.warrants check b423b6a82c3451bfbd75563b39e6391093a64db57941d9247a61a6c620bd997f
```

The last command re-executes the content-addressed, ATP-bounded reason locally;
it does not merely read the filed verdict. The pinned asset, source specimen and
portable layout are documented in [`demos/air-canada/`](demos/air-canada/),
[`EVIDENCE-PACK.md`](EVIDENCE-PACK.md) and [`PUBLISHING.md`](PUBLISHING.md).

## Integrate it

As a GitHub Actions gate:

```yaml
- uses: s0fractal/warrant@v0.6.0
  with:
    store: ./evidence-pack
    version: '0.6.0'
```

Pin `0.6.0` or newer for domain-separated signatures. The action checks the
required capability, emits the machine report and fails on verification errors;
see [`action.yml`](action.yml).

From an MCP client:

<!-- mcp-name: io.github.s0fractal/warrant -->

```bash
warrant-mcp-server --store /abs/path/.warrants
claude mcp add warrant -- warrant-mcp-server --store /abs/path/.warrants
```

The server exposes filing, store verification and reason inspection with fresh
`ski@v1` replay. It is deliberately distinct from `warrant-mcp`, the sealing
proxy for somebody else's downstream MCP server. See
[`integrations/mcp-server/`](integrations/mcp-server/) and
[`integrations/mcp/`](integrations/mcp/).

## Contract map

| Surface | Authority |
|---|---|
| Record format, canonicalization, signatures, replay, settlement and trust config | [`SPEC.md`](SPEC.md) |
| JSON schemas | [`schemas/`](schemas/) — derivative; SPEC remains normative |
| Portable `.warrants/` bundle | [`EVIDENCE-PACK.md`](EVIDENCE-PACK.md) |
| Writing WPL checks | [`docs/authoring-checks.md`](docs/authoring-checks.md) |
| WPL design boundary | [`docs/policy-language-choice.md`](docs/policy-language-choice.md) |
| Threat model | [`THREAT-MODEL.md`](THREAT-MODEL.md) |
| External implementation contract | [`conformance/`](conformance/) |
| Releases and artifacts | [`PUBLISHING.md`](PUBLISHING.md) and [`CHANGELOG.md`](CHANGELOG.md) |

The Python reference implements the five filing verbs and settlement. Go is an
independent verify/settle implementation. Rust (`impl-rs/`, the `warrant-verify`
crate) is a from-scratch, dependency-free implementation of the whole `warrant`
CLI — filing, settlement grade, `ski@v1` re-execution, Ed25519 signing and
verification — held to the reference's output byte for byte by
`tests/rs_parity.py`. All three were produced within one author/model lineage:
agreement is conformance evidence, not independent custody or adoption.

```bash
python3 impl/warrant.py conformance examples
python3 impl/warrant.py selftest
(cd impl-go && go build -o warrant-go .)
./impl-go/warrant-go conformance examples
(cd impl-rs && cargo build --release)
./impl-rs/target/release/warrant-rs conformance examples
python3 tests/differential.py
```

A fourth JavaScript candidate was created without Warrant implementation source
and reached the complete base grade under a self-certified iterative local-model
experiment. Its settlement grade is unimplemented. This establishes bounded
implementability against that frozen corpus, not external custody, adoption or
correctness beyond it; see [`needs/README.md`](needs/README.md).

To test another implementation without executing ours:

```bash
python3 conformance/run.py --candidate "./your-verifier probe"   # 149 vectors
python3 conformance/run.py --candidate "./your-verifier probe" --self-check
```

The runner checks the candidate's typed answer. 68
of the 149 vectors are MUST-REJECT, and the pack reports `base` and `settlement`
separately. Details and digest verification are in
[`conformance/README.md`](conformance/README.md).

## Breaking signature boundary

Since release `0.6.0`, signatures cover the domain-separated message

```text
"warrant-sig-v1:" || WarrantID_raw
```

rather than the bare WarrantID. There is no dual-accept window. Pre-0.6.0
signatures therefore do not verify under the current protocol. Where the old
signing key is available, migrate only signatures — WarrantIDs and body links do
not change:

```bash
warrant resign --key mykey.key --dry-run
warrant resign --key mykey.key
```

## Settlement

Settlement is a separate verification grade defined by SPEC §5.1, §7, §9 and
§12. It derives key state and active roots from explicit trust configuration,
replays settlement-active reasons, and requires new evidence or a new outcome
fingerprint to reopen a subject. Prose alone never reopens settlement.

```bash
python3 impl/warrant.py verify --settlement --trust-config trust.json
python3 impl/warrant.py settle <settling-wid> candidate-body.json
./impl-go/warrant-go verify --settlement --trust-config trust.json <store>
python3 tests/settlement.py
```

An unusable requested trust configuration fails closed; it does not fall back to
a clean base-grade report. `genesis.json` is advisory unless explicitly pinned
by the verifier.

The only known format consumer is the sibling `sigma-glyph` repository. It is a
working same-author integration, not outside adoption. Published software,
protocol adoption, green CI, self-review and independent validation remain
different claims.

License: MIT.
