Every gate a bank's procurement asks about, assessed against what is actually in the code and on the operator on 2026-09-29. Done names the file, route or test that proves it. Nothing here claims a certificate, an audit, an insurer, an opinion or a board that does not exist.
47 done - 0 partial - 1 refused by design - 13 need an outside party or the maintainer's decision - 0 not started.
G1 - Security & crypto custody#
Owner: CISO, crypto governance
| Gate | Status | Evidence |
|---|---|---|
| HSM / KMS-native corridor-secret custody (deal-killer) | Done | The corridor secret and every signing key can live in the bank's HSM or KMS: mirror-custody spawns the bank's own tool (no shell), with KCV checks at the ceremony and before every run. See custody; tested in kernel/mirror-custody.test.js and server/witness-supply.test.js. |
| Key rotation ceremony for the corridor secret (deal-killer) | Done | Rotation by corridor version, never by clock: a signed plan (rotate-plan), a rehearsal against both HSM keys (rotate-rehearse), and a state machine with an honest rollback window and crypto-shredding of the old key. Rehearsed end to end in server/rotation-rehearsal.test.js. See corridor key rotation. |
| Key hierarchy and separation | Done | Member root keys delegate scoped, short-lived desk keys (mirror-delegation, mirror-bander delegate); origination, relay, agreement and witness keys are separate ceremonies. No chains: delegates cannot delegate. See access control. |
| FIPS 140-2/3 answer, documented | Done | FIPS position: no validated module is shipped or claimed; the algorithms are approved; key operations can run inside the bank's own validated HSM. mirror-bander fips reports the running process's mode. |
| SBOM for every artefact | Done | /sbom.cdx.json (CycloneDX 1.5), freshness-tested by server/sbom.test.js. |
| Signed release provenance (SLSA / in-toto) | Done | /provenance.intoto.json: in-toto v1 + SLSA provenance v1 in a DSSE envelope, signed by /release-key.jwk, over every published artefact and every source file by digest. SLSA Build Level 1 - not L2/L3, which need a hosted builder. See provenance. |
| Reproducible builds (deal-killer) | Done | Every subject of the signed provenance rebuilds byte for byte: the operator, bander and kit bundles, the vectors, the OpenAPI document, the SBOM and all four native binaries. One command, node server/reproduce.js, verifies the provenance, confirms the sources are the release's, rebuilds in a disposable copy at another path and compares each digest. Reproduced on 29 Sep 2026 on Windows 11 and on Ubuntu 24.04 (Node 24.16.0): 10 of 10 both times, the Linux run against provenance signed on Windows. server/reproduce.test.js rebuilds the JavaScript artefacts on every build. Not shown: a rebuild by anyone but the maintainer (both machines are one computer's), and the source tree is not public - a reviewer receives it in diligence; publishing it is the maintainer's decision. See provenance. |
| Independent penetration test + retest | Outside party | None commissioned and none claimed. Needs a testing firm. |
| Threat model with a public review history | Done | Review history: every review that changed the threat model, what it found, what changed and the test that pins it. No named external reviewer yet - listed only with consent. |
| Secure development lifecycle statement | Done | How Mirror is built - the practice as it is, including where it falls short (one maintainer, no public repository, no second reviewer). |
| Disclosure programme with a published SLA | Outside party | Waiting on the maintainer's decision about a contact route. No intake channel is invented in the meantime. |
G2 - Regulatory & contractual#
Owner: Legal, vendor management, DORA
| Gate | Status | Evidence |
|---|---|---|
| DORA Article 30(2) clause pack, all provisions (deal-killer) | Outside party | Counsel's to draft. Product-side support is written: contract schedules (audit, exit, location, incidents, cooperation), each promise naming its mechanism. |
| Critical-function scoping opinion, in writing (deal-killer) | Outside party | Counsel's to write. The facts it needs are test-pinned in /cannot. |
| TLPT cooperation clause | Outside party | Contract text for counsel. |
| Incident reporting support pack (4h / 72h / 1 month) | Done | mirror-incident: the three clocks from stated times, and what the rail did during the window counted only from signed records the bank verified. See incident support. Not legal advice. |
| GDPR pack with a pre-filled DPIA | Done | DPIA pre-fill: every fact marked as code-enforced (with its test) or a design statement; the conservative reading of pseudonymised commitments. |
| Records retention schedule, erasure conflict resolved | Done | Retention schedule: per record class; erasure resolved by crypto-shredding the secrets that make commitments linkable, after the retention period and any hold. Automated pruning of two classes is stated as not yet built. |
| Audit rights clause with notice periods | Done | Drafted for counsel: Schedule A - continuous offline verification without notice, the supervisor seat, 30 days' notice for on-site audit. |
| Exit and portability clause | Done | Drafted for counsel: Schedule B - standing portability, a 30-day export, succession, continued verifiability. |
| Liability cap + published E&O / PI cover | Outside party | No cover is held and none is described. Needs an insurer. |
| Data residency commitment | Done | Processing and storage in the EU. The operator runs in a Cloudflare Durable Object restricted to the EU jurisdiction, read from the object's own id at runtime; if it is unreachable, the request is refused and processed nowhere else. The store is in AWS eu-central-1. The operator signs this at GET /residency; a fail-closed meets(record, "eu") ships in the kit and every SDK. Tested in server/residency.test.js and kernel/mirror-residency.test.js. The record lists what it does not cover: the first edge hop (forwarded unread), object ids in Cloudflare's logs, and storage that is declared rather than observed. Clause drafted for counsel in Schedule C. See data residency. |
| Published sub-processor register | Done | Sub-processors: Cloudflare and Supabase, what each processes and where, the shared database project disclosed, 30 days' notice of change proposed. |
| AMLR / 6AMLD position paper + R.16 alignment | Done | Position paper for counsel: AMLR information-sharing partnerships, both directives called 6AMLD, and why R.16 compliance stays with the payment chain. |
G3 - Assurance & certifications#
Owner: Third-party risk, vendor assurance
| Gate | Status | Evidence |
|---|---|---|
| SOC 2 Type II (deal-killer) | Outside party | Not held, and never shown. |
| ISO 27001:2022 | Outside party | Not held. |
| ISO 42001 | Outside party | Not held. Mirror runs no model (deterministic kernels). |
| Published pen-test summary + retest | Outside party | Depends on the pen test. |
| ISO 22301 or equivalent, tested | Outside party | Not held. |
| Financial-crime programme alignment statement | Done | Programme map: what Mirror contributes to each part of an AML/CFT programme, and what it deliberately does not. The per-bank mapping is the bank's. |
| Regulator engagement pack | Done | Supervisor pack: one hand-over document - what it is, what it cannot do, how to check it offline, the seat, the known limits, who operates it. |
G4 - The model#
Owner: Protocol
| Gate | Status | Evidence |
|---|---|---|
| Versioned schema with a published stability policy (deal-killer) | Done | /stability-policy.md; the spec is pinned by hash on every record. |
| Extensible leg-role registry, without breaking v1 (deal-killer) | Done | An open lowercase namespace with defined unknown-role behaviour; independence comes from the signing key's class, never the role name. |
| Formal canonicalization / normalization spec | Done | SPEC §8.2, normative: canonical JSON, length-prefixing, exact decimal, every commitment derivation, key ids, low-S, webhook ids, and the wall's key normalisation - each pinned by named vectors. |
| Versioned signal / error taxonomy | Done | /signals.json, drift-tested against every code the operator can return. |
| /cannot: the refusal list as a contract (deal-killer) | Done | Signed at GET /cannot; version 1.1.0, 39 refusals, change-controlled by test. |
| Stable identifier scheme | Done | mirror-locator: domain-separated locator commitments with normalisation rules and vectors. |
| Independent-implementation interoperability test | Done | conformance/reference-impl.js, written from the spec text and importing nothing from the kernels, reproduces every vector byte for byte - including fine, basis, origin, both witness axes, webhook ids and canonical JSON. |
G5 - Architecture & operations#
Owner: Infrastructure, bank platform team
| Gate | Status | Evidence |
|---|---|---|
| Streaming / event-driven, not batch (deal-killer) | Done | Signed webhooks to proven parties only, first attempt immediately (under 2 s on loopback, test-enforced), a stated retry schedule. See webhooks. |
| Idempotency and replay semantics (deal-killer) | Done | A retry and a failover resubmission are one verdict: counts key on the judgement, not signature bytes (mirror-idempotency). |
| HA bander, active-active with a degraded mode | Done | Two hosts band to byte-identical commitments; the operator counts one verdict; an unreachable HSM stops the run with no fallback. Tested in server/bander-ha.test.js. See HA bander. |
| Clock discipline, documented and monitored | Done | Signed, nonce-bound POST /time; mirror-clock proves an offset interval from the round trip; client.clock() monitors it. See clock discipline. |
| Backfill and re-processing | Done | Party-side re-banding under a new table version makes new verdicts on the append-only ledger; /replay bounds a period. See HA bander. |
| Documented RTO / RPO, tested | Done | RTO / RPO: a recovery drill on every build (restore and rebuild from members' signed copies to the same log root) and measured restore times. The store's backup setting is stated as unconfirmed. |
| Business continuity plan, tested annually | Outside party | Needs an operating organisation to test it. |
| Multi-operator resilience | Done | mirror-cosign, federation, succession, witness-cosigned heads with gossip fork detection. |
| Documented graceful degradation | Done | Degradation runbook: every failure degrades toward not clearing and toward telling the member; each row is tested. |
G6 - Integration & enterprise plumbing#
Owner: Bank engineering, procurement
| Gate | Status | Evidence |
|---|---|---|
| Terraform provider (deal-killer) | Done | A compiled provider (terraform/provider, Go, terraform-plugin-framework). It manages webhook subscriptions with drift detection and import, and sends signed governance events; a data source reads the registry. The key never enters Terraform: the bander signs. Tested with a real terraform 1.16.4 against an operator (server/terraform-provider.test.js): apply, drift recreated, import, no key in the state. The module is validated as well (server/terraform-e2e.test.js). Not on the Terraform Registry: publishing is the maintainer's decision. See Terraform. |
| OpenAPI 3.1 + SDKs (Python, Java, .NET, Go) (deal-killer) | Done | /openapi.json (generated, drift-tested, raw-data wall in the schema), and verifying SDKs in JavaScript, Python, Go, Java and .NET. Each is held to the 100 shared cases in /sdk-vectors.json and run against an operator (server/sdk-polyglot.test.js). Shipped as source; not on PyPI, Maven Central, NuGet or the Go module proxy. See SDK. |
| SSO (SAML 2.0 + OIDC) with SCIM | Refused by design | By design, not at the operator: Mirror authenticates keys, not people, and will not hold a staff directory (/cannot staff_directory). SSO sits at the member's gateway, which holds scoped desk keys. See access control. |
| Fine-grained RBAC | Done | Delegated desk keys with scopes (reconcile, governance, webhooks, read), enforced on every request, revocable live. |
| Native audit-log export to the bank's SIEM | Done | POST /audit/export: the member's own signed records as JSON lines, signed batch, cursor paging. The operator stores no SIEM credential. |
| Reference connectors for the top trade platforms | Done | Reference connectors for the messages trade platforms exchange: SWIFT MT700 / MT710 / MT720 documentary credits, and DCSA eBL 3.0 bills of lading (connectors/mirror-connect.js; tested in kernel/mirror-mt700.test.js and kernel/mirror-ebl.test.js). They sit beside the origination sidecar, the ISO 20022 ingest, the e-invoice readers and import mapping. Tested against messages built from the published standards. No named platform is connected; that is per-platform work with its owner. See connectors. |
| Webhooks with documented retry and delivery guarantees | Done | At least once, dedupe id stable across retries and failover, 7-step schedule, 410 pauses, redelivery. See webhooks. |
| Migration and import tooling | Done | import-check (a dry run reporting counts and column names only), band --mapping, table-import. Tested. |
| Sandbox with production-parity auth, RBAC and audit | Done | server/sandbox.js: the same code with every production default on; its verdicts can never verify as production ones. See sandbox. |
G7 - Governance & protocol stability#
Owner: Board, ecosystem
| Gate | Status | Evidence |
|---|---|---|
| Published change control over the refusal list (deal-killer) | Done | server/cannot.test.js: no removal or weakening within a major version; additions announced as minor versions. |
| Protocol stability policy (deal-killer) | Done | /stability-policy.md. |
| Public RFC process | Done | RFC process: what needs an RFC, a 30-day last call, published objections and answers. Today the maintainer decides - stated. |
| Independent security advisory board, named | Outside party | No names are published until people have agreed. |
| Transparency-log operations policy | Done | Log policy: one history, mirroring, compelled withholding, what happens on a detected fork. |
| Roadmap governance, published and signed | Done | GET /roadmap signs the roadmap's Refused list (binding, change-controlled) and pins the page by hash. |
Generated by server/deal-gates.js; the same data drives the private tracker.