Encryption
Lumber encrypts your logs and notes on your own server and devices, and decrypts them only on the devices of people with access to the app. Lumber's servers store the ciphertext and never hold a key that opens it.
What the server sees
Encrypted, so Lumber can't read it:
- Each log's message,
requestId,traceId, context and metadata - The text of each note
- Each app's encryption key, wrapped to the account key of each person with access
Readable to Lumber:
- Each log's level and time
- Which app a log or note belongs to, who wrote each note, and which logs you've flagged
- Your account (email, name, plan) and each app's name, color and logo URL
- A SHA-256 hash of each API key's token, never the token itself
The level stays readable so Lumber can filter logs by level and decide when to send push alerts.
AES-256-GCM: logs and notes
AES-256-GCM is authenticated encryption: it keeps content secret and also detects any change to it, so a log altered in storage or in transit fails to decrypt instead of showing different text. It's one of the ciphers TLS 1.3 uses for HTTPS.
Each app has one 32-byte encryption key, the part of each API key after the dot. Every log and note is encrypted with it under a fresh random 12-byte nonce and sent as { nonce, payload, keyVersion }, where payload is the 16-byte authentication tag followed by the ciphertext, both base64-encoded. The HTTP API has the exact format, with an example.
Standard: NIST SP 800-38D.
X25519: sharing keys between people
X25519 is an elliptic-curve key agreement: two parties, each with a key pair, arrive at the same shared secret without ever sending it. It's the key exchange behind Signal, TLS 1.3, SSH and WireGuard.
Every Lumber account has an X25519 key pair, its account key. The private half stays in each of your devices' secure storage (the Keychain on iPhone and iPad, a file the Keychain encrypts in the Mac app, and the login Keychain for the command line). The public half is on Lumber's servers, so your teammates' devices can encrypt to you.
To give someone an app's key, a device that has it wraps the key to their account key:
- It makes a one-time X25519 key pair and agrees a shared secret with the recipient's public key
- HKDF-SHA256 turns that secret into a 32-byte AES key
- AES-256-GCM encrypts the app's key with it, under a random 12-byte nonce
The wrapped copy, with the one-time public key in front of it, is what Lumber's servers store, and they only ever give each person their own copy. Only the recipient's private key can open it. This construction is known as ECIES. API Keys covers when it happens.
Standards: RFC 7748 (X25519) and RFC 5869 (HKDF).
Linking a device
A new device gets your account key from one you're already using. It makes a one-time X25519 key pair and asks to link. Your other device shows a 7-digit code for the request (with a QR code a new phone's camera can scan), which you type into the new device. The code goes through a password-authenticated key exchange (CPace over ristretto255): the new device's answer proves it had the code and covers its one-time public key, and only once that checks out does your other device encrypt your account key to that key, the same way app keys are wrapped. It sends the key with a tag made from the code too, and the new device stores it only if the tag checks out.
Lumber's servers carry the exchange but never see the code, and can't test guesses against what they carry. They get one guess per request (one in a million), and a wrong code ends the request, so they can't swap in a key of their own and receive your account key, nor hand your new device a key of their own. The command line links the same way, from the code you type at its prompt.
SHA-256: API key tokens
The server keeps only a SHA-256 hash of each API key's token, so a copy of Lumber's database can't be used to send logs to your apps. The token is shown once, when you create the key.
Standard: FIPS 180-4.
Limits
- Lumber can't recover a lost key. If nobody with access to an app still has its key on a device, and no plugin config still has one of its API keys, the app's logs can't be decrypted.
- Rotating an app's encryption key isn't supported yet: logs encrypted with one key can't be read with another.
- What's readable to Lumber, above, stays readable: levels, times, and who wrote and flagged what.
Libraries
The SDK and the command line use Node's built-in crypto. The Lumber app uses @noble/ciphers and @noble/curves, small pure-TypeScript libraries, and decrypts logs with the platform's own AES (OpenSSL on iPhone and iPad, Web Crypto on the Mac).
Read the code
You don't have to take this page's word for it. The code that encrypts your logs and decrypts them on your Mac is open source: the Node.js SDK, the lumber command, and Lumber-Kit, the shared types and wire format both use.