Promethean · Security & Compliance Brief
Document type Security review packet · CISO · Security Architect · third-party audit firm
Length ~7 pages
Last revised 2026-05-18 (v1.3 — post-launch hardening)
Companion docs TECHNICAL_ARCHITECTURE · PROMETHEAN_THEORY_AND_FINDINGS (canonical theory + adversarial findings) · substrate/docs/OPERATIONS_RUNTIME_AI.md (operational runbook)
1. Executive summary
The substrate is built on a single Ed25519 trust anchor and twelve cryptographic commitment chains (L1–L12). The runtime-AI containment layer (L12) signs every LLM call inside emitted products. The verifier is plain JavaScript with zero dependencies (~500 lines; exact LoC + sha256 served live at /api/facts/verifier-loc); it runs offline, against your copy of the chain, with your choice of Bitcoin full node. You verify the producer. The producer cannot verify you.
Six independent threat-model audit passes (5 external + 1 internal hammer pass) closed 40 findings before v1.2. Phase R reliability stack adds seven mechanisms that demonstrate (not just claim) the chain stays correct under change: golden chains, property-based fuzz, differential verifier, watch daemon, mutation testing, cross-language port, direct OpenTimestamps anchor.
Post-launch hardening (2026-05-17 → 2026-05-18). Between launch and this revision, the production deployment underwent six iterative adversarial stress-test rounds finding 31 distinct findings, all closed. Severity dropped monotonically after round 2; round 5 findings required concurrency, DNS rebinding, attacker-controlled chunked-transfer encoding, or specific timing windows to exploit. The full trajectory is in the canonical paper §5 (PROMETHEAN_THEORY_AND_FINDINGS); commit references for every fix are on /release-notes. Material shipped fixes summarised in §1a below.
1a. Post-launch hardening — what shipped
The fixes that materially change the threat surface vs the v1.2 brief:
| Finding | Fix shipped | Commit |
|---|---|---|
Email TOCTOU at createWorkspace |
Atomic SET NX claim before workspace write; legacy-scan race a known residual (§10) |
c69bd08 |
CSRF wildcard on *.vercel.app |
Pinned to this project's host pattern; anonymous form endpoints require Origin match | 3738fea, 95a9689 |
| Verifier passing empty/EMPTY files as PASS | verify.mjs distinguishes EMPTY from PASS, shows hash length on mismatch |
1602f62 |
| 10 concurrent ingests dropping 4 entries (data loss) | Per-workspace Redis lock with exponential backoff + Lua-EVAL atomic release. Lock perimeter extended to anchor cron, tier change, delete, session, key rotation | 3a56653 |
| Verifier accepts entries with unknown top-level fields | Closed-field schema check in verify.mjs: any unknown top-level key fails verification |
3a56653 |
| Chain rewind on out-of-order writes | Distinguish out-of-order from true replay; bounded backoff allowance | ca9b960 |
Webhook signing secret leaking via toPublicView |
Stripped from public-view shape; reveal-only via authenticated /api/workspaces/.../reveal-webhook-secret route |
7446e08, 024adcc |
| SSRF via IPv6 / DNS-rebind / chunked-transfer body | IPv6 SSRF allowlist + DNS-rebind defense + chunked-transfer body cap | 7446e08, 3443161 |
| Identity squatting via unverified email | Magic-link email verification on signup: workspaces.verifiedAtIso gates SDK ingest + paid-tier upgrade; /api/auth/verify + /verify complete the flow |
024adcc |
| Stripe webhook replay | event.id deduped via SET NX with 30-day TTL — duplicate events ack as no-op |
024adcc |
| Verifier trust-anchor opacity | verify.mjs prints the trust-anchor sha256 fingerprint banner on every run |
024adcc |
The combination closes the round-1 → round-6 audit set. The two findings deferred to backlog are documented in §10 as known residuals.
2. Threat model
The substrate's audit-readiness rests on the producer's signing key not being compromised, used to retroactively sign a fabricated chain, or extracted from the operator's infrastructure. Every defence below is structured around that assumption.
| Threat | Mitigation | Residual risk |
|---|---|---|
| Operator's signing key compromised | Shamir 3-of-5 backup; HSM-backed signing (PKCS#11 / YubiHSM / CloudHSM / Luna); OpenTimestamps anchor caps damage to forge-forward window | Pre-compromise chain remains valid until anchored block; post-compromise chain verifies as legitimate until rotation |
| Operator's substrate filesystem corrupted | Linux chattr +a append-only attribute; S3 Object Lock COMPLIANCE mode; auditd / Wazuh FIM |
Operator must keep chattr set; we document this in OPERATIONS §4 |
| L12 chain backdated (fake history) | Bitcoin anchoring every 5 minutes via OpenTimestamps; the operator cannot create chain entries dated before the most recent anchor block | Pre-OTS-launch entries cannot be backdated proven; mitigated by initial-anchor publication |
| Spec bypass at SDK boundary | Adversarial autoplay runs 8 closed-enum attacker strategies in CI; spec-violations caught at build time, never reach prod | A custom forked SDK could bypass — caught by mutation testing + reference-deployment cross-check |
| LLM output exfiltrating PII via canonical form | Input never recorded raw; only inputHash (SHA-256). Output canonical hash is over schema-projected form (no raw text). Schema is closed-enum where possible |
Output text fields under explicit operator review during pilot |
| Substrate code itself compromised (supply chain) | Apache 2.0 source on GitHub; build receipts (L4) signed; deterministic builds; subresource integrity on verify.mjs |
NPM / PyPI distribution depends on registry security (no different from peer projects); audit-bundle ships with embedded verifier |
| Cross-language drift (JS / Python emit different bytes for same input) | Issue 31 (integer-only numerics) + canonical-form spec + fixture-driven differential tests (8 tests in runtime-ai-py/tests/test_cross_language.py) |
Rust port differential tests roadmap Q3 |
| Regulator presented with forged chain by a third party | Chain signed under our published Ed25519 key. Forgery requires the key. Verifier accepts only signatures under the published public key | If the operator publishes a key rotation, regulators check the rotation note; pre-rotation entries remain anchored |
| Identity squatting on signup (e.g., paysafe@... by an attacker) | Magic-link email verification before any SDK ingest is accepted; workspaces.verifiedAtIso gates ingest + paid-tier upgrade |
Operators can still squat unrelated domains in the workspaceId namespace (a side-channel attestation matter, not a chain-integrity one) |
| Closed-field schema bypass (attacker adds a JSON field outside the canonical-form allowlist) | verify.mjs rejects entries with unknown top-level fields — see §1a and paper §2.1.2 / §5 round 3 |
None known |
| Concurrent-write data loss on the chain | Per-workspace Redis lock with atomic Lua-EVAL release + exponential backoff. 10/10 entries land under the same conditions that previously lost 4 (paper §5 round 2) | Lock perimeter shares ingest + cron domain; ≤60s starvation possible at extreme write rates (acceptable for current load — §10) |
3. Cryptographic primitives
All primitives use IETF/NIST-standard algorithms with reference implementations:
| Primitive | Algorithm | Implementation |
|---|---|---|
| Per-entry signing | Ed25519 (RFC 8032) | Node crypto.sign · Python cryptography.hazmat.ed25519 · Rust ed25519-dalek |
| Chain hashing | SHA-256 (FIPS 180-4) | Node crypto.createHash · Python hashlib |
| Canonical form | JSON with explicit field order, integer-only numerics (Issue 31) | Substrate-defined spec; canonical-form v1 since ADR (tt) |
| Bitcoin attestation | OpenTimestamps (Merkle tree + Bitcoin block) | ots CLI; air-gappable proof verification |
| Cross-language byte parity | Fixture-driven differential test | JS-authoritative fixture; Python re-derives |
No proprietary primitives. No "audit cryptography by Promethean." Every algorithm is verifiable against its reference implementation.
4. Key custody
The Ed25519 signing key is the most operationally important secret. Documented in OPERATIONS §2; summarised here.
Generation: Air-gapped machine (Tails / NixOS ISO). HSM-generated preferred (key never leaves device). Plaintext seed destroyed after backup.
Backup: Shamir's Secret Sharing 3-of-5. Geographically separated custodians (SRE lead, CTO, External Counsel, Security Officer, Cold Storage Vault). Quarterly recovery rehearsal.
Production signing: HSM-backed (PKCS#11, YubiHSM 2, AWS CloudHSM, Thales Luna). v1.2 ships in-memory signing as default; HSM injection point in v1.3 native; current ops workaround is a local patch to signCanonical (one-function diff).
Rotation: Chained co-signing — a rotation entry is signed by BOTH old and new keys; verifier accepts entries signed by either key for IDs ≤ rotation.id, only new key for IDs after. Publicly published transparency note accompanies every rotation.
Compromise response: Five-step runbook (OPERATIONS §2.4): stop appenders → verify last good anchor → rotate → publish compromise note → file CVE if root cause is substrate code (zero such cases to date).
5. Filesystem hardening
The L12 log file and its sidecar are the substrate's primary on-disk artifacts.
Linux (ext4 / xfs): chattr +a makes the JSONL log append-only at the kernel level. Even root cannot truncate without removing the attribute (requires CAP_LINUX_IMMUTABLE).
AWS S3: Object Lock in COMPLIANCE mode (not GOVERNANCE) — even the AWS root account cannot override the retention period.
File Integrity Monitoring: auditd rules ship in the runbook. Wazuh/OSSEC configuration provided. Any non-promethean-runtime-ai-user write to the log triggers an alert.
Multi-process coordination: Single-writer architecture recommended; in-process serialisation via writeQueues map (one promise queue per log path). For multi-host: single-writer service + gRPC, OR sharded chains + nightly aggregation, OR operator-added proper-lockfile wrapper. Documented OPERATIONS §5.
6. Regulatory mapping
Each substrate output maps to specific articles of EU + sectoral regulations. This is the mandate-mapping table a regulator will read:
| Substrate output | Maps to | Question it answers |
|---|---|---|
| L12 receipt chain (chain integrity) | AI Act Art. 12 (record-keeping), Art. 13 (transparency) | Was each high-risk AI output produced under a declared, bounded schema? |
| Per-entry Ed25519 signature | GDPR Art. 5(1)(f) (integrity), Art. 32 (security of processing) | Has any record of automated processing been tampered with? |
reviewerVerdict field |
GDPR Art. 22 (automated decisions), AI Act Art. 14 (human oversight) | Did a human review the decision when the spec required it? |
fallbackTriggered field |
AI Act Art. 15 (accuracy + robustness) | When the model failed, did the system fall back to a declared safe state? |
inputHash (PII-safe) |
GDPR Art. 25 (data minimisation) | Is the audit trail itself a privacy liability? No — raw inputs never recorded. |
| OpenTimestamps anchor | DORA Art. 6/8 (digital operational resilience), NIS 2 Art. 21 | Can the operator backdate the audit trail? No — Bitcoin attestation prevents it. |
modelIdentity per entry |
AI Act Art. 13 (model provenance), PSD3 (vendor diligence) | Which exact model version made this decision? |
schemaValid + canonical form |
AI Act Art. 15 (technical robustness) | Did the model output stay within the declared output surface? |
7. Security review pre-answers
Standard questions a security team will ask in due diligence:
Q: How do you protect against a supply-chain attack on the npm package?
A: The Apache 2.0 source is on GitHub. Build receipts (L4) are signed under the same trust anchor. Subresource integrity hashes on verify.mjs. The audit-bundle (buildRunnableAuditBundle) ships with an embedded verifier so regulators can verify without trusting the npm pipeline.
Q: What happens if your Ed25519 key is exfiltrated?
A: Bitcoin anchoring caps the damage: any forged-forward chain dated past the most recent anchor block is provably impossible. Pre-compromise entries remain trustable up to the last clean anchor. Rotation procedure in OPERATIONS §2.3.
Q: Is the verifier itself audited?
A: It's plain JS, zero dependencies, Node 18+ stdlib only. ~500 lines — the exact LoC + sha256 of the deployed file are at /api/facts/verifier-loc and recompute on every request. Read every line in one sitting. Plus: the differential verifier test asserts the embedded verify.mjs agrees byte-for-byte with the substrate's TypeScript verifier on every test case. The round-3 hardening added a closed-field schema check, so an attacker can't slip an unknown top-level JSON key past the canonical-form hash.
Q: Can the verifier be tricked by a malicious chain file?
A: Property-based fuzz runs 700+ random scenarios per CI run, including malformed JSONL, truncated entries, tampered fields, swapped signatures, broken prevHash linkage. Every drift mode is caught.
Q: How do you handle PII in the chain?
A: PII never enters the chain. The L12 entry records inputHash (SHA-256 over canonical input), not the input. Output is canonical-hashed under the schema-projected form. Operators with PII review concerns can inspect the schema before pilot. Reference deployments (paysafe, cliniclens, civicgate) carry zero PII in their published chains.
Q: Do you log anything from verify.mjs?
A: No. Zero telemetry. The verifier is offline-capable. Run it air-gapped against a local Bitcoin node — we have no way to know you ran it.
Q: What's your incident-response runbook?
A: OPERATIONS_RUNTIME_AI.md §8 (DR). Loss of L12 file (S3 replica + OTS-anchored snapshot + L1 commit). Loss of signing key (Shamir reconstruction; if irrecoverable, chain freezes at current HEAD + transparency note + new chain). Bitcoin reorg (≥6 blocks; mitigated by continuous re-anchoring).
Q: How do you handle multi-tenancy isolation?
A: R4 primitives ship today. tenantId is a first-class field on L12 entries; tenant-scoped verification + summarisation + alerting. Reserved __substrate__ synthetic tenant for single-tenant deployments; reserved __ prefix for substrate-internal scopes. requireTenantField option for hosted-Author deployments where every entry MUST identify the tenant.
Q: SOC 2 / ISO 27001 / FedRAMP? A: Not yet — pre-revenue. The substrate's verifiability properties exceed what SOC 2 audits typically require (cryptographic chain + Bitcoin anchor + cross-language byte parity); we'll pursue SOC 2 Type II once operator-pilot revenue justifies the audit cost. For now, audit firms evaluating the substrate read the ADRs + run the verifier directly.
Q: Penetration testing?
A: Six audit passes (5 external + 1 internal hammer pass) closed 40 findings before v1.2. Six further adversarial stress-test rounds (2026-05-17 → 2026-05-18) closed an additional 31 findings — full trajectory in PROMETHEAN_THEORY_AND_FINDINGS §5. Adversarial autoplay runs 8 closed-enum attacker strategies in CI. An external pen-test (Cobalt or Hacken) with a public one-page summary is on the roadmap for 2026-Q3 — see /roadmap.
Q: Email verification — when does it gate?
A: Magic-link verification at signup. workspaces.verifiedAtIso is null until the operator clicks the link served via /api/auth/verify (page at /verify). Until then: SDK ingest is refused; paid-tier upgrades are refused; downgrade to Dev is allowed (de-escalation, not a privilege gain). Hash-only token storage server-side with 24h TTL; the click-through sets verifiedAtIso and flips the gate.
8. Audit checklist (for a regulator preparing certification)
From OPERATIONS_RUNTIME_AI.md §10, abbreviated:
- Key custody documented (HSM model, Shamir split, custodian list)
- HSM signing demonstrated via cross-language fixture test
- L12 file has
chattr +a(Linux) or S3 Object Lock COMPLIANCE mode - FIM alerting in place (auditd / Wazuh / OSSEC)
- OTS anchoring cron active; most recent anchor age < 6 h
- Daily verification job runs and is monitored
- Federation peers' anchors verified nightly (if federated)
- DR runbook tested in the last 90 days (Shamir reconstruction drill)
- Compromise-response runbook tested in the last 180 days
- Mutation testing (Stryker) score ≥ 75% on Phase R modules
- Cross-language port CI green against pinned fixture (if Python-emitted products exist)
9. Sub-processors + data flow
| Sub-processor | Purpose | Data shared |
|---|---|---|
| OpenTimestamps calendar servers (alice.btc.calendar.opentimestamps.org, bob.btc.calendar.opentimestamps.org, finney.calendar.eternitywall.com) | Bitcoin time attestation | SHA-256 hash of L12 HEAD only (no chain contents) |
| Bitcoin network | Final attestation anchor | OpenTimestamps Merkle root (one tx per ~hour of substrate operation) |
| Anthropic / OpenAI / etc. (operator's choice) | LLM provider | Input as the operator's product sends it; output as the model returns it. The substrate sits between but does not forward additional metadata to providers. |
| AWS / Azure / GCP (operator's choice) | Hosting | Whatever the operator chooses; substrate is provider-neutral. |
No Promethean-operated sub-processor for the open tier. The hosted Author tier (roadmap) will be operator-electable EU / US / dedicated.
10. Vulnerability disclosure
security@promethean.software · GPG-encrypted · <24h acknowledgement · 90-day coordinated disclosure window. Process documented at /feedback. CVE assignment + transparency note + new test guarding against regression for every fix.
Zero security advisories to date (architecture pre-revenue; first operator pilot pending). Public security log will be maintained from first paid operator forward.
10. Known residuals (publicly tracked)
Honest enumeration of weaknesses identified during stress testing that have NOT been fully closed. These are tracked publicly rather than buried.
- Legacy-data-migration race in
createWorkspace(round 2 audit C). TheSET NXatomic claim closes the primary email TOCTOU, but a concurrent caller can briefly read the workspace-id of a not-yet-committed signup during the legacy-data-scan window. Closure requires a Lua/EVAL atomic block that does both the SET-NX claim and the legacy-scan in one round-trip. Backlog. 'unsafe-inline'on script-src in CSP. Next.js App Router currently requires it for hydration. A per-request nonce-based CSP is the proper fix; the refactor is non-trivial. Backlog.- Lock-domain sharing between anchor cron and ingest. Per-workspace lock covers both; a long-held ingest could in principle starve a cron tick for that workspace for up to ~60s. Mitigation is next-cron-retry idempotency, not exclusion. Acceptable at current write rates; worth revisiting at higher throughput.
Paper §7.2 records the same list with deeper explanation.
11. References
PROMETHEAN_THEORY_AND_FINDINGS(canonical theory · adversarial findings · verifier benchmarks)OPERATIONS_RUNTIME_AI.md(full operational runbook)- ADR (tt), (uu), (vv), (ww), (xx) — Phase R complete
TECHNICAL_ARCHITECTURE.md(this directory)- Six audit-pass test files:
runtime-ai-r-hardening{,-v2,-v3,-v4,-v5,-v6}.test.ts - Reliability stack tests:
runtime-ai-golden-chains.test.ts·runtime-ai-property-tests.test.ts·runtime-ai-differential.test.ts /release-notes— buyer-facing roadmap with commit references for every fix/api/facts/verifier-loc— live LoC + sha256 of deployedverify.mjs/api/facts/trust-anchor— both published Ed25519 public keys with sha256 fingerprints
This brief is intended for review by security-cleared personnel. Source for every claim is in the substrate repository at the cited ADR / test file. Falsifiable predictions per ADR — verify directly against the codebase. Last revised 2026-05-18.