Changelog
All notable changes to erebus are documented here.
[Unreleased]
Added
examples/verify/body.sh(+ fixtures): offline smoke forverify-attest --request-body/--response-body(honest accept, forged refuse). Documents--expected-output-hashnext to the offline pack.attest-tool http --expected-output-hash: e2e that a wrong golden digest writes no receipt;verify-attest --expected-output-hashmismatch is exit 2.
Fixed
scripts/yc_demo.shruns on stock macOS without GNUtimeout(gtimeoutor perl alarm fallback).
[0.6.3] — 2026-10-17
Fixed
- Windows release build:
LocalFreereceives the SID string pointer as an untyped allocation, soerebus-stubcompiles forx86_64-pc-windows-gnu.
Changed
- Registry bearer comes from
--token-fileorERE_TOKEN. A--tokenflag would show the secret inpsand in shell history. - The transparency log timestamps the checkpoint after the publish response,
so a slow RFC 3161 server no longer holds
POST /attestations. - An installed CLI with no local
erebus-stubdownloads the matching stub from the GitHub release and checks it againstchecksums.txt. - A release uploads one archive per built architecture plus
checksums.txt.
Added (registry)
scripts/erebus_registry/: the registry’s proof core (Ed25519, Merkle, RFC 3161, transparency log) as a stdlib-only package; byte formats unchanged and pinned against the reference implementations in the battery.- Merkle roots and inclusion/consistency proofs are O(log n) over a cached append-only tree instead of O(n) list scans (200k leaves: ~0.3 ms vs ~0.5 s per query); leaf lookup is a hash map.
Added
- Spec 2.1 action receipts:
erebus serve --attest-keysigns every tool call the engine returns (name and call id in clear,sha256of the arguments) intoexecution.request.tool_calls, for chat completions (JSON, SSE deltas, legacyfunction_call) and the Responses API. A response that cannot be fully parsed is flaggedtool_calls_incompleteinstead of being reported as “no calls”. Receipts without tool calls stay byte-identical to 2.0. erebus verify-attest <receipt> --response-body FILE: an auditor holding the plaintext response checks it against the signedoutput_hashand re-derives the signed tool-call list (exit 2 on any mismatch). Human output lists the signed tool calls.- Spec 2.1 tool linkage: the proxy also signs the request’s tool context
(
tools_offered, replayedtool_history,tool_resultsas call id + content digest).verify-attest --chainlinks every result to the call that requested it and reports injected results, rewritten history, changed results and calls to unoffered tools;--require-tool-linkagemakes them fatal (exit 2).verify-attest <receipt> --request-body FILEre-derives the request side from the request body. - Spec 2.1 attested tool boundary:
erebus attest-tool http --listen … --upstream …(plain HTTP tools, MCP Streamable HTTP with JSON or SSE answers) anderebus attest-tool stdio … -- <mcp server>sign one receipt per tool execution (tool_invocation: tool name, canonical arguments digest, result digests, error flag).verify-attest --chain AGENT --tool-trail [NAME=]DIRmatches every issued call to an execution and reports results no tool produced (result_without_execution), results that differ from what the tool returned (result_not_from_tool) and tool runs no model call asked for (execution_without_call);--require-tool-executionmakes them fatal (exit 2). Tool calls gain an optionalarguments_jcs_hash(JCS of the parsed arguments) so the two sides match whatever the whitespace. - Tool executions can be bound to the engine’s call id: MCP
params._meta["dev.erebus/call_id"]or theErebus-Call-IdHTTP header is signed astool_invocation.call_id. A bound execution matches only its call, so identical calls executed out of order no longer yield a spuriousresult_not_from_tool, and a tool that ran the call with other arguments is reportedexecution_altered(fatal with--require-tool-execution). verify-attest --tool-trusted-dir DIR: tool trails are verified only against the tool operator’s keys, which must share none with the agent’s trusted keys, so a single stolen key can no longer forge both the agent and the tool trail. Reportstool_trust: separate|shared; shared trust is flagged in the human output.- Egress confinement:
ERE_EGRESS=PORT[,PORT…]|nonelimits the payload’s outbound TCP to the given ports (macOS Seatbelt:localhostonly; Linux 6.7+: Landlock TCP connect rules plus a seccomp filter refusing UDP, raw and packet sockets, TCP Fast Open,io_uringand foreign ABIs). Signed asexecution.egress; an invalid policy or a kernel without Landlock ABI 4 refuses the launch.scripts/egress_e2e.sh(Linux) andscripts/macos_slice.shcheck it on a real bundle against a control. erebus evidence export <bundle> -o pack.zipanderebus verify-evidence pack.zip [--bundle] [--json]: a self-contained, offline-verifiable audit pack (receipts, inclusion proofs, checkpoints, RFC 3161 tokens, DSSE envelopes, signing keys). Export refuses an unverifiable trail; verify refuses any edited, missing, extra or path-traversing entry and a mismatched bundle (exit 2).verify-evidence --operator-key <key> --log-key <hex>pins the keys an auditor obtained out of band; any other key in the pack is refused, and the report lists every key as pinned orNOT PINNED.- Evidence packs carry
anchors/<receipt>.checkpoint, the exact bytes the RFC 3161 token covers, soopenssl ts -verify -datachecks the TSA signature without erebus. - Docs:
guides/audit.md(auditor walkthrough); proof-first introduction, problem, positioning and comparison pages, in English; CLI help leads with the proof commands.
Fixed
- Health-check server (stub): on macOS/BSD an accepted socket inherits
O_NONBLOCKfrom the listener, so a probe that had not yet written its request got a reset instead of200. Accepted sockets are now blocking with a 2 s read timeout. - SISR e2e mock server: same inheritance made
e2e_sisr_mainandupgrade_migrationfail randomly on macOS (“Peer disconnected”). erebus builddetects Mach-O (thin and universal) agents as the binary runtime; it only knew ELF and PE, so native macOS apps were refused. Java class files, which share the fat magic, are not mistaken for them.erebus buildno longer stages a Linux glibc base (and warns about it) for non-ELF payloads.verify-evidence --operator-keyaccepts the public key hex thaterebus keygenprints, as well as thesha256:fingerprint.attest --pushalso stores the inclusion proof beside the trail entry, so trails built withattestcarry their proofs likeserve --pushones.- Spec and docs: a batch run’s
input_hashcovers the launch vector (entrypoint + arguments), not stdin, andoutput_hashneeds Linux isolation ≥ 2; the spec claimed stdin. Quickstart trusted-keys path fixed.
Fixed (macOS, ROADMAP P1-5)
--landlockbundles are now confined by Seatbelt (sandbox_init), and the launch is refused if the profile cannot be installed. Before, the profile was never applied. Payload reads are limited to the system trees and the rootfs; writes go to the rootfs and a privateTMPDIR.- With
ERE_ATTEST_KEY, macOS receipts now carryoutput_hashandexit_code(the Linux capture supervisor, shared). - A launch that failed inside the capture supervisor exited 0, so its receipt would have signed a clean run. It now exits 126.
scripts/macos_slice.sh: no KNOWN GAP left; the chain check no longer fails at random underpipefail.
Added (batch stdin)
- Spec 2.1
execution.stdin_hash: with capture on, a non-terminal stdin is relayed by the supervisor and itssha256signed, only when the whole input was delivered (Linux isolation ≥ 2 and macOS). Receipts without it keep the 2.0 bytes.
[0.6.1] — 2026-09-29 — internal pilot
Added
erebus serve --push --registry …: each inference receipt is published to the transparency log and its verified inclusion proof is stored beside the local trail entry; missing proofs are retried at the next start.- Reproducible T2 harness under
scripts/pilot/: pinned Baguettotron Q8, persistent RFC 3161-anchored registry, enrollment, real probe and evidence-derived report.
Fixed
build --embed-modelnow stages the model before payload compression. Previously metadata advertised the ModelLayer, but the assembled payload did not contain the model bytes.- Attestation-trail enumeration ignores
.proof.jsonsidecars, so publishing per-call proofs cannot corrupt sequence numbering or chain verification. - Stub: a fresh bundle could fail its very first launch with
Invalid argument (os error 22). The cold-start rootfs hashing joins one worker per core, and the kernel wakes the joiner before the thread leaves the thread group, so theunshare(CLONE_NEWUSER)that follows was refused with EINVAL.enter_usernsnow waits for/proc/self/taskto drain and retries the unshare;scripts/chaos_extreme.shgained a 40-launch cold-start storm that reproduces the window under load. erebus buildright after a release install: the CLI now finds theerebus-stubshipped next to theerebusbinary (host-target builds). It used to search only source-tree build dirs andPATH, and failed with “erebus-stub not found”.
[0.6.0] — 2026-09-28 — the proof chain
Added
- Execution receipts spec 2.0 — DSSE envelope over the JCS (RFC 8785)
canonical in-toto statement, predicate
https://erebus.dev/attestation/execution/v2;signaturemirrors the operator entry so 1.x consumers keep working.verify-attestaccepts 1.x and 2.0. Spec:spec/attestation.md. - Machine identity —
erebus enroll [--registry]: per-host Ed25519 key (0600), certificate signed by the registry key (POST /machines/enroll,GET /machines); every receipt produced on the host is co-signed;verify-attest --strict [--registry-key]. - Sigstore interop —
erebus keygenwrites<fp>.pem,erebus attest --envelope FILEwrites the bare envelope;cosign verify-blob-attestationverifies it (exercised in CI). - Transparency log — registry
attestations/log.sqliteMerkle tree (RFC 6962/9162) with signed checkpoints,GET /log/{checkpoint,proof, consistency,key}, RFC 3161 anchoring (--tsa-url,--anchor-every);erebus attest --pushstores<receipt>.proof.json;verify-attest --proof [--log-key]verifies it offline. - Warm-cache re-verification —
rootfs.merklemanifest written at extraction; every warm start re-hashes the rootfs and re-extracts on drift;execution.rootfs_rootis signed into receipts (ERE_ROOTFS_CHECK=off|strict). - Attesting reverse proxy —
erebus serve --attest-key: one hash-chained receipt per inference call (execution.request). - Hash-chained local trail —
<bundle>.attest.d/,execution.prev_attestation_hash,verify-attest --chain. verify-attesthardening: display-copy consistency check,--bundlebinding, fingerprint check.- Proof chain in CI —
.github/workflows/proof.yml(README badge) runsscripts/test_transparency_log.py, the fullscripts/yc_demo.sh(sandbox on, enroll,--strict, cosign, four chaos harnesses) andscripts/chaos_extreme.shon every push and daily.
Fixed
erebus serve --llama-server PATHno longer requires allama-serveron PATH or a runtime pack: the given binary is used as the engine directly.
Changed
- The in-toto statement now serializes
predicateType(standard); the 1.x spellingpredicate_typeis still accepted and its signature bytes are reproduced for legacy verification. - Registry attestations moved from JSONL to SQLite (old journal imported once).
[1.0.0] — 2026-08-03 — SISR
First major release integrating SISR (Self-Incremental Sovereign Reconstruction) end to end.
Added
- SISR delta self-updates — binaries embed a content-addressed chunk
manifest;
./app --erebus-update <url>fetches only the changed chunks from the update channel and rebuilds the binary in place. erebus upgrade-binary— promote a legacy (v1, SISR-less).erebusto the v2 format by injecting the delta manifest andSISRheader, preserving every payload byte and the integrity hash.- Post-update health gate + automatic rollback — a newly updated binary runs under supervision; a failed health check restores the previous version from a snapshot, and quarantined targets are refused.
- Migration guide — see
migration/v1-to-v2.md.
Compatibility
- v1 (SISR-less) binaries run on the v2 runtime unchanged and without warnings: standard extraction only, zero startup penalty.
- Deployment scripts based on v1 remain 100 % valid.
- The
.erebusfooter format is frozen; new capabilities ship as flags and backwards-readable format-version bumps.
Testing
- End-to-end suite against a mock HTTP update channel (
cargo test -p erebus-stub --test e2e_sisr_main), network fault injection on the reconstruction engine, and property-based parser fuzzing via proptest. - Cross-version tests prove legacy binaries load on the v2 runtime and that upgraded binaries gain auto-update.
[0.3.0] — 2026-07-XX — Pre-SISR
Last release before the SISR integration. Classic [stub][payload][metadata] [footer] binaries only; no delta updates.