// code.noirebox.eu — the technical reference. The marketing page is gone on purpose.

NoireBox v0.21.0

A tamper-evident journal for AI agents: every event hash-chained (SHA-256), signed (Ed25519), append-only on SQLite, anchored to external RFC 3161 witnesses — plus a pluggable guardrail and a standalone verifier a third party can run offline, without trusting you.

PyPI Python 3.11+ MIT tests GHCR
pip install noirebox

§Overview

NoireBox is a single FastAPI service + Python package that gives an AI-agent system two guarantees: provable traceability (every event sealed in a journal a third party can verify offline) and input filtering (transcripts scanned before they reach the agent, verdicts sealed as events). The journal is domain- and language-agnostic — an event is a type and a payload — which is why it fits any stack. The verification never contacts the server: the export plus openssl is the whole trust surface.

End-to-end architecture. A transcript flows through the guardrail — regex, ML or tiered LLM engines — into any agent; the incident verdict and the agent's llm_call / llm_output events drop into the tamper-evident journal (SQLite append-only, SHA-256 chain, Ed25519), drawn as a row of sealed documents chained together. GET /api/v1/export and GET /api/v1/attestation produce export.json and attestation.json, which verifier.py — third party, offline — checks to a verdict: exit 0 valid, exit 1 invalid (tampered), exit 2 anchoring unproven.
The whole trust surface in one picture: sealed at runtime, exported, verified offline by a third party — exit 0 valid · 1 tampered · 2 anchoring unproven (verifier contract below).

Properties

  • Append-only — no UPDATE, no DELETE exist; sealing costs 0.17 ms/event
  • Located tamper detection — rewrite, insert or delete fails verification at the exact seq
  • Non-repudiation — Ed25519 over each event_hash; the key never has to leave your infrastructure
  • External time — RFC 3161 anchoring: only a 32-byte head hash ever leaves
  • Offline verification — the verifier speaks JSON + openssl ts, no API to trust
  • Undecidable ≠ clean — unverifiable states are named first-class, never folded into OK

Not

  • Not a blockchain — one issuer, one verifier, no consensus tax (ADR 001)
  • Not an observability tool — Langfuse-style logs are for debugging and stay rewritable; this is for proof
  • Not a certification — a building block that states its perimeter instead of overselling it (threat model)
  • Not multi-tenant SaaS — self-hosted, zero telemetry, one SQLite file per journal

§Install

Python ≥ 3.11, MIT, zero telemetry. The ML guardrail engine ships inside the wheel (FR + EN detectors, ~250 KB each) — no model download at runtime.

pip install noirebox        # core: journal + API + guardrail + verifier deps
pip install noirebox[pdf]   # + PDF attestation export (reportlab)
pip install noirebox[hsm]   # + PKCS#11 key custody (python-pkcs11)

Or Docker (image includes the ML engine):

docker run -p 8768:8768 ghcr.io/noirebox/noirebox:latest
# API + OpenAPI docs on http://127.0.0.1:8768/docs — data persists in ./data

Or from source (venv + deps + tests + API):

git clone https://github.com/noirebox/noirebox && cd noirebox
./start.sh

Environment variables

VariableEffect
NOIREBOX_DBJournal path (default: data/noirebox.db, or nearest .noirebox/ directory)
NOIREBOX_CLIENTSid:secret,… — enables JWT bearer auth + per-client rate limit. Unset = open API. A SET-but-unparseable value is fail-closed since 0.21.0 (NB-01)
NOIREBOX_METADATA_AUTH1 moves the metadata routes behind the bearer token (verification routes can never be locked)
NOIREBOX_TSA_PROFILESMulti-TSA anchoring: several independent witnesses behind one anchor (ADR 008/009)
NOIREBOX_TSA_URLLegacy single-TSA endpoint (still works)
NOIREBOX_JUDGE_MODELLocal LLM judge for the tiered engine (default qwen2.5-coder:3b via Ollama)
NOIREBOX_HSM_PINPKCS#11 token user PIN — prefer over argv (an argv PIN sits in the process table)

§Quickstart

# 1 — run the API
noirebox serve                # http://127.0.0.1:8768 — interactive OpenAPI at /docs

# 2 — seal an event (auth off by default; enable with NOIREBOX_CLIENTS)
curl -X POST http://127.0.0.1:8768/api/v1/events \
  -H 'Content-Type: application/json' \
  -d '{"type":"llm_call","payload":{"model":"gpt-x","prompt_sha256":"…"}}'

# 3 — anchor the chain head with an external TSA witness (RFC 3161)
curl -X POST http://127.0.0.1:8768/api/v1/anchors

# 4 — export the full dossier
curl -s http://127.0.0.1:8768/api/v1/export > export.json

# 5 — verify as a third party, offline (pip install noirebox is enough)
python verifier/verifier.py export.json
# exit 0 = intact & witnesses verified · 1 = broken · 2 = anchoring unproven

One-command demos: make demo (full scene: scan → journal → auditor → caught tampering) · make demo-mcp · make demo-fleet · make demo-tamper-style standalone scripts in demo/.

§Data model

An Event is the only write shape. Types are free-form strings (1–64 chars) — llm_call, llm_output, incident, eval, anchor are conventions, not a closed enum.

FieldTypeConstraint
seqint≥ 1, no gaps, UNIQUE
tsstrISO 8601 UTC, milliseconds
typestr1–64 chars, free-form
payloadobjectarbitrary JSON (GDPR minimization = caller's responsibility)
prev_hash64 hexSHA-256 of event seq-1 ("0"×64 genesis)
event_hash64 hexSHA-256 of the canonical serialization of {seq, ts, type, payload, prev_hash}, UNIQUE
signature128 hexEd25519 over bytes.fromhex(event_hash), instance key

Canonical serialization — any third-party reimplementation must produce the same bytes:

json.dumps(obj, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8")

The key is Ed25519, PEM PKCS8, chmod 600, created at database init (<db>.key). The public key alone is enough to verify. Where that key lives is pluggable — see custody.

§HTTP API

FastAPI, 12 routes, interactive OpenAPI at /docs and /redoc; machine-readable spec: docs/openapi.json. Base http://127.0.0.1:8768. 🔒 = behind the bearer token when auth is enabled; verification routes are open by design.

RouteAuthBody / queryResponse
GET /health——liveness
POST /api/v1/token—{client_id, client_secret}JWT HS256, 1 h expiry
POST /api/v1/events🔒{type, payload} → 201the sealed event
GET /api/v1/events🔒?limit&offset&since_seqpaginated list; since_seq = live-tail cursor (indexed, no replay)
GET /api/v1/activity——day → sealed-event count (aggregate only)
GET /api/v1/verify——{valid, nb_events, first_error}
POST /api/v1/transcripts/scan🔒{meeting_id, text, engine, lang} → 201incidents + journal entry
GET /api/v1/attestation——signed summary (head hash + signature)
GET /api/v1/attestation.pdf——A4 PDF for the DPO
POST /api/v1/attestation/verify—attestation{valid}
POST /api/v1/anchors🔒— → 201{anchor_event_seq, anchored_head_seq, anchored_head_hash, tsr_bytes}
GET /api/v1/export🔒—{format_version, service, public_key, events, attestation}

Auth (ADR 004) — enabled only if NOIREBOX_CLIENTS is set: 1-hour JWT HS256, 60 req/min per client (sliding window, 429 + Retry-After), constant-time secret comparison. Errors: 401 (missing/invalid token) · 422 (Pydantic) · 429 (quota) · 503 (engine unavailable, actionable message) · verification failures always carry the exact seq.

§Receipts & sealed payload schemas

Beyond free-form events, subsystems seal versioned payload schemas into the same journal. Each schema version is a contract; the reconciliation layer assembles verdicts from them.

SchemaSealed byCarries
receipt/0.2.2any writerjudged_by (an llm judge requires spec_hash — it read its sealed spec, or the seal refuses), reason, optional requirement_ref
writer/0.1the registrarknown-writers registry: public_hex → domain, declared_by — resolved against the PINNED registrar key (out-of-band); non-registrar entries can't vote; a key under two domains = registry_duplicate_undecidable, never first-wins
registry-disclosure/0.2the reportregistrant + evidence grade — disclosure, never violation; never touches clean_pass
retention/0.1noirebox seal-retentionprune / migrate / policy-change / restore — retention changes are custody events journaled like any other, not ops-layer edits

Named states, never silence: unverified_label · independence_undecidable · label_llm_call_mismatch · registry_duplicate_undecidable · scope_check_not_run — an undecidable outcome is never reported as clean, and a skipped check is reported as skipped.

The doctrine behind these schemas — who may judge what, and which verdict outranks which — is written up as the authority ladder (ADR 019–022).

§Verification contract

verifier/verifier.py — strict order, first failure wins, never contacts the server (openssl only):

  1. seq of event n equals n — no gaps, no duplicates
  2. prev_hash equals event_hash of event n−1
  3. event_hash equals the recomputed canonical SHA-256
  4. signature valid for the export public key
  5. Attestation: head_seq = event count, head_hash = last event_hash, signature valid on the canonical summary
  6. Each anchor event: referenced head consistent with the chain, RFC 3161 token verified (openssl ts -verify) against that hash and the embedded TSA certificate
ExitMeaning
0INTACT — chain valid and every witness token verified
1BROKEN — first chain/signature break + exact location
2ANCHORING UNPROVEN — a witness token could not be verified (missing openssl/ots, timeout). Report field: unverifiable_witnesses. Accept explicitly with --allow-unverified-witnesses (0.21.0, NB-02: undecidable is never clean — including inside the tool that enforces the doctrine)

TSA roots for public witnesses are pinned in the repository (verifier/tsa_roots/) — the auditor never trusts a certificate the operator ships. The auditing machine needs pip install noirebox, nothing else.

§Key custody & signing

Every signer answers the same interface — .sign() + .public_hex() — so custody can move without touching callers:

SignerKey livesNotes
KeyPairPEM file, chmod 600default; fine when process and disk are one trust domain
SocketSignerkeyholder daemon, over a unix socketsigns bytes, never reveals the key, no export operation; --allow-uid peer check fails CLOSED where the peer uid isn't checkable (0.15.0)
P11SignerPKCS#11 token, non-extractable--ensure-key imports the existing journal key — continuity, not rotation; public hex doesn't move (0.16.0, noirebox[hsm])

Custody disclosure — the journal states its own proof grade:

  • noirebox custody-grade prints witness = min(authoring, retention): a journal is only as provable as the weaker of "was the writer's key in custody at seal time" and "was the retention path itself journaled"
  • noirebox seal-retention --action prune|migrate|policy-change|restore — retention operations become first-class sealed events
  • Custody vs anchoring are different trust boundaries and are disclosed separately (ADR 022)
  • Post-quantum evidence is staged as hybrid signing behind the signer interface — classical Ed25519 stays the trust anchor while a PQ sibling rides along (ADR 027)
  • The pinned hardware root remains the deployer's call — stated in the threat model, not hidden

§Anchoring & fleet

A journal that dates itself proves nothing — that's the suspect writing its own report. Anchoring lets an external Timestamp Authority sign "I received hash X at time T". Only the 32-byte head hash ever leaves. A regenerated journal shows a head the old token doesn't cover, and a backdated token is arithmetically impossible — this is the control that catches an operator with the private key.

Witness layers you choose

LayerTrustSetup
Qualified eIDAS TSA (Universign, Certigna…)legal presumption of date + integrity (eIDAS art. 41)point a profile at the endpoint
Public TSAs (DigiCert, FreeTSA…)independent organizations; roots pinned in this repoNOIREBOX_TSA_PROFILES
OpenTimestamps / Bitcoinforging means redoing the network's proof of work; verifying ≈ 30 µsots client in PATH
Self-hosted OpenSSL TSAown root + leaf chain, 0 €, works offlinemake tsa

The anchor event payload carries head_seq, head_hash, the primary RFC 3161 token (tsr, base64 DER), the TSA cert bundle (tsa_cert_pem) and the full witness list (tokens — one token per profile + OTS receipts).

Fleet hub

One seal for the whole fleet: the heads of N journals form a Merkle tree, a single TSA seal covers them all, and each member proves its place with ~11 hashes — verified offline. noirebox fleet-anchor seals it, fleet-verify checks one member, fleet-status checks every member (exit 0 all covered / 1 alarm, --json for alerting), fleet-watch runs that on a cadence with optional re-anchoring (--reanchor shrinks the falsifiable window to the interval) and webhook alarms. The hub sees 32-byte hashes, never your events.

§Guardrails

Prevention is pluggable; proof is universal. Already running Lakera, Llama Guard, your own LLM-judge? Keep them — a guardrail is just another event source, and its verdicts become tamper-evident instead of living in rewritable logs.

EngineHowLatency (7 lines)
regexbilingual FR/EN patterns compiled at load; score = fixed severity0.21 ms
mlTF-IDF (1–2 word + 3–5 char grams) + logistic regression; FR + EN detectors ~250 KB each in the wheel; threshold 0.5, granularity = line10.3 ms
tieredregex + ML first; a local LLM judge (default qwen2.5-coder:3b, JSON-pinned, temperature 0) only pays for doubt (ADR 015/016)ml cost + judge on disagreement

Incident contract — same shape from every engine:

{
  "category": "instruction_override|data_exfiltration|pii_request|tool_abuse",
  "score": 0.94,
  "excerpt": "…≤ 120 chars…",
  "start": 214, "end": 305
}

Honest failure modes: unparsable judge output → 503, never a manufactured incident; verdicts outside the taxonomy are dropped; measured false negatives are non-zero and tracked. The bundled detector is a working example of the plugin contract — swappable by design (ADR 003: Meta's Prompt Guard evaluated and declined, integration path documented).

§Integrations

MCP server — agent tools over stdio JSON-RPC

python -m noirebox.mcp_server
# client config: {"mcpServers": {"noirebox": {"command": "python", "args": ["-m", "noirebox.mcp_server"]}}}
ToolDoes
noirebox_scanscan a transcript, seal the incidents
noirebox_log_eventseal one free-form event
noirebox_verifyverify the journal in place
noirebox_attestationfetch the signed summary

Agent hooks & editor

  • noirebox hook tool-use / session-end — seal one tool action (PostToolUse) or the full session transcript (SessionEnd) from hook JSON on stdin; best-effort by design (ADR 013)
  • noirebox seal-trajectory --rollout <dir> — seal a coding agent's model trajectory (model-io JSONL): per-line digests only, never the payload (ADR 012)
  • VS Code / Cursor extension (also on Open VSX) — journal + verify from the editor; versioned separately from the package
  • ZCode / Claude Code plugin — plugins/claude + plugins/noirebox in-repo

Operations

  • noirebox export-siem --format cef|otlp --since N — CEF (ArcSight) or OpenTelemetry JSON, tail-only with --since
  • noirebox audit-pack <outdir> — one folder for the auditor: export + verifier report + the Annexe IV §2(f) logging description (ADR 010)
  • noirebox replay <meeting_id> — rebuild the decision timeline from sealed evidence; refuses if the chain doesn't verify first ("replay refuses theater")
  • noirebox reconcile --config invariants.json --journal-report --fail-on-findings — invariants over the journal, CI-friendly exit 2
  • noirebox crosschain --journal a=path … — consumption edges across journals, partial order, fan-in (ADR 024)
  • noirebox toolset-pin --server S --tools tools.json --reviewed-by NAME --check --fail-on-drift — MCP tool descriptions are instructions: pin the baseline, diff on session start (ADR 025)
  • Built-in dashboard (heatmap, traffic radar) on the service; Grafana JSON in docs/grafana-dashboard.json

§CLI reference

noirebox --version → 0.21.0. Twenty subcommands; every journal path resolves the same way (--db → NOIREBOX_DB → nearest .noirebox/ → data/noirebox.db).

CommandWhat it does
serverun the HTTP API (uvicorn) — --host, --port (default 127.0.0.1:8768)
sealseal one free-form event: seal <type> '<json>' — digests and short facts, never raw content
verifyverify the journal chain in place, print the verdict
locateprint the resolved journal path
reconcileinvariants over the journal from a JSON config; --journal-report seals the report; --fail-on-findings exits 2 (CI)
crosschainreconciliation across journals (consumption edges, ADR 024); repeatable --journal NAME=PATH
toolset-pinMCP toolset baseline: pin, then --check (session-start gate), --fail-on-drift for cron/CI
audit-packauditor pack: export + verifier report + Annexe IV §2(f) description
seal-trajectoryseal a coding agent's model trajectory (model-io JSONL, digests only); --rollout scans a dir, --interval rescans
hook tool-useseal one agent tool action (PostToolUse) from hook JSON on stdin
hook session-endseal the session transcript (SessionEnd)
fleet-anchorone Merkle seal over N journal heads, witnessed by the configured TSAs, proofs printed per member; --allow-local = no external date
fleet-verifyis this journal covered by the hub's latest fleet seal? (exit 0/1)
fleet-statuscheck every member against the hub seal — cron/CI friendly, --json
fleet-watchservice form: check on a cadence, alarm on drift (webhook), optional --reanchor
replayrebuild the decision timeline for one meeting/run from sealed evidence; refuses an unverified chain
export-siemCEF or OTLP/JSON export, --since tail-only
keyholderserve the key over a unix socket — signs bytes, never reveals; --allow-uid, --pkcs11-*, --ensure-key
keyholder-statusdescribe a running keyholder: public key (the out-of-band pin value) + peer check
seal-retentionseal a retention change INTO the journal (prune | migrate | policy-change | restore)
custody-gradeprint the custody disclosure: witness = min(authoring, retention)

§Performance & measurements

Every number is reproducible from the repo. A metric we can't reproduce means unknown, not a rounder number.

MetricValueReproduce with
Sealed append — SHA-256 + Ed25519 + SQLite commit0.17 msmake bench
Regex guardrail scan (7 lines)0.21 msmake bench
ML guardrail scan (7 lines, sklearn loaded)10.3 msmake bench
Full chain verification, 100 events (incl. JSON load)44 msverifier/verifier.py
Bundled detectors, FR + EN293 + 243 KBmake train · make train-en
Held-out attack sentences, FR + EN12/12tests/test_ml_guardrail.py
Test suite392 · 0 failingmake test

MacBook M-series, local venv — sealing/verification rows 2026-10-04. Absolute values vary with hardware; make bench on your machine is the reference.

SCAR-BENCH-114 — open-weights measurement

A published, seeded measurement protocol (docs/SCAR-BENCH.md): 120 planned trials against three local models, canary-witness scoring, raw results in-repo. Headline:

ModelCondition A (prose) — canary trip rateCondition B (primitive)Strict canonical JSON
qwen2.5-coder:3b100% (18/18)75% (15/20)0
qwen2.5:0.5b100% (19/19)78.9% (15/19)0
llama-guard3:1b100% (20/20)see results file0

Disclosure: single machine, local Ollama, temperature 0.7, per-trial fixed seeds. Measurement, not a verdict. 4 timeouts excluded, disclosed in the results file.

§Project layout

noirebox/            the package
  chain.py store.py schemas.py       hash chain, SQLite append-only store, Pydantic models
  receipt.py reconcile.py independence.py    sealed receipts, invariants, flow independence
  anchors.py witness.py merkle.py fleet.py   RFC 3161 anchoring, fleet hub
  keyholder.py hsm.py                custody: unix-socket signer, PKCS#11
  guardrail.py ml_guardrail.py llm_judge.py tiering.py   three scan engines + tiered judge
  mcp_server.py hookcli.py trajseal.py toolset.py        agent-facing surfaces
  crosschain.py digests.py siem.py replay.py pdf_export.py c2pa.py aiact.py
  cli.py main.py dashboard.py auth.py
verifier/verifier.py  standalone third-party verifier (+ tsa_roots/ pinned certificates)
bench/                SCAR-BENCH harness + published results (scar114)
tsa/                  self-hosted OpenSSL witness (root + leaf chain)
plugins/              VS Code extension · Claude Code hooks · ZCode plugin
tests/                392 tests — the invariant suite, incl. SCAR replay
docs/                 SPECS · THREAT-MODEL · ADRs · compliance & bench docs (this site)
demo/                 runnable end-to-end scenes

§Releases & distribution

ChannelArtifact
PyPIpip install noirebox
GHCRghcr.io/noirebox/noirebox:latest
VS Code Marketplace · Open VSXeditor extension — versioned separately (0.10.1)
GitHub ReleasesVSIX + tagged sources, notes per release

History

21 releases since 2026-09-19, every one in the changelog. Recent arc:

  • 0.21.0 — 2026-10-15 — external audit round: 10 findings, 0 breaking the cryptography; 8 fixed outright, NB-05/NB-08 closed in their compatible halves, each with a named test. Fail-closed defaults everywhere (unverifiable witness → exit 2, unparseable NOIREBOX_CLIENTS refuses to boot)
  • 0.20.0 — 2026-10-14 — retention sealing + custody grade: witness = min(authoring, retention)
  • 0.19.0 / 0.18.0 — 2026-10-14 — uid-gate as a tri-state disclosure (off / enforced / unavailable), custody protocol 0.2, PIN moved out of argv
  • 0.16.0 / 0.15.0 — 2026-10-13 — the key leaves the disk: keyholder daemon, then PKCS#11 token
  • 0.11.0 — 2026-10-11 — receipt/0.2.2 (judged_by + spec_hash), SCAR-BENCH-114 published
  • 0.1.0 → 0.10.0 — 2026-09-19 → 10-04 — chain, guardrails, RFC 3161, fleet hub, distribution

§Security & honest limits

The adversary model, trust boundaries and residual risks live in the threat model; the security baseline and scan methodology in SECURITY-BASELINE.md. Report vulnerabilities per SECURITY.md — please don't open public issues for them.

Known limits, assumed and tracked:

  • Payloads are not encrypted — GDPR minimization is the caller's responsibility
  • Pinned hardware root of custody is still open; the published attestation is the countermeasure until then
  • Probabilistic guardrail — measured false negatives are non-zero
  • Single-process append lock — sharding/scaling not targeted
  • 0.21.0 residual: re-hash detection stays a versioned-format decision (NB-05 compatible half)

§Compliance mapping

Full write-ups: COMPLIANCE-EU.md · ART12-MAPPING.md. A building block, not a certification — the perimeter is stated, not oversold.

ObligationAnswered by
GDPR art. 5(2) — accountability: demonstrate compliancetamper-evident journal of what the AI produced, when, on which input
GDPR art. 15/20 — access & portabilitysigned export, verifiable by the subject's own auditor
EU AI Act art. 12 + 19 — automatic event logs, retained ≥ 6 monthssealing at runtime; cryptographic log integrity; ART12 mapping doc
EU AI Act art. 55(1)(c) — GPAI incident tracking (live since Aug 2025)incidents as first-class sealed events; audit-pack for reporting
AI Act Annexe IV §2(f) — document the logging characteristicsnoirebox audit-pack generates the description with the export
eIDAS art. 41 — qualified timestamp presumptionanchor profiles for qualified TSAs + pinned-root public TSAs + OpenTimestamps
Sovereigntyself-hosted, zero telemetry, keys on your infrastructure, EU-only clouds fine

§Documentation index

Everything below lives in docs/ in the repository — markdown, versioned with the code.

DocumentContents
SPECS.mdthe source of truth: data contracts, crypto, API contract, measured performance — divergence between code and spec is a bug
ADRs.md27 architecture decision records with their context and rejected options
THREAT-MODEL.mdadversary model, trust boundaries, open residuals
AUTHORITY-LADDER.mdwho may judge what — the verdict-precedence doctrine behind receipt schemas
SCAR-BENCH.mdthe published measurement protocol: taxonomy, claims format, differential harness
COMPLIANCE-EU.mdGDPR / AI Act / eIDAS mapping with article-level references
ART12-MAPPING.mdAI Act art. 12 logging requirements → journal properties
SECURITY-BASELINE.mdsecurity scan methodology and results, with reproduction commands
VULGARISATION.mdthe whole project explained from zero, no prior knowledge assumed
ROADMAP-PLUGINS.mdplugin ecosystem direction

§ADR index

All 27 in docs/ADRs.md — each with context, decision and what was rejected:

001Guardrail: regex → ML micro-model → LLM judge last
002Reject Meta's Llama Prompt Guard 2 as the engine
003Third-party detectors: documented path, not embedded
004OAuth2 (JWT bearer) + rate limiting, optional by config
005PDF attestation: proof for humans, JSON for machines
006RFC 3161 anchoring: close the "insider with the key" gap
007Self-hosted TSA: root + leaf chain, not a single certificate
008Multi-TSA anchoring with pinned roots
009OpenTimestamps witness: the compute-grade layer
010AI-Act-shaped events + audit-pack
011Content-provenance events (model-collapse research, in journal form)
012Model-trajectory sealing: the flight recorder's flight recorder
013Transcript-agnostic sealing + per-project journals
014JWT secret derived from private material, never public data
015The tier-2 judge: local LLM, house taxonomy, JSON-pinned, honest when silent
016The tiered scan: the judge pays only for doubt
017Fleet hub v0: one seal covers N journals
018Key via secret-manager injection, loud on mismatch
019Receipt schema v0.2: verdict binds its check, evaluator, reach
020The denominator: attempt-first sealing
021Flow independence is a testable invariant
022Custody vs anchoring: map the trust boundary, disclose the grade
023Seal the belief, not only the action
024Cross-chain reconciliation: consumption edges, partial order
025Toolset baseline: tool descriptions are instructions, pin them
026Admission receipts: seal the basis, not only the act
027Post-quantum evidence: hybrid signing behind the signer interface

§Contributing

  • Branches + pull requests; main is protected (PR + required checks). No direct pushes.
  • make test — 392 tests, must stay green · ruff for lint (B/SIM/C4 enforced) · CI runs the suite + Bandit + a stub-LLM coverage pass.
  • The spec is the contract: a code change that diverges from docs/SPECS.md updates the spec in the same PR — or it's a bug.
  • Numbers cited in docs must be reproducible (make bench, make test) — measured, never rounded up.
  • MIT. Issues and PRs welcome; security issues go to SECURITY.md, not public issues.

NoireBox is engineered in the Vosges, France 🇫🇷. NoireBox is a building block, not a certification — it proves the integrity of the record; data minimization and key-root decisions remain the deployer's.