Skip to main content
EPI Logo

EPI (Evidence Packaged Infrastructure) — Evidence for AI agents

Record. Seal. Verify offline. The answer is a file.

PyPI Python 3.11+ Version v4.6.1 License: MIT Release Gate

pip install epi-recorder
epi demo --no-browser    # record → seal → verify (no API key)

60-second path · What a .epi is · CLI · Docs & pilot · Standards

How it works: ARCHITECTURE.md — packages, integrity model, and where to read next.


When someone asks what your agent did six months ago,
the answer should be a .epi file — not a dashboard login and a shrug.

epi-recorder turns an AI agent's run into evidence a third party can verify. Not a log you have to trust, but a portable, signed artifact that leaves your platform and declares exactly what it captured — verifiable offline with open tooling. No account and no EPI server: verification is fully offline. When sealing, EPI asks public RFC 3161 timestamp services for a trusted time (only a hash is sent; switch off with EPI_NOTARIZE=0 or use your own with EPI_TSA_URLS).

Open a sealed .epi offline — epi view run.epi

EPI offline viewer showing a sealed .epi evidence file — timeline, integrity, and decision context

Forensic case view of a sealed run. Sample artifact: docs/assets/readme-demo.epi


60-second path

Works without any LLM API key:

# demo.py
from epi_recorder import record, get_current_session

with record("demo.epi", goal="show the golden path"):
    s = get_current_session()
    s.log("tool.call", tool="lookup", id="A-1")
    s.log("tool.response", ok=True, balance=250)
    s.log("decision", action="approve", reason="within limit")
python demo.py
epi verify demo.epi
epi-register demo.epi
epi view demo.epi

record("demo.epi") writes epi-recordings/demo.epi: a bare file name goes into ./epi-recordings/. A path with a folder, such as ./demo.epi or out/demo.epi, is written exactly there. Run the commands above from the same folder as demo.py; epi verify demo.epi finds the file in ./epi-recordings/.

Running python demo.py again never replaces earlier evidence: the second run seals to epi-recordings/demo-2.epi (then demo-3.epi, …) and says so. Set EPI_OVERWRITE=1 to replace demo.epi in place instead.

$ python demo.py
[EPI] 3 steps → epi-recordings/demo.epi  Signed ✓
Step Command What you get
Record + seal python demo.py Signed epi-recordings/demo.epi (secrets redacted by default)
Verify epi verify demo.epi Integrity + signature checks offline
Register epi-register demo.epi Without --service: a local, self-issued SCITT receipt embedded in the .epi (not an independent ledger entry)
View epi view demo.epi Self-contained browser viewer (screenshot above)

Typical first-run verify:

Check Result
Integrity (SHA-256) Valid
Signature (Ed25519) Valid
Identity Often LOCAL / UNKNOWN until you pin trust
Secrets Redacted by default (redact=True)

First-run WARN / LOCAL identity is normal — seal integrity and signature can still pass.
Identity is separate from seal. A customer pins the sender's key, obtained out of band, with epi keys trust <sender-key-file> --name <name> (the sender's .pub, did.json or saved epi keys export line — never the .epi you are checking) or epi keys trust --from-did did:web:vendor.com --name <name>.
Policy / “did the run break our rules?” is separate again: epi analyze — see docs/POLICY.md.

epi-register is a standalone alias for epi scitt register (same options, same result). Without --service <url> it creates a LOCAL self-issued receipt signed by your own key, not an entry in an independent transparency ledger; pass --service for that.

That’s the product. Everything below is optional depth.


With OpenAI

from openai import OpenAI
from epi_recorder import record, wrap_openai

client = wrap_openai(OpenAI())  # needs OPENAI_API_KEY

with record("agent.epi", goal="Answer a user question"):
    client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": "Hello"}],
    )
python agent.py
epi verify agent.epi
epi view agent.epi

API keys in prompts/headers are redacted automatically before they land in the file.


What a .epi file is

Every .epi uses the Envelope v2 container format — a polyglot HTML+ZIP binary that opens natively in any browser and can be extracted programmatically.

demo.epi
├── manifest.json     # Ed25519 signature + SHA-256 file hashes
├── steps.jsonl       # Timeline (hash-linked steps)
├── environment.json  # Runtime snapshot (sensitive env redacted)
├── analysis.json     # Fault / policy analysis (when generated at seal)
├── viewer.html       # Offline forensic UI
└── VERIFY.txt        # Plain-text auditor instructions
Guarantee How
Integrity SHA-256 over every sealed member
Authenticity Ed25519 signature on the manifest
Chain Each step’s prev_hash links the timeline
Privacy Default secret redaction (API keys, tokens, PII)

Samples: docs/assets/SAMPLES.md · try docs/assets/readme-demo.epi.


Integrations

Stack How
OpenAI wrap_openai(OpenAI())
Anthropic wrap_anthropic(Anthropic())
LangChain EpiCallbackHandler(session) inside with record(...) as session
LiteLLM EPICallback
pytest pytest --epi
Microsoft AGT epi import agt <file>
TRACE Trust Record epi export trace <file.epi> — log-import Level 0; see caveats below
# LangChain (canonical adapter)
from langchain_openai import ChatOpenAI
from epi_recorder import record
from epi_recorder.adapters.langchain import EpiCallbackHandler

with record("run.epi") as session:
    handler = EpiCallbackHandler(session)
    llm = ChatOpenAI(model="gpt-4o-mini", callbacks=[handler])
    llm.invoke("…")
# pytest — attach evidence to failing tests
pytest --epi

pytest --epi writes one .epi per failing test to ./test-evidence/ (--epi-dir DIR to change it, --epi-on-pass to keep passing tests too). Files are named after the test (test_x.py_test_bad.epi); unlike record(), a re-run replaces the file for the same test in place.

TRACE export (honest claims)

epi export trace run.epi --out run.trace.json --transcript-uri https://host/run.epi

Signing key order: --key → private key that sealed the .epi → TRACE_PRIVATE_KEY_PEM → ephemeral (warning). allow_embedded_key=True when verifying only proves internal consistency of the record against its own cnf.jwk. It does not prove the record came from a trusted issuer. What we claim: the JSON is self-consistent and bound to the key that signed it (the sealing key when that key is available).

policy.bundle_hash in TRACE is specified as a Cedar policy hash. We hash policy.json (the sealed EPI policy document), not a Cedar bundle. policy.enforcement_mode is "declared" — we did not run a Cedar engine. appraisal.status is "none".

More: docs/FRAMEWORK-INTEGRATIONS-5-MINUTES.md


CLI

Command Purpose
epi demo Guided demo: record → seal → verify
epi verify <file.epi> Offline integrity + signature check; customers and auditors run epi verify FILE --policy strict --witness-log received.jsonl (rely on the exit code only with --policy strict; WARN exits 0)
epi-register <file.epi> Alias of epi scitt register: embed a SCITT receipt (local self-issued unless --service <url>)
epi view <file.epi> Open offline viewer (screenshot above)
epi analyze <file.epi> Fault / policy summary from sealed analysis
epi policy init Create epi_policy.json rulebook
epi run [script.py] Record a Python workflow that already emits EPI steps (prompts for a script if omitted)
epi keys generate / list / trust / revoke / unrevoke Signing keys and pinned (trusted) keys; generate and list print fingerprints
epi enterprise setup / pack Org kit + auditor pack
epi scitt register <file.epi> SCITT receipt; --service <url> for an independent transparency service
epi import agt <path> Import Microsoft AGT evidence
epi export trace <file.epi> TRACE v0.2 log-import record (self-consistency, not issuer attestation)

Policy + fault analyzer guide: docs/POLICY.md


Security defaults

  • Redaction is on (redact=True). Keys/tokens/PII become placeholders.
  • Prefer not using redact=False in production (it warns).
  • Verification is local — no network required for integrity/signature.
  • First-run identity WARN / LOCAL is expected until you trust a key.
  • Seal ≠ identity ≠ policy. Verify proves the file; analyze grades the run against rules/heuristics.

Docs & pilot

Topic Link
Docs map docs/README.md
How it works ARCHITECTURE.md
Archive (historical guides, may be out of date) Pilot pack · Enterprise in 15 minutes · Enterprise capability · Policy + fault analyzer
Known limitations docs/KNOWN_LIMITATIONS.md
CLI deep dive docs/CLI.md
Auditors guide docs/AUDITORS-GUIDE.md

Standards & compliance

EPI produces evidence files that help with audit trails. It is not a compliance guarantee and does not provide legal advice. Whether evidence satisfies a specific regulatory threshold is for the auditor or notified body to determine.

Topic Docs
EU AI Act Annex IV docs/ANNEX-IV.md
AIUC-1 domains docs/standards/aiuc-1-evidence.md
SCITT docs/standards/scitt-predicate.md
epi verify agent.epi --aiuc1   # evidence grouped by AIUC-1 domain (not a certification)

Troubleshooting

Symptom Fix
epi: command not found Same venv as pip install, or python -m epi_cli
First verify WARN / LOCAL identity Normal if seal OK — the receiver pins the sender's key from the sender (epi keys trust <sender-key-file> --name <name> or --from-did <did>), never from the file being checked
Integrity: FAILED File changed after seal — re-record
epi analyze says heuristic only Add epi_policy.json via epi policy init, re-run from that folder
Share / portal fails Hosted needs backend; local record/verify never depends on it

Trust-model note: Integrity checks whether the sealed record was altered since sealing — not that every real-world action was captured.


Project layout (contributors)

Core (actively developed, covered by the release gate):

Path Role
epi_recorder/ Python SDK (record, wrappers)
epi_core/ Container, crypto, redaction, verify, fault analyzer
epi_cli/ epi command
epi_gateway/ Self-hosted capture/review runtime (epi gateway serve)
web_viewer/ Canonical browser UI (source for epi view and the embedded viewer)
epi_viewer_static/ Browser crypto baked into every sealed file at pack time
pytest_epi/ Pytest plugin
website/ Public site source of truth (epilabs.org)
scripts/ Build, sync, release and verification helper scripts
docs/ Start at docs/README.md
tests/ ~190 test files, including the test_core_loop_golden.py regression path

Experimental (frozen — works as documented, bug fixes allowed, no new features until a paying user asks; not on the critical path):

Path Note
epi_mcp/ ChatGPT/Claude connector
verify_portal/ Hosted portal, auth, Paddle billing, tier gating
epi_recorder/integrations/trace_exporter.py TRACE export
epi_recorder/integrations/agt* Microsoft AGT import/export
epi_guardrails/ Guardrails
website-v2/ Sandbox redesign (not deployed)
remote SCITT service Local SCITT receipt is core; the hosted service is not

Website edits: only under website/ (the source of truth). npm run build copies it to site/ for Cloudflare Pages; python scripts/sync_website.py mirrors it to verify_portal/static/, epi-official/ and site/. Details: ARCHITECTURE.md, docs/archive/SITE.md.


License

MIT — see LICENSE.

Site: epilabs.org · Issues: GitHub Issues

Metadata

Release files for epi-recorder 4.6.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 epi-recorder 4.6.1
File Size Uploaded
epi_recorder-4.6.1.tar.gz 1.2 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for epi-recorder 4.6.1
File Interpreter ABI Platform
epi_recorder-4.6.1-py3-none-any.whl Python 3 none any Details

Total release size: 2.1 MB

Release files / epi_recorder-4.6.1.tar.gz

Download URL epi_recorder-4.6.1.tar.gz
Size 1.2 MB
Tags Source
SHA-256 checksum
How to use checksums
64a9ea33862f0ff7f8946d82d240b0528e7c3ef9b09560a611bcb0c21222a678
BLAKE2b-256 checksum
How to use checksums
f708399c3c9bc20f9d42e3e0fdfda38c1a7f353b24f29fa7852c5bc943e3de5c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.10

Release files / epi_recorder-4.6.1-py3-none-any.whl

Download URL epi_recorder-4.6.1-py3-none-any.whl
Size 844.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
28c68230aadc11d4daf42163401d88bf6bee86b056f54b986cad7cd1561e5c03
BLAKE2b-256 checksum
How to use checksums
32bb2d46d296c517b2d00db647d2404c8ffa027ddf69b5736660f27efadab441
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.10

Release history Release notifications | RSS feed

This release

4.6.1 This release

2 release files

4.6.0

2 release files

4.4.8

2 release files

4.4.6

2 release files

4.4.5

2 release files

4.4.4

2 release files

4.4.3

2 release files

4.4.1

2 release files

4.4.0

2 release files

4.3.0

2 release files

4.2.0

2 release files

4.1.0

2 release files

4.0.3

2 release files

4.0.2

2 release files

4.0.1

2 release files

4.0.0

2 release files

3.0.2

2 release files

3.0.1

2 release files

3.0.0

2 release files

2.8.10

2 release files

2.8.9

2 release files

2.8.8

2 release files

2.8.7

2 release files

2.8.6

2 release files

2.8.5

2 release files

2.8.4

2 release files

2.8.3

2 release files

2.8.1

2 release files

2.8.0

2 release files

2.7.2

2 release files

2.7.1

2 release files

2.7.0

2 release files

2.6.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