Undercroft's whole premise is hardened memory — vulnerability reports are taken seriously and handled privately.
Please do not report security vulnerabilities through public GitHub issues.
- Preferred: GitHub private vulnerability reporting (Security → Report a vulnerability) — keeps the report, discussion, and fix coordination private until disclosure.
- Alternative: email compufreq@proton.me with subject
[SECURITY] undercroft: <short summary>.
Include what you can: affected version/commit, vault level (sealed / hmac-only), reproduction steps, and your impact assessment.
Response expectations: acknowledgment within 72 hours; an initial assessment (accepted / needs-info / declined, with reasoning) within 7 days; accepted reports are fixed in a priority release with a GitHub security advisory crediting the reporter (unless you prefer anonymity). Please allow coordinated disclosure — up to 90 days before public details; usually far faster.
Only the latest release receives security fixes. Releases are
self-contained and migration is lossless (open the palace with the new
binary, or export / import) — please reproduce against the latest
version before reporting.
Undercroft's vault layer protects memories at rest: disk theft, cross-vault bleed, and offline tampering of the database or manifest (XChaCha20-Poly1305 AEAD, per-vault HKDF-SHA256 key derivation, HMAC-SHA256 record tags plus a tamper-evident audit chain). It does not defend against an attacker who can read process memory while a vault is unlocked, nor against a compromised host OS.
Above that layer the served surfaces carry boundaries of their own, and
they are in scope too: the write-path admission screen (applied at the
single write choke point, so no surface reaches storage unscreened), the
quarantine wing's exclusion from every read that returns content, the
operator-only capabilities that must never appear over MCP, and
--read-only as a posture on the whole process rather than a filter on
one port. The whitepaper linked below states each mechanism and its
residual.
Tamper is detected on read, not prevented. Any record, KG triple, tunnel,
or manifest that fails its HMAC surfaces immediately: undercroft verify names
the record, and (on a --features telemetry build) the
undercroft_hmac_verify_failures_total metric, the live event stream, and the
Palace Monitor beacon all fire on the same real signal — never synthetically.
deploy/observability/ ships a PalaceTamperDetected alert, and
deploy/observability/RUNBOOK.md (published at /docs/runbook.html) covers
how to confirm, mitigate, fix, and prevent it.
Details are documented in crates/undercroft-vault/src/lib.rs and the
security model;
the formal adversary-class treatment — what each layer defeats and the
honest non-goals — is the
threat-model whitepaper
(docs/THREAT_MODEL.md).
In scope (examples, not a limit):
- Reading sealed content, embeddings, or derived artifacts (token matrices, PQ/FDE rows, codebooks) at rest without the vault's keys — AEAD/AAD bypass, nonce misuse, key-derivation flaws.
- Forging records, audit-chain entries, or manifests that
verifyaccepts; making a rollback pass as a crash. - Cross-vault access: any way blob or key material from one vault helps open another.
- HTTP-surface auth bypass: reaching
/v1without the bearer, addressing a vault without a valid assertion, escaping the orchestrator's tenant→vault mapping or/t/*allowlist, forging or replaying tenant tokens. - Plaintext or plaintext-derived data persisted to disk by a sealed vault (including via derived indexes, logs, or telemetry).
- Key material exposure through logs, errors, or telemetry.
- Opening an encrypted export bundle without its identity key, or
making a hybrid (
pq1) recipient silently accept a legacy X25519-only bundle — the downgrade refusal is a security property. - Reaching stored content without passing the admission screen when
a deployment declares
UNDERCROFT_ADMISSION=quarantine: any write path that lands a drawer unscreened, or a save that reports success under the id the caller aimed at while the content sits in quarantine. - Reading, editing, or destroying quarantine-pending content through
any surface but the operator's own (
admissionon the CLI and/v1) — MCP in particular must refuse it — or reaching an operator-only capability (admission rulings, wing-trust assignment, retention, forgetting, rotation) from the agent surface. - A
--read-onlyserver changing state: any request that writes, and any read-path write beyond the accepted open-time ones below.
Out of scope (documented threat-model boundaries):
- Attacks requiring the master key, the passphrase, or process memory while a vault is unlocked; a compromised host OS.
- A consistent old database + manifest pair restored together by an attacker with full disk control (documented residual; external witness is the planned mitigation).
- Writes a
--read-onlyprocess performs at open — schema creation, rotation reconciliation, chain initialization — andPOST …/verify, which only fast-forwards the manifest anchor and is deliberately classified as a read. Two things are known gaps rather than boundaries, so a report adds nothing but we would rather say so than have you find them: underUNDERCROFT_RETRIEVAL=pqa read-only search can still build or retrain a missing PQ/IVF index, and audited read records anchor only at the next store open, so a stripped unanchored tail is crash-indistinguishable until then. - Denial of service against a server you operate, and resource exhaustion requiring authenticated access.
- Vulnerabilities exclusively in optional attached components (remote vector backends, local LLM runtimes, user-supplied ONNX models) — though sealed-content leakage to those components is very much in scope.