Concepts · Templates
Test-mode safety
Three independent guards make a test-mode document indistinguishable from a live one impossible: PDF watermark (artifact), KMS key segregation (cryptographic), amber TEST badge (UX). All three are required; each is sufficient by itself.
Last updated
A test-mode document MUST be impossible to mistake for a live one. The contract is three independent guards, each sufficient by itself, all required:
| Guard | Layer | Defeats |
|---|---|---|
| PDF watermark | Artifact | Visual confusion — screenshot, print, email forward |
| KMS key segregation | Cryptographic | Programmatic verification (signed receipt validation) |
Amber TEST badge | UX | Dashboard misclicks |
PDF watermark
Every page of a non-live (test or sandbox) document carries a diagonal grey banner at 30% opacity:
TEST · DO NOT FILE · sig 0123456789abcdefThree-token ·-separated format matches the test-mode banner in the
dashboard chrome and the TEST badge label. Watermark renders in the
brand sans (Geist subset, embedded in the PDF) for visual continuity.
The fingerprint suffix is display-only. The cryptographic guarantee that this PDF is test-mode is the next guard. The watermark is the human-facing artifact guard; it answers "did I just print a test document?" not "is the signature on this PDF actually valid?".
A structural marker
(matter:test-mode-watermark:<fingerprint16>) is also written into
the PDF's Keywords metadata. Downstream verifiers (the evidence-
bundle builder, the dashboard preview) can confirm a PDF is test-mode
without re-decoding the content stream.
KMS key segregation
Live and test signing keys are distinct AWS KMS CMKs:
alias/matter-audit-live-2026— live mode chain-signing key.alias/matter-audit-test-2026— test mode.- (Sandbox uses its own CMK on the same axis.)
A signature produced by the test CMK CANNOT validate against the live
key. Verifiers fetch
https://api.mattermode.com/.well-known/matter-keys.json
and get mode-segregated key sets:
{
"live": {
"active": [{ "fingerprint": "...", "algo": "Ed25519", "rotated_in": "2026-05-01" }],
"rotated_out": [{ "fingerprint": "...", "rotated_in": "2025-01-01", "rotated_out": "2026-05-01" }],
"revoked": []
},
"test": { ... }
}Pre-rotation receipts remain verifiable post-rotation because
rotated_out keys are kept indefinitely (just not used for new
signatures). The _active is what new signatures use.
This is the receipt-validation guarantee. A document signed in test
mode produces an AuditEntry with a chain_signature that only
validates against the test public key. If you point a verifier at the
live key set, validation fails.
Amber TEST badge
Every dashboard surface that renders a non-live resource shows the
TestModeBadge (from @matter/components):
- Document list rows
- Document detail page hero
- Preview thumbnails (top-right overlay)
- Sidebar mode indicator
The badge is amber, matching the mode-banner. NOT destructive red. Round-2 design audit fix: red is reserved for irreversible actions (dissolution, key revocation). Test mode is advisory, so it gets amber.
The badge's aria-label and title attributes both carry the full
banner copy ("TEST · Sandbox · Documents are not legally binding")
so screen readers and tooltip-on-hover surface the same message.
Cross-mode laundering is structurally impossible
Three database-layer guards make a test artifact unable to extend a live audit chain:
AuditEntry @@unique(orgId, mode, prevHash). A test-mode row'sprevHashlookup is scoped tomode = test; there is no path to point at a liveentryHash.Document.modeis part of every read query. Ask_test_token seesmode = testrows only; cross-mode queries return empty.- Webhook signing secrets are mode-scoped.
whsec_live_*andwhsec_test_*come from distinct hierarchies; a test event can never be signed with a live secret.
The cumulative effect: every reasonable adversary (a confused engineer, a compromised tier_3 token, an insider with API access) is structurally blocked from passing a test artifact off as live.
See also
- Test, sandbox & live mode — the full three-mode model.
- Evidence bundle — verifier-side validation against the published key catalog.