Skip to main content

ER1 — Epistemic Receipt v1

An open, offline-verifiable format for proving the constraint state an autonomous AI agent's action was checked against — portable, recomputable in any language. The verdict is recomputed, never trusted.

Two independent verifiers re-deriving the same verdict from the frozen golden vectors, offline

Agents act. Today you can attest who an agent is, whether it was allowed to act, where it ran, and what it did — but nothing attests the constraint state (the active, deterministic "context lineage") the action was actually taken under. ER1 is that missing record: a signed, chained receipt that a stranger can recompute offline, in any language, with no network and no access to the producer — and get the same verdict, or catch the receipt in a lie. What that proves is coherence and integrity, not the empirical truth of the recorded constraints; the boundary is stated precisely in SCOPE_OF_CERTIFICATION.md.

$ pip install er1-verify
$ curl -sO https://raw.githubusercontent.com/Cruxia-Labs/er1-spec/v1.0.1/golden_vectors.json
$ er1-verify golden_vectors.json    # self-test the 12 frozen vectors, offline
VERIFIED ✓  golden_vectors.json:entry[0] name="equals_mismatch_halt"  verdict=HALT (recomputed HALT)  hash=sha256:3c2034ac1c3…  signer=A6EHv_POEL4d… (unpinned)
VERIFIED ✓  golden_vectors.json:entry[1] name="banned_entity_halt"  verdict=HALT (recomputed HALT)  hash=sha256:94196b8debc…  signer=A6EHv_POEL4d… (unpinned)
…and ten more; the exit code is 0 only when every receipt verifies.

(unpinned) is the verifier being precise: without --pubkey it proves a receipt is internally consistent and signed by the key it names — pin a key to also assert whose key that is. Tamper a single byte and it says FAILED ✗. The same vectors verify identically in JavaScript (node er1_verify.mjs golden_vectors.json, no dependencies) and in the browser with the network off (verify/er1_verify.browser.mjs, WebCrypto only) — three implementations on disjoint stacks, showing the format is reproducible, not one tool's output. The Ed25519 key pinned in the vectors is test-only — never sign a production receipt with it.

The sdist is self-proving: pip download er1-verify --no-binary :all: unpacks to the verifier, the frozen vectors, and a conformance suite that needs nothing else — python -m pytest test_conformance.py.

What 1.0.1 changes

1.0.0 called an ordinary receipt malformed: an unpinned install against an operator version constraint (pip install numpy where a satisfies dep:numpy >=2.0 rule exists) has no version to compare, and the verifier refused it — including from a producer that honestly records the gap. As of 1.0.1 that gap is part of the verified claim: the receipt must declare it in coverage.unevaluated_constraints, the verifier recomputes the declared set, and a verified receipt with a declared gap prints it (~ not evaluated: …) instead of a VERIFIED indistinguishable from a fully-checked one. A declaration that recomputation cannot find is refused, always. An UNDECLARED gap is refused exactly where it protects something — a receipt whose recomputed verdict is ALLOW (the silent-skip bypass 1.0.0's refusal existed to prevent); on a recomputed HALT a missing declaration is tolerated, because an unevaluable constraint can never be the conflict and 1.0.0 verified HALT receipts with unevaluated trailing constraints.

Two spellings, two meanings, both deliberate: ==2.0 is the gap-declarable pin; a bare pin (2.0) is the strict must-pin spelling — an unpinned action violates it, exactly as 1.0.0 evaluated it.

Also in the satisfies grammar: the != operator (1.0.0 mis-evaluated it as a bare string), and U+0020 around the version after an operator (>= 2.0 was malformed to 1.0.0). A constraint whose own version does not parse (>=abc, ~=2) stays malformed — that is a defect in the rule, and no action can repair it.

Every receipt 1.0.0 verified still verifies, byte for byte — except receipts that already carried a coverage.unevaluated_constraints field, which had no defined meaning before 1.0.1 and now fails closed if its shape or content does not recompute, and except receipts whose != constraints 1.0.0 mis-evaluated as bare strings, whose verdicts now recompute correctly. The frozen vectors grew from 6 to 12. Full semantics: CONFORMANCE.md §4.

What's in the spec repo

The format lives at github.com/Cruxia-Labs/er1-spec; links below are pinned to the v1.0.1 release.

File What it is
er1.schema.json The receipt wire format (JSON Schema 2020-12).
er1_verify.py The reference verifier — one self-contained file, stdlib + cryptography, zero project imports. This package.
er1_verify.mjs A second verifier in JavaScript — node:crypto only, no npm install. A third runs in the browser: verify/er1_verify.browser.mjs (WebCrypto only).
golden_vectors.json Frozen cross-language conformance vectors (a fixed test key + 12 fully-signed receipts). Also ships in this package's sdist.
CONFORMANCE.md How any implementation (Rust/WASM/Go/TS) proves itself conformant.
SCOPE_OF_CERTIFICATION.md Plain-English statement of exactly what a receipt does and does not certify, and the breach definition.
test_conformance.py · test_cross_language.py The Python verifier accepts every golden receipt and catches tampering; the JS verifier computes byte-identical hashes and the same verdicts.
IMPLEMENTATIONS.md The conformance roster — three verifiers on disjoint stacks (Python, Node, browser WebCrypto), and an open invitation to add an independent one in your language.
KEYS.md Announced signing keys and key tiers. A key adds continuity, never the integrity claim — that stays recomputation.

The constraint set

A receipt records the constraint set an action was checked against — each entry a typed rule on an entity (equals / excludes / satisfies), e.g. env:DEPLOY_TARGET equals staging, lib:boto3 excludes, dep:numpy satisfies >=2.0. The conflict predicate (~30 lines, in CONFORMANCE.md) computes ALLOW or HALT deterministically over that set. Only active, deterministic constraints can gate a HALT; advisory (NL-extracted) constraints are carried but never gate.

The on-wire array is named beliefs[] in the frozen v1 schema for signature compatibility; conceptually it is the constraint / context-lineage set. A future schema revision may rename the field.

What it certifies (and what it does not)

ER1 certifies coherence with the recorded constraints (the verdict follows from them by a fixed, public predicate) and integrity (any tampering breaks the signature). It does not certify the empirical truth of the constraints, anything outside the recorded set, or safety/correctness of the action. Garbage in, certified garbage out. The verified claim is the verdict + the chain hash; receipt_id and created_at are opaque signed metadata. See SCOPE_OF_CERTIFICATION.md.

Conformance

An implementation is conformant iff it reproduces every entry in golden_vectors.json from the same pinned inputs — the canonical-JSON serialization (RFC 8785-inspired; see CONFORMANCE.md), the primitive hashes, the conflict-predicate verdict, and the Ed25519 signatures. The spec repo ships three conformant implementations (Python, Node, browser WebCrypto) that produce byte-identical hashes on the golden vectors; that mutual acceptance across disjoint stacks makes ER1 reproducible — not just one vendor's log. (We reserve the word "standard" for when a party other than us recomputes these vectors; see IMPLEMENTATIONS.md.)

Prior art

The closest related work is Context Lineage Assurance for Non-Human Identities in Critical Multi-Agent Systems (arXiv:2509.18415), which describes signed, hash-chained provenance for agent context verifiable without replaying the full history. ER1 differs by being a productized, open format with an explicit, public decision procedure and a frozen cross-language conformance suite.

License

Released under the Apache License 2.0 (see LICENSE) — chosen for its explicit patent grant, the license an open format's adopters can build on. © 2026 Cruxia.


ER1 is small on purpose: a way to prove the constraint state an action was checked against, recomputable by someone who doesn't trust the producer's tooling. sagrada-linter is the first tool that emits one — and we'd rather the format outlive any one tool, including ours. → Cruxia-Labs

Metadata

Release files for er1-verify 1.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for er1-verify 1.0.1
File Size Uploaded
er1_verify-1.0.1.tar.gz 39.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for er1-verify 1.0.1
File Interpreter ABI Platform
er1_verify-1.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 66.4 kB

Release files / er1_verify-1.0.1.tar.gz

Download URL er1_verify-1.0.1.tar.gz
Size 39.1 kB
Tags Source
SHA-256 checksum
How to use checksums
1caee47e61cb20914887259ba26d9a6037a0ab567844e8fe61e6fd24f6d0c10f
BLAKE2b-256 checksum
How to use checksums
96a826b2946fcad40939cd99f55fa45bda6c3457c808214d2122b8a86cd734de
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.4

Release files / er1_verify-1.0.1-py3-none-any.whl

Download URL er1_verify-1.0.1-py3-none-any.whl
Size 27.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
725546b945ddec7b97c61a566e570c66cc2c93343128942b977fc766b060e390
BLAKE2b-256 checksum
How to use checksums
5e363bf6fe2a68bec6d1329f0ec663a3d75fd38316e797c3a31ba8080771fb2e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.4

Release history Release notifications | RSS feed

This release

1.0.1 This release

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page