Your data is encrypted with a key we don't have
We built the vault. We maintain the vault. But only you have the combination. This is not a policy — it is a technical guarantee.
Your data is encrypted with a key we don't have. We store ciphertext. We return ciphertext. We cannot read it. Not our engineers, not a subpoena, not a breach. Your key lives only in RAM on your machine — never on ours.
How it works
Every piece of data you create — messages, documents, contacts, health metrics, financial data — is encrypted on your VPS before being stored. The encrypted data travels to Cloudflare's infrastructure (D1, R2, Vectorize) for storage and search. The key that makes it readable is held in RAM on your VPS, provided by a Swiss-jurisdiction KMS via mTLS.
Swiss KMS wraps the key. Your VPS unwraps and encrypts. Cloudflare stores ciphertext. Only your VPS decrypts.
The honest caveat: when you use the web portal, your browser connects to your customer subdomain (e.g. handle.mycelium.id) which is proxied through Cloudflare. Cloudflare terminates TLS at their edge, sees the live HTTP traffic, then re-encrypts to your VPS. This is the same architecture every Cloudflare-fronted SaaS uses. Cloudflare is a contractual subprocessor under their standard data processing terms — they cannot access the data you've already stored (it's encrypted), but they can technically see live portal traffic in transit. We disclose this rather than hide it. See the trust model below for the full picture.
Swiss KMS Your Machine Our Infrastructure KEK-wrapped key | | | mTLS 1.3 ──────────> Unwrap KEK Store in tmpfs (RAM) | You write a message | Encrypt with your key (AES-256-GCM) | Ciphertext ─────────> Store in Cloudflare D1 (cannot decrypt) You read a message | Ciphertext <───────── Return ciphertext | Decrypt with your key | Plaintext (only on your machine)
The technical details
Each record is encrypted with its own unique data encryption key (DEK). That DEK is wrapped by a scope key, which is derived from your master key using HKDF-SHA256. Even if one record's DEK were somehow exposed, no other record is affected.
| Component | Specification |
|---|---|
| Algorithm | AES-256-GCM (authenticated encryption) |
| Key derivation | HKDF-SHA256 with scope-specific info strings |
| Key wrapping | AES-KW (RFC 3394) — DEK wrapped by scope key |
| IV | 96-bit random per record (never reused) |
| Auth tag | 128-bit GCM tag (tamper detection) |
| Scopes | personal, org, wealth, moms — each derived independently |
| Master key | 256-bit (64 hex chars), KEK-wrapped in Swiss KMS, unwrapped on VPS only |
| Session tokens | SHA-256 hashed before storage — D1 breach cannot expose active sessions |
| API secrets | Bot tokens and API keys encrypted (AES-256-GCM) in D1 — key names and values both encrypted |
What's encrypted
All content that could identify you or reveal your thinking. Metadata fields like tags and entities are also encrypted. Timestamps and volume metrics remain unencrypted for functionality.
| Data | Encrypted fields |
|---|---|
| Messages | content, thinking, tags, entities, entity_summary |
| Documents | content, summary, title, tags, entities, metadata |
| Contacts | name, email, phone, company, position, LinkedIn URL |
| Health | sleep, HRV, heart rate, steps, workouts, mindfulness |
| Wealth | transaction notes, costs, values, P&L |
| Agent tasks | context, results |
| Attachments | transcripts, file_name, description, metadata |
| Secrets | API keys, bot tokens — both key names and values encrypted |
How the master key is protected
The master key is the single most sensitive piece of data in the system. Even with the encryption architecture above, if an attacker can read the key from memory or disk, the entire vault unlocks. We've hardened key storage at eight layers — starting with a Swiss-jurisdiction KMS that wraps the key before it ever reaches your VPS.
| Defense | What it blocks |
|---|---|
| Swiss KMS via mTLS 1.3 | Key is KEK-wrapped in Swiss jurisdiction. VPS receives wrapped key, unwraps locally. KMS validates client certificate before release. |
| Key on tmpfs (RAM-only filesystem) | Disk theft, rescue mode, file backups, .env leaks |
| sodium-native SecureBuffer (mlock'd, MADV_DONTDUMP) | Heap snapshots, memory dumps, V8 string interning |
| Encrypted swap (random key per boot) | Swap pages from previous boots become unreadable |
| Core dumps disabled | Crash dumps cannot capture key from memory |
| ptrace restricted (yama=1) | gdb -p <pid> from another shell blocked |
Node --inspect blocked at startup | DevTools heap snapshots cannot be enabled |
| PM2 dump.pm2 filtered + scrubbed | Process env serialization cannot leak secrets |
The master key is fetched from a Swiss-jurisdiction KMS over mTLS 1.3. The KMS stores the key as a KEK-wrapped blob. On startup, the VPS presents its client certificate, the KMS validates it, and the wrapped key is sent over the mutual TLS channel. The VPS unwraps the KEK locally and stores the result on tmpfs (RAM-only). The key never enters a bash variable, never appears in shell history, never touches a regular file. On reboot the tmpfs is empty and the KMS re-provides the key automatically — no manual re-keying needed. Disk theft and rescue mode can never recover the key.
Residual risk: A root attacker on a running system could still read process memory directly. However, the master key in memory was provided by the Swiss KMS via mTLS — the KMS validates the client certificate before unwrapping. An attacker would need both root access to the running VPS and the ability to impersonate the client certificate to re-obtain the key from the KMS. The split-jurisdiction architecture means that even full compromise of the data VPS does not give an attacker persistent access to the key material.
How the managed service works
When you sign up for Mycelium Managed, we provision and maintain your infrastructure: a VPS running your agents, Cloudflare Workers for storage, and the web portal. We handle updates, monitoring, and uptime.
Your master key is generated during setup. It is KEK-wrapped and stored in a Swiss-jurisdiction KMS, accessible only via mTLS with your VPS's client certificate. The key is unwrapped on your VPS and held in RAM only. It never passes through our systems, our logs, or our databases. We maintain the vault. You hold the only key.
If your VPS needs maintenance or migration, we provision a new one with a new client certificate. The Swiss KMS re-provisions the wrapped key via mTLS. Your data — still encrypted in Cloudflare — becomes readable again automatically. No manual re-keying. We never touch the key.
Tenant isolation
Each customer is fully isolated at every layer. There is no shared state between tenants except operator-level provisioning metadata.
| Layer | Isolation |
|---|---|
| D1 databases | Per-tenant database — no shared tables, no cross-tenant queries |
| Vectorize namespaces | Per-tenant namespace — 4-layer defense: CF namespace enforcement, post-fetch filtering + embedding value stripping, metadata filter redundancy, D1 cross-validation |
| R2 storage | Per-tenant path prefix — no shared buckets |
| Request routing | X-Tenant-ID header validated against authenticated token identity — cannot request another tenant's data |
| Encryption scopes | Per-tenant master key — even if isolation fails, data is encrypted with a different key |
Search embeddings (BGE-M3 1024D vectors) are stored in Cloudflare Vectorize with per-tenant namespaces. Raw embedding float arrays are never exposed through API responses — mitigating embedding inversion attacks where an attacker attempts to reconstruct plaintext from semantic vectors. Post-fetch filtering strips embedding values before returning results, and metadata filters provide redundant namespace enforcement.
Your master key & recovery code
During setup, a 256-bit master key is generated locally on your device — 64 hexadecimal characters. A short recovery code is derived from it for easier backup. Store both somewhere safe — a password manager, a fireproof box, or split across trusted locations.
a7f3e901 4bc28d56 e0f1a3c7 9d82b4e6 53c71f08 2da9e4b7 f6018c35 92d7a4e0
MYCEL-A7F3E-4BC28-D56E0-F1A3C
Your master key is the 64-character hex string used to encrypt all your data. The recovery code is a shorter derivative for easier backup. Either one can restore access to your data on any new machine. Without them, your data is permanently gone. There is no "forgot password" flow. There is no admin override. There is no backdoor.
What happens when things go wrong
Live portal traffic is a different story. While you're actively using the web portal, your browser connects to
handle.mycelium.id via Cloudflare's proxy. Cloudflare terminates TLS at their edge and sees the unencrypted HTTP traffic before re-encrypting to your VPS. If Cloudflare were compromised at the edge layer during your session, an attacker could observe the messages and search queries you sent during that session. They could not access your historical data (it's stored as ciphertext), and they could not extract your master key (it never crosses Cloudflare). This is the same trust boundary as every Cloudflare-fronted SaaS.What we can and cannot see
| Data | Mycelium (your VPS) | Cloudflare (storage) | Cloudflare (live portal traffic) |
|---|---|---|---|
| Message content (stored) | Plaintext | Ciphertext | — |
| Contact names (stored) | Plaintext | Ciphertext | — |
| Document text (stored) | Plaintext | Ciphertext | — |
| Health metrics (stored) | Plaintext | Ciphertext | — |
| Financial records (stored) | Plaintext | Ciphertext | — |
| Master encryption key | In memory only (from Swiss KMS) | Never transmitted | Never transmitted |
| Live chat & portal session | Plaintext (it's yours) | — | Visible in transit (TLS terminated at edge) |
| Search queries (live) | Plaintext | — | Visible in transit |
| Passkey assertions | Verified locally | — | Visible in transit |
| Session tokens | Plaintext | Hashed (SHA-256) | — |
| API secrets / bot tokens | Plaintext | Ciphertext (key names and values) | — |
| Timestamps | Visible | Visible | Visible |
| Message counts & volume | Visible | Visible | Visible |
| Search embeddings | Visible | Namespace-isolated per tenant (vectors, not text; raw values never exposed via API) | — |
| Tags & entities | Plaintext (derived on your VPS) | Ciphertext | — |
Stored data is protected. Every piece of vault data is encrypted on your VPS before it ever leaves the box. Cloudflare's storage layers (D1, R2, Vectorize) only ever see ciphertext. The master key is KEK-wrapped in a Swiss KMS and never crosses the wire to Cloudflare — not as a Worker secret, not as an environment variable, not in any request body. Session tokens are stored as SHA-256 hashes, so even a D1 breach cannot expose active sessions. API secrets and bot tokens are stored encrypted with both key names and values as ciphertext. Tags and entities are encrypted fields. If Cloudflare's storage is breached or subpoenaed, the result is encrypted blobs and hashed tokens that neither we nor they can decrypt.
Live portal traffic is the honest caveat. When you load the web portal at handle.mycelium.id, your browser hits Cloudflare first. Cloudflare terminates TLS at their edge using their cert, inspects the HTTP request (this is how their proxy and WAF work), then re-encrypts to your VPS. During an active session, Cloudflare can technically see the messages you type and the queries you run. They are a contractual subprocessor under their standard data processing terms. This is the same trust boundary that every Cloudflare-fronted SaaS — including most banks and most "private" tools — relies on. We disclose it rather than hide it behind diagrams that show end-to-end encryption.
Why we accept this tradeoff. The alternative is to point DNS directly at your VPS's public IP, eliminating Cloudflare from the traffic path entirely. That removes the TLS subprocessor, but it exposes your server's raw IP to the public internet — meaning direct DDoS surface, port scanning, and zero-day exposure with no edge protection. For a single-tenant personal vault on a small VPS, that risk is worse than the privacy gain. We've chosen to keep Cloudflare's edge protection and be honest about what that costs in transit visibility.
What this means in practice. Your historical data, your archive, your years of conversations and documents — all of that is encrypted with a key only your VPS holds. A breach of Cloudflare, a subpoena to Cloudflare, or a rogue Cloudflare employee with full database access cannot read any of it. The remaining surface is the live HTTP traffic during active portal sessions: the same surface every other web app has. We're working on options to shrink even that surface (client-side encryption for chat, end-to-end TLS via direct VPS connections for advanced users) and will document them as they ship.
What's protected, and what isn't
Your vault data (messages, documents, contacts, health, financial records) is encrypted with AES-256-GCM — symmetric cryptography. The master key is generated locally, never transmitted, and never stored on our infrastructure. Key derivation uses HKDF-SHA256. No asymmetric cryptography, no key exchange, no public/private key pairs. Quantum computers running Shor's algorithm have nothing to attack here. The best quantum attack (Grover's algorithm) halves the effective key length to AES-128 equivalent — still 2128 operations, far beyond any foreseeable capability.
The infrastructure uses the same transport security as every other internet service: TLS for HTTPS connections, SSH for server access, elliptic curve signatures for passkey authentication. These rely on asymmetric cryptography that quantum computers will eventually break. This is an industry-wide problem, not specific to Mycelium.
The practical implication: if someone records your encrypted network traffic today and decrypts it with a future quantum computer, they see ciphertext moving between your browser and your server — but that ciphertext is AES-256-GCM encrypted with your master key, which was never in the traffic. The vault contents remain protected even if the transport layer is compromised.
We don't claim the entire system is quantum-proof. The transport layer has the same limitations as everyone else's. What we can say is that your data at rest is encrypted with a stack that has no known quantum vulnerability.
Ready to own your data?
Start with Mycelium Managed or self-host on your own infrastructure.
Get Started