apohara-sealchain
Prove your AI artifact wasn't tampered with — and prove exactly how much that proof is worth.
Quick Start · The five layers · Trust profile · How it works / honesty
A single Rust binary — a CLI and an MCP server — that seals any file into a tamper-evident <artifact>.seal.json receipt anyone can verify offline. Five real cryptographic layers, no account, no SaaS, and no verified=true hardcoded anywhere: every layer re-checks its own crypto, or the seal aborts.
$ apohara-sealchain seal model.bin
SEALED model.bin.seal.json
sealedAt: 2026-01-01T00:00:00+00:00
layers: hmac, ed25519, c2pa
$ apohara-sealchain verify model.bin model.bin.seal.json
PASS
content [ok] artifact hash matches receipt
hmac [ok] hmac verified
ed25519 [ok] ed25519 verified
c2pa [ok] c2pa manifest valid; payload hash bound
$ printf 'x' >> model.bin # flip one byte
$ apohara-sealchain verify model.bin model.bin.seal.json
FAIL
content [FAIL] artifact hash mismatch: file does not match receipt
$ echo $?
1
Real output from
scripts/demo.sh, which doubles as a CI smoke test. A seal is evidence, not a verdict —verifyreports each layer with a reason, and exits non-zero the instant a byte moves.
💡 Concept
[!NOTE] An AI artifact is only as trustworthy as the proof that travels with it. A model on the Hub, a dataset in a bucket, a generated image in a PR — a downloader has no way to know it's byte-for-byte what you produced, that you produced it, when it existed, or whether anyone can audit that claim. apohara-sealchain answers those questions with math you can re-run yourself, offline, with nothing but the file and its receipt.
apohara-sealchain reads a file and emits an <artifact>.seal.json receipt — a self-contained JSON sidecar chaining up to five independent cryptographic layers, canonicalized with JCS (RFC 8785). Publish the receipt next to your artifact and anyone can verify it offline: the CLI, the in-browser WASM verifier, or the thin Python / Node SDKs. The Ed25519 public key is embedded in the receipt, so verification needs no key server and no network.
The same binary speaks the Model Context Protocol, so an AI agent (Claude Desktop, Claude Code, Cursor, or any MCP client) can seal and verify artifacts as native tools.
✨ Features
| 🧾 Self-contained receipts | One <artifact>.seal.json sidecar carries every layer + the embedded public key. Verify with just the file and the receipt — no account, no key server, no network. |
| 🔌 CLI and MCP server | apohara-sealchain is a full CLI; apohara-sealchain mcp is an MCP server (stdio by default, or streamable-HTTP with mcp --http <host:port> for remote/CI) exposing seal_artifact / verify_receipt / show_chain to any agent. |
| 🧭 Canonical trust profile | packaging/trust-profile.json states, machine-readably, what each layer combination proves and does not prove. |
| 🎫 Attestation policies | verify --policy file.toml or --profile {offline-basic|transparency|legal-grade|full} enforces a bar after verification — exit 5 if crypto is valid but the policy isn't met, 1 if the artifact was tampered. |
| 📊 Offline transparency dashboard | apohara-sealchain dashboard renders a self-contained HTML report of your receipts — layers, an honest verify status, policy compliance — with zero network references. |
| 🌐 In-browser verifier (WASM) | Drag a file + its receipt onto a static page and verify content + Ed25519 + C2PA fully offline in WebAssembly — no backend, no upload. |
| 📜 SLSA-style provenance | apohara-sealchain provenance maps a receipt onto an in-toto Statement v1 for supply-chain tooling — honestly typed, never claiming SLSA build semantics. --format model-signing emits the model-transparency / OpenSSF Model Signing shape for ML-ecosystem interop. |
| 🦀 Honest by construction | Pure Rust. verify is always offline. Every layer produces and re-checks real crypto, or the seal aborts — there is no faked pass anywhere in the tree. |
| 🔏 Signed releases | Every release binary carries a SLSA build provenance (L2+) attestation (Sigstore keyless) — verify it with gh attestation verify before you run it. |
🔐 The five layers
All five are implemented and live-exercised — no placeholders, no unexercised crypto.
| Layer | What it proves | Notes |
|---|---|---|
| HMAC-SHA256 | local integrity (symmetric) | always present; the secret is never in the receipt |
| Ed25519 | authorship by the key holder | public key embedded → offline, self-contained verify |
| C2PA | a provenance manifest is bound to the payload | real JUMBF manifest; v0.1 is self-signed with the seal key (not third-party-trust-anchored); --ai-generated records the IPTC trainedAlgorithmicMedia source type |
| RFC-3161 TSA | existence-before-a-point-in-time, per the authority | real token; the default TSA is not eIDAS-qualified — point --tsa at a QTSP for legal-grade |
| Sigstore Rekor v2 | public, append-only transparency-log inclusion | real DSSE entry; offline-verifiable inclusion proof + C2SP checkpoint against a pinned shard key |
The default seal is fully offline (HMAC + Ed25519 + C2PA). --tsa / --rekor add the network-backed transparency layers; --all seals every configured layer real-or-abort (if a requested layer can't be produced, the seal aborts and writes nothing). verify is always offline — signature, timestamp, C2PA, and the Rekor inclusion proof all check from the receipt alone.
🚀 Quick Start
# Install the CLI (builds from source — lowest-trust path)
# Seal a file — offline default: HMAC + Ed25519 + C2PA
# -> writes model.bin.seal.json
# Verify offline — exits 0 on PASS, 1 on tamper
Run it as an MCP server — add this to your client config (matches packaging/mcp.json):
# Add the public transparency layers (RFC-3161 TSA + Sigstore Rekor v2; needs network at seal time)
# Enforce a named profile after verification (exit 0 = pass, 5 = policy fail, 1 = tamper)
# ...or a custom declarative policy
# Render a self-contained, offline HTML transparency report of your receipts
# Emit an in-toto / SLSA-style provenance Statement for supply-chain tooling
# Batch-seal a directory and query the local index
Other paths. npx -y @apohara/sealchain downloads the prebuilt binary and runs the MCP server. Pre-built per-OS binaries are on Releases. Thin Python / Node SDKs wrap the binary, and a reusable GitHub Action seals build artifacts in CI.
[!WARNING] Downloading a pre-built binary is itself a supply-chain surface — the very risk this tool exists to make auditable. Prefer
cargo installand build from source, or verify the release provenance (below) before running.
Verify the release provenance. Every release binary ships with a SLSA build provenance (L2+) attestation, minted by the release workflow with Sigstore keyless signing (no long-lived key). Verify a downloaded asset against this repository before running it:
The attestation is bound to the asset's digest, so it covers exactly the bytes you run. (We claim L2+, not L3, until the level is independently verified against a published release — measure, don't assert.)
🧭 Trust profile
A seal proves the properties of the layers it actually carries — nothing more. packaging/trust-profile.json is the machine-readable source of truth; docs/TRUST-PROFILE.md is its human-readable rendering. The named profiles below are enforceable with verify --profile and surfaced per-row in the dashboard.
| Profile | Requires (present and verified) | Use it for |
|---|---|---|
offline-basic |
HMAC + Ed25519 + C2PA | the fully-offline default: integrity + authorship + provenance |
transparency |
Ed25519 + Rekor v2 | publicly-recorded authorship (append-only log) |
legal-grade |
Ed25519 + a qualified eIDAS QTSP timestamp | an eIDAS-oriented timestamp (see honesty note) |
full |
all five layers | the maximal chain |
A layer counts toward a profile only if it is present and its verification passed — there is no asserted pass.
🔬 How it works / honesty
[!WARNING] A seal is evidence, not a verdict — and not legal advice. It does not make an artifact "trusted"; it lets a human (or a policy) decide based on layers that each re-check their own crypto. Read these limits before you rely on it:
- The default timestamp is not legally qualified. A non-eIDAS TSA is fine for integrity/credibility but is not a court-admissible qualified timestamp under eIDAS Art. 42. For legal weight, point
--tsaat a Qualified Trust Service Provider on an EU Trusted List — that is your account to provide.- The v0.1 C2PA manifest is self-signed with the seal's Ed25519 key (trust check disabled — "Valid, not Trusted"). It binds the payload, but is not a third-party-trust-anchored C2PA credential. The CA-issued upgrade path (SSL.com / DigiCert) and its trade-offs are documented in
docs/c2pa-trust.md.require_qualified_tsais a host-allowlist match, not cryptographic proof of eIDAS qualification — and the violation message says so.- HMAC is symmetric. Only the secret holder can re-check it; a third party verifies content + Ed25519 + C2PA instead, and the tools say so rather than fake a pass.
Measure, don't assert. No layer hardcodes a pass. Each re-derives and re-checks its binding at verify time, or reports ok: false with a reason. The Rekor inclusion proof and signed checkpoint verify offline against a shard key pinned with provenance — never fetched from the thing being verified — and an unknown log key is a measured ok: false, never a silent pass.
Wire format. The receipt schema (apohara-seal-v1), every layer's binding, and verify semantics are specified in SPEC.md — including a per-field compatibility matrix (since-version, required/optional, native vs WASM verifier coverage) backed by the machine-readable packaging/receipt.schema.json. The format is a clean-room Rust reimplementation of an Apache-2.0 reference; where the reference is internally inconsistent, this implementation defines the canonical behavior and documents the divergence (see NOTICE).
Performance. Honest, reproducible offline-profile numbers (seal/verify latency, batch throughput, the C2PA cost, receipt size) live in BENCHMARK.md — regenerate them yourself with scripts/bench.sh.
🏗️ Repository layout
apohara-sealchain/
├── crates/
│ ├── apohara-sealchain-core/ # the apohara-seal-v1 engine: layers, seal/verify, JCS
│ │ ├── src/layers/ # hmac · ed25519 · c2pa · tsa · rekor
│ │ ├── src/{policy,dashboard,trust_profile,provenance,keystore,index}.rs
│ │ └── trust-profile.json
│ ├── apohara-sealchain/ # the CLI + MCP stdio server
│ └── sealchain-wasm/ # the offline in-browser verifier (wasm-bindgen)
├── web/ # static drag-and-drop WASM verifier page
├── sdk/{python,node}/ # thin SDKs over the binary
├── packaging/ # mcp.json · plugin.json · trust-profile.json · receipt schema
├── examples/ # HuggingFace seal-your-fine-tune · attestation policies
├── docs/ # SPEC, trust profile, positioning, publishing, key management
└── .github/ # CI, release workflow, seal-artifact Action
🗺️ Roadmap
- Five real, live-exercised layers (HMAC · Ed25519 · C2PA · RFC-3161 TSA · Rekor v2)
- CLI + MCP server (stdio and streamable-HTTP) + offline in-browser WASM verifier
- Canonical machine-readable trust profile + attestation policies + transparency dashboard
- Thin Python / Node SDKs · in-toto/SLSA-style provenance (+ model-transparency interop) · encrypted keystore + rotation
- Batch sealing + local receipt index · reusable GitHub Actions (
seal-artifact,huggingface-seal) - Signed releases (SLSA build provenance) · OpenSSF Scorecard ·
SECURITY.md· honest benchmarks - Rekor seal-time stale-shard guard (TUF SigningConfig) · C2PA AI-generated disclosure (
--ai-generated) - Third-party-trust-anchored C2PA signer beyond v0.1 self-signed (workflow documented in
docs/c2pa-trust.md) - First-class eIDAS QTSP presets for legal-grade timestamps
- Direct HuggingFace Hub model-registry push (beyond the seal Action)
🛡️ Security
Found a vulnerability? Please report it privately via GitHub Security Advisories — see SECURITY.md for the disclosure process, supported versions, and the threat model (what each layer protects and what it deliberately does not).
🤝 Contributing
Contributions are welcome.
- Fork the repository.
- Create a feature branch (
git checkout -b feature/my-change). - Make your change and run the gate:
cargo test --workspace(pluscargo clippy --workspace --all-targetsandcargo fmt --check). - Open a pull request.
See CONTRIBUTING.md for details.
Unless you state otherwise, any contribution you intentionally submit for inclusion in this work, as defined in the Apache-2.0 license, shall be dual-licensed as below, without any additional terms or conditions.
📄 License
Licensed under either of MIT or Apache-2.0, at your option. See NOTICE for attribution.
Maintained by SuarezPM.