The Core Guarantee
When you send data to Olane for analysis:- You encrypt it on your machine using a key only you possess
- We decrypt it in server memory for the duration of analysis
- We archive it in its original encrypted form to S3
- We discard the key — the DEK exists in server memory only for the lifetime of the request
TL;DR (Technical)
- Raw text at rest: Archived in S3 in its encrypted form. Cannot be read without the DEK (which requires the master key).
- Raw text in transit: Encrypted before it leaves your machine. TLS provides an additional transport layer.
- The DEK in transit: Wrapped with your access token via a session token. Cannot be unwrapped without a valid authentication session.
Key Management
Your Master Key
Everything starts with your master key — a secret string stored in theCOPASS_ENCRYPTION_KEY environment variable or in your local .olane/config.json. You can generate one with:
.olane/config.json. This key never leaves your machine. If you lose it, archived encrypted data cannot be recovered — there is no key escrow.
The Data Encryption Key (DEK)
The master key is not used directly for encryption. Instead, a Data Encryption Key is derived from it using HKDF-SHA256:Session Tokens: Protecting the DEK in Transit
The DEK needs to reach the server so it can decrypt your data. Rather than sending the raw DEK over the wire, we wrap it:- A wrap key is derived from your Supabase access token using HKDF-SHA256
- The DEK is encrypted with AES-256-GCM using this wrap key
- The result — the session token — is an opaque 60-byte blob (IV + encrypted DEK + auth tag), base64-encoded
X-Encryption-Token header and the access token in the Authorization header. It derives the same wrap key from the access token and unwraps the session token to recover the DEK. Without a valid access token, the session token is meaningless.
Encryption in Detail
Algorithm: AES-256-GCM
All encryption uses AES-256-GCM, an authenticated encryption scheme that provides both confidentiality (your data is unreadable without the key) and integrity (any tampering is detected). Each encryption operation produces three values:| Field | Size | Purpose |
|---|---|---|
encrypted_text | Variable | The ciphertext (your encrypted data) |
encryption_iv | 12 bytes | A random initialization vector (nonce) |
encryption_tag | 16 bytes | An authentication tag for tamper detection |
os.urandom(12). This ensures that encrypting the same plaintext twice produces different ciphertext, preventing pattern analysis.
Encrypting Data
Theolane CLI handles encryption transparently when you ingest data. You don’t need to call encryption commands separately:
encrypted_text, encryption_iv, and encryption_tag fields automatically.
The Ingestion Flow
Here is what happens when you send encrypted data to the/ingest endpoint:
Step 1: Client-Side Encryption
Theolane CLI encrypts the plaintext and prepares the request automatically when you run an ingest command:
Step 2: HTTP Request
The encrypted payload is sent as a standard API request:text field is absent — the server knows this is an encrypted request because encrypted_text is present instead.
Step 3: DEK Resolution
The server’sget_dek() dependency resolves the DEK from the headers using a priority chain:
X-Encryption-Token(preferred): Unwraps the session token using the access token from theAuthorizationheaderX-Encryption-Key(legacy): Parses a base64-encoded DEK directly — supported for backward compatibility with bash-based tooling- Neither header: Treats the request as plaintext (backward-compatible for unencrypted clients)
Step 4: Decryption
The server decrypts the payload in memory:InvalidTag error and the server returns HTTP 400. There is no silent corruption.
Step 5: Archival
The original encrypted payload (not the plaintext) is archived to S3/R2:encrypted: true along with the IV and tag for retrieval. The plaintext is never written to disk, S3, or any persistent store.
Step 6: Cleanup
The DEK is discarded when the request completes. It exists only as a local variable in the request handler — no reference is retained.Key Rotation
To rotate your master key:- Generate a new key with
olane setup(into a new config) or set one directly witholane config set copass_encryption_key <new-key> - All new ingestions will use the new DEK derived from the new master key
- Previously archived data remains encrypted with the old DEK — to read it, you need the old master key
Cryptographic Primitives Summary
| Component | Algorithm | Purpose |
|---|---|---|
| DEK derivation | HKDF-SHA256 | Derive 32-byte DEK from master key |
| Data encryption | AES-256-GCM | Encrypt/decrypt user text |
| Wrap key derivation | HKDF-SHA256 | Derive wrap key from access token |
| DEK wrapping | AES-256-GCM | Encrypt DEK for transport as session token |
| IV generation | os.urandom(12) | Cryptographically random 96-bit nonce per operation |
cryptography library’s hazmat primitives.
Integration Quick Reference
CLI (@olane/o-cli)
API Headers
| Header | Value | When |
|---|---|---|
X-Encryption-Token | Base64 session token | Preferred — DEK wrapped with access token |
X-Encryption-Key | Base64 DEK | Legacy — raw DEK for bash-based tooling |
Authorization | Bearer <access_token> | Always required for authenticated requests |
Frequently Asked Questions
What happens if I lose my master key? Archived encrypted data becomes permanently unrecoverable. The structured output remains accessible since it is stored separately. Generate and back up your master key securely. Why is the DEK deterministic rather than random per message? A deterministic DEK means you never need to store or manage it — just re-derive it from your master key. Since each encryption operation uses a unique random IV, the ciphertext is still unique per message despite using the same key. Why wrap the DEK with the access token instead of sending it directly? The session token is opaque without a valid authentication session. If the token is intercepted (e.g., in a log), it cannot be unwrapped without the access token. TheX-Encryption-Key header (raw DEK) is supported for backward compatibility but the session token approach is preferred.
Can I use my own key management system (KMS, Vault)?
The system expects a string in COPASS_ENCRYPTION_KEY. You can source that string from any secret manager — the crypto library doesn’t care where it comes from, only that it’s consistent.
Is the structured output encrypted?
No. The structured output is stored unencrypted. These are structured abstractions — they capture what was observed, not the original text. The raw text that produced them is what’s protected.
What if the server is compromised during analysis?
During the request lifecycle, the plaintext and DEK exist in server memory. This is the trust boundary. The encryption protects data at rest (S3 archives) and provides defense-in-depth for data in transit. It does not protect against a fully compromised server at the moment of processing.