Summary
Our first external security report showed that a provider can read the prompts and completions it serves — e.g. by dropping a 13-line sitecustomize.py into the inference venv and capturing a requester's prompt (SSN, card number, etc.) verbatim. The report is correct, and the root cause is architectural, not a bug:
Decryption happens in the Rust agent (advisor.rs::open_from) and the plaintext is streamed to a Python + vllm-mlx child over a Unix socket. Everything downstream of that decryption runs on hardware the provider owns.
End-to-end crypto_box sealing protects the prompt in transit to the provider you chose; it does not protect it from that provider. This issue tracks the path from where we are (best-effort hardening) to actually raising the posture.
Full trust model: docs/security/confidential-compute.md. Reporting: SECURITY.md. Decision record: docs/adr/0002-confidential-compute-threat-model.md.
What "solved" requires
Two things have to become true, and neither is true today:
- The plaintext must live somewhere the machine owner can't read it. Today it sits in an interpreted Python process the owner provisioned and fully controls.
- The requester's encryption must be bound to an attestation of that environment — not to the provider's long-lived X25519 key (which the provider holds in the clear).
The posture ladder
Rung 0 — Cost-raising hardening ✅ (done)
Defense in depth that kills the cheap attacks but doesn't change the trust model. Shipped:
- Launch the inference child under
python -S -E -B — disables the site module, removing the entire sitecustomize/usercustomize/.pth startup-hook class the report used (the class that needs no package source change and so survives the wrapper-overwrite check). Wrapper re-adds only the venv site-packages via sys.path.insert.
- Self-harden the Python child:
PT_DENY_ATTACH (macOS) / PR_SET_DUMPABLE=0 (Linux) / RLIMIT_CORE=0.
- Plus existing:
mlock over plaintext, Zeroizing, never piping child output into agent logs, agent-side SIP requirement + PT_DENY_ATTACH + no-core.
This raises cost; it does not solve it. A provider who edits imported source or reads process memory is not stopped.
Rung 1 — Make "trust by selection" real and accountable (cheap, ship now)
Stop implying trustlessness; make the trusted-provider model good: verifiable provider identity (DIDs — have it), on-record ToS/confidentiality attestations (mint these already), reputation/history, requester-side allowlists / "friend networks," optional stake-and-slash so a provider caught leaking loses something. Adds consequences and informed choice, not secrecy. Honest and fully shippable.
Rung 2 — Attestation-bound ephemeral keys (buildable now, additive to the lexicon)
Requester seals to a per-session key the provider generates and attests, and the receipt commits to the attestation that was live. Receipts already strong-ref an attestation record — natural hook. Adds no secrecy on a Mac yet, but closes the "long-lived key sitting in the clear" gap and is the substrate Rungs 3/4 bind to. Lexicon-additive (new optional fields), so it respects the evolve-additively invariant.
Prerequisite for Rung 3.
Rung 3 — macOS-native sealed inference (the real ceiling without new hardware)
Strongest thing achievable on the hardware we target. Good news: ~60% of the attestation data model already exists — attestation.rs signs binaryHash, sipEnabled, secureBootEnabled, authenticatedRootEnabled, osVersion, an MDA cert chain to Apple's Enterprise Attestation Root (mda.rs), a SEP-backed selfSignature, and encryptionPubKey; cocore.entitlements already sets get-task-allow=false + library validation on + allow-jit=false. The rung is really "(1) make the measured binary be the one that sees plaintext, and (2) make the requester actually check, fail-closed."
Work streams:
- WS1 — Get plaintext out of the owner-controlled interpreter (long pole). Replace
SubprocessEngine (Python+vllm-mlx over UDS) with a native engine so the attested binaryHash actually covers the code that touches the prompt. The Engine trait already isolates this. (A) in-process native inference via mlx-c FFI or llama.cpp+Metal (de-risk: mature/embeddable, but moves to GGUF weights), or (B) a separate signed native helper over a mutually-authenticated UDS. (A) is cleaner. Cost: lose process-level crash isolation (native segfault aborts the agent).
- WS2 — Sign / harden / notarize the agent (not just the shell). Parallel entitlements for the agent binary: hardened runtime,
get-task-allow=false, library validation on (blocks swapping a bundled dylib), no dyld-env / disable-* escapes; Developer ID signed + notarized + stapled. Early spike needed: Metal shader JIT vs hardened runtime — precompile signed .metallib to avoid allow-jit (which would punch a hole in the exact protection we're buying). Payoff: on a SIP-enabled Mac, a hardened-runtime binary with no get-task-allow cannot have its task port acquired by the owner (even root) without an Apple debugger entitlement or disabling SIP (reboot → kills the process; agent refuses SIP-off). That blocks the task_for_pid/memory-read path.
- WS3 — Measure the right binary + requester verifies (highest leverage). Repoint attestation at the inference code path; add cdhash (
csops(CS_OPS_CDHASH)/SecCodeCopySelf), Developer-ID team id, hardened-runtime/library-validation/get-task-allow status (additive lexicon fields). Bind the Rung-2 ephemeral key into the attestation instead of the long-lived key. Add requester-side verification in packages/sdk / sdk/py / console (verifier-side equivalent of mda.rs): verify MDA chain → Apple root, sipEnabled && get-task-allow==false && libraryValidation==true, osVersion ≥ floor, cdhash ∈ known-good, freshness nonce — then seal. Default-on, fail-closed. Skipping this makes the rung theater.
- WS4 — Reproducible builds + transparency log.
cdhash ∈ known-good only means something if a requester can map cdhash → reviewable source. Deterministic builds + a published append-only measurement log (Apple PCC's "code transparency" property).
- WS5 — Productization. Receipt/attestation tier marker (
attested-confidential vs best-effort); keep the Python path as an explicit untrusted tier. OS-version floor enforcement; revisit MDA revocation (currently skipped per mda.rs).
Residual holes (still "mostly solved," not "solved"): self-attestation of software on macOS isn't first-class (no TDX/SEV-SNP/TPM-style measured-launch quote; cdhash+MDA+SEP leans on library-validation/no-debugger-entitlement/SIP, which a kernel compromise could defeat); Metal JIT tension; kernel exploit / vulnerable OS beats kernel-enforced task-port protection (mitigate by attesting osVersion); physical / DMA / cold-boot out of scope.
Rung 4 — A real TEE (true solution, hardware pivot)
Trustless confidentiality needs memory the owner cannot read by construction: NVIDIA Confidential Computing GPUs (H100/H200) + AMD SEV-SNP / Intel TDX, with attestation the requester checks before sealing — but that means providers run Linux + CC GPUs, not Macs/MLX (hardware/business pivot). Or wait for Apple to expose enclaved inference; Apple Private Cloud Compute is the existence proof and design template (attested stateless nodes, ephemeral per-node keys, code transparency, no persistence) but relies on custom silicon a federated network can't replicate. darkbloom.dev is chasing the Apple-silicon-confidential angle — worth tracking for primitives.
FHE / secure-MPC (no trusted hardware) remain many orders of magnitude too slow for useful-size LLMs — watch-the-research, not buildable.
Recommended sequencing
- Rung 1 now (honest + immediately valuable).
- Rung 2 next (additive, lexicon-compatible, the substrate everything above binds to).
- WS2 JIT spike (cheap; de-risks whether Rung 3 is even feasible under hardened runtime).
- WS1 (long pole; start with llama.cpp+Metal to de-risk).
- WS3 (leverage existing attestation; new work is fail-closed requester verification), WS4 in parallel.
The contract until a rung lands
The network gives you verifiable receipts and transport privacy, not privacy from the provider. Don't send secrets to a provider you don't operate or trust. Receipt integrity (signed, content-addressed, non-forgeable) is a separate guarantee and is not weakened by any of the above.
Contributions from people who know more about confidential compute than we do are very welcome — especially on WS1 (native engine), the WS2 JIT/notarization spike, and WS3 requester-side verification.
Summary
Our first external security report showed that a provider can read the prompts and completions it serves — e.g. by dropping a 13-line
sitecustomize.pyinto the inference venv and capturing a requester's prompt (SSN, card number, etc.) verbatim. The report is correct, and the root cause is architectural, not a bug:End-to-end
crypto_boxsealing protects the prompt in transit to the provider you chose; it does not protect it from that provider. This issue tracks the path from where we are (best-effort hardening) to actually raising the posture.Full trust model:
docs/security/confidential-compute.md. Reporting:SECURITY.md. Decision record:docs/adr/0002-confidential-compute-threat-model.md.What "solved" requires
Two things have to become true, and neither is true today:
The posture ladder
Rung 0 — Cost-raising hardening ✅ (done)
Defense in depth that kills the cheap attacks but doesn't change the trust model. Shipped:
python -S -E -B— disables thesitemodule, removing the entiresitecustomize/usercustomize/.pthstartup-hook class the report used (the class that needs no package source change and so survives the wrapper-overwrite check). Wrapper re-adds only the venv site-packages viasys.path.insert.PT_DENY_ATTACH(macOS) /PR_SET_DUMPABLE=0(Linux) /RLIMIT_CORE=0.mlockover plaintext,Zeroizing, never piping child output into agent logs, agent-side SIP requirement +PT_DENY_ATTACH+ no-core.This raises cost; it does not solve it. A provider who edits imported source or reads process memory is not stopped.
Rung 1 — Make "trust by selection" real and accountable (cheap, ship now)
Stop implying trustlessness; make the trusted-provider model good: verifiable provider identity (DIDs — have it), on-record ToS/confidentiality attestations (mint these already), reputation/history, requester-side allowlists / "friend networks," optional stake-and-slash so a provider caught leaking loses something. Adds consequences and informed choice, not secrecy. Honest and fully shippable.
Rung 2 — Attestation-bound ephemeral keys (buildable now, additive to the lexicon)
Requester seals to a per-session key the provider generates and attests, and the receipt commits to the attestation that was live. Receipts already strong-ref an attestation record — natural hook. Adds no secrecy on a Mac yet, but closes the "long-lived key sitting in the clear" gap and is the substrate Rungs 3/4 bind to. Lexicon-additive (new optional fields), so it respects the evolve-additively invariant.
Prerequisite for Rung 3.
Rung 3 — macOS-native sealed inference (the real ceiling without new hardware)
Strongest thing achievable on the hardware we target. Good news: ~60% of the attestation data model already exists —
attestation.rssignsbinaryHash,sipEnabled,secureBootEnabled,authenticatedRootEnabled,osVersion, an MDA cert chain to Apple's Enterprise Attestation Root (mda.rs), a SEP-backedselfSignature, andencryptionPubKey;cocore.entitlementsalready setsget-task-allow=false+ library validation on +allow-jit=false. The rung is really "(1) make the measured binary be the one that sees plaintext, and (2) make the requester actually check, fail-closed."Work streams:
SubprocessEngine(Python+vllm-mlx over UDS) with a native engine so the attestedbinaryHashactually covers the code that touches the prompt. TheEnginetrait already isolates this. (A) in-process native inference viamlx-cFFI or llama.cpp+Metal (de-risk: mature/embeddable, but moves to GGUF weights), or (B) a separate signed native helper over a mutually-authenticated UDS. (A) is cleaner. Cost: lose process-level crash isolation (native segfault aborts the agent).get-task-allow=false, library validation on (blocks swapping a bundled dylib), no dyld-env / disable-* escapes; Developer ID signed + notarized + stapled. Early spike needed: Metal shader JIT vs hardened runtime — precompile signed.metallibto avoidallow-jit(which would punch a hole in the exact protection we're buying). Payoff: on a SIP-enabled Mac, a hardened-runtime binary with noget-task-allowcannot have its task port acquired by the owner (even root) without an Apple debugger entitlement or disabling SIP (reboot → kills the process; agent refuses SIP-off). That blocks thetask_for_pid/memory-read path.csops(CS_OPS_CDHASH)/SecCodeCopySelf), Developer-ID team id, hardened-runtime/library-validation/get-task-allow status (additive lexicon fields). Bind the Rung-2 ephemeral key into the attestation instead of the long-lived key. Add requester-side verification inpackages/sdk/sdk/py/ console (verifier-side equivalent ofmda.rs): verify MDA chain → Apple root,sipEnabled && get-task-allow==false && libraryValidation==true,osVersion ≥ floor,cdhash ∈ known-good, freshness nonce — then seal. Default-on, fail-closed. Skipping this makes the rung theater.cdhash ∈ known-goodonly means something if a requester can map cdhash → reviewable source. Deterministic builds + a published append-only measurement log (Apple PCC's "code transparency" property).attested-confidentialvsbest-effort); keep the Python path as an explicit untrusted tier. OS-version floor enforcement; revisit MDA revocation (currently skipped permda.rs).Residual holes (still "mostly solved," not "solved"): self-attestation of software on macOS isn't first-class (no TDX/SEV-SNP/TPM-style measured-launch quote; cdhash+MDA+SEP leans on library-validation/no-debugger-entitlement/SIP, which a kernel compromise could defeat); Metal JIT tension; kernel exploit / vulnerable OS beats kernel-enforced task-port protection (mitigate by attesting
osVersion); physical / DMA / cold-boot out of scope.Rung 4 — A real TEE (true solution, hardware pivot)
Trustless confidentiality needs memory the owner cannot read by construction: NVIDIA Confidential Computing GPUs (H100/H200) + AMD SEV-SNP / Intel TDX, with attestation the requester checks before sealing — but that means providers run Linux + CC GPUs, not Macs/MLX (hardware/business pivot). Or wait for Apple to expose enclaved inference; Apple Private Cloud Compute is the existence proof and design template (attested stateless nodes, ephemeral per-node keys, code transparency, no persistence) but relies on custom silicon a federated network can't replicate. darkbloom.dev is chasing the Apple-silicon-confidential angle — worth tracking for primitives.
FHE / secure-MPC (no trusted hardware) remain many orders of magnitude too slow for useful-size LLMs — watch-the-research, not buildable.
Recommended sequencing
The contract until a rung lands
The network gives you verifiable receipts and transport privacy, not privacy from the provider. Don't send secrets to a provider you don't operate or trust. Receipt integrity (signed, content-addressed, non-forgeable) is a separate guarantee and is not weakened by any of the above.
Contributions from people who know more about confidential compute than we do are very welcome — especially on WS1 (native engine), the WS2 JIT/notarization spike, and WS3 requester-side verification.