Encryption & Security
TensorPM uses zero-knowledge end-to-end encryption (E2EE) to protect your project data in Cloud Sync. Your workspace content is encrypted on your device before it leaves your computer, and can only be decrypted by your registered devices. TensorPM servers never see your data in plaintext.
Overview
When you create or join a cloud workspace, TensorPM automatically sets up encryption:
- Your device generates a unique cryptographic identity (device keypair)
- A symmetric workspace key is created to encrypt workspace content
- The workspace key is securely distributed to each authorized device via sealed envelopes
- All sensitive project data is encrypted before sync upload and decrypted after sync download
This is true zero-knowledge architecture: our servers store only ciphertext. We cannot read your project content, even if compelled.
Cryptographic algorithms
TensorPM uses modern, audited cryptographic primitives from libsodium (NaCl):
Data encryption: XChaCha20-Poly1305 (AEAD)
All workspace content is encrypted with XChaCha20-Poly1305, an Authenticated Encryption with Associated Data (AEAD) cipher:
- 256-bit symmetric keys generated via libsodium's keygen
- 192-bit (24-byte) random nonce per encryption operation, ensuring nonce uniqueness
- Poly1305 authentication tag guarantees data integrity and authenticity
- Associated Authenticated Data (AAD) binds ciphertext to its context (workspace, table, record), preventing cross-context attacks
XChaCha20-Poly1305 is widely regarded as a modern, secure alternative to AES-GCM. The extended 24-byte nonce makes accidental nonce reuse virtually impossible.
Key exchange: X25519 (Elliptic Curve Diffie-Hellman)
Device-to-device key distribution uses X25519 elliptic curve key exchange:
- Each device generates a unique X25519 keypair (32-byte public/private keys)
- Workspace keys are wrapped in sealed boxes (
crypto_box_seal) for each recipient device
- Only the recipient's private key can unseal the envelope
- Device isolation: compromising one device does not expose other devices' private keys. However, any workspace keys already received by that device are at risk, which could allow decryption of data encrypted with those key versions
Password-based key derivation: Argon2id
For optional password-based recovery (escrow), TensorPM uses Argon2id:
- Memory-hard KDF resistant to GPU and ASIC attacks
- Parameters: memory = 64 MB, iterations = 3, parallelism = 4
- OWASP-recommended for password hashing and key derivation
- The server stores only the encrypted escrow blob, never the password or derived key
What gets encrypted
TensorPM encrypts all sensitive workspace content across 37+ table types:
Encrypted (examples):
- Project descriptions, notes, and custom fields
- Action item content, acceptance criteria, and details
- Milestone names, objectives, and descriptions
- Stakeholder labels and descriptions
- Budget and effort tracking data
- Timer history records
- Risk descriptions and mitigation plans
- Requirements and specifications
Not encrypted (metadata required for sync and access control):
- Record IDs and foreign keys (needed for relational integrity)
- Timestamps (created_at, updated_at)
- Workspace membership and role information
- Sync coordination metadata
Never synced (local-only):
- API keys and credentials (stored in OS secure storage)
- Local app settings and preferences
- Email drafts and temporary data
Key management
Device keys
When you first access a cloud workspace, TensorPM generates a unique X25519 keypair for your device:
- Stored locally in an encrypted file, protected by your OS keychain (macOS Keychain, Windows DPAPI, or Linux libsecret)
- Each device has a unique UUID identifier registered with TensorPM servers
- The private key never leaves your device
- If OS secure storage is unavailable, TensorPM warns you and shows a "Keyring" status
Workspace keys
Each cloud workspace has its own symmetric encryption key:
- Generated by the workspace creator using libsodium's
crypto_aead_xchacha20poly1305_ietf_keygen()
- Multi-version support: when a key is rotated, all historical versions are kept for decrypting older records
- Stored locally in the device's encrypted key store
Envelopes (key distribution)
Workspace keys are distributed to authorized devices via sealed envelopes:
- The workspace key is encrypted with each recipient device's X25519 public key using
crypto_box_seal()
- Envelopes are synced through the server (the server sees only ciphertext)
- When a new device joins, existing devices create envelopes for it
- When a device is revoked, it stops receiving new envelopes
Encryption data flow
Upload (encrypting before sync)
Your local database (plaintext)
|
v
Sensitive fields are extracted based on schema definition
|
v
Random 24-byte nonce is generated
|
v
AAD is constructed: workspace_id | table_name | primary_key
|
v
XChaCha20-Poly1305 encrypts the sensitive fields
|
v
Encrypted blob: { version, workspace_key_version, nonce, ciphertext }
|
v
Only the encrypted blob is written to the sync sidecar
|
v
Server stores encrypted blob (cannot decrypt)
Download (decrypting after sync)
Server sends encrypted record
|
v
Encrypted blob is parsed (version, key version, nonce, ciphertext)
|
v
Correct workspace key version is loaded from local key store
|
v
AAD is reconstructed: workspace_id | table_name | primary_key
|
v
XChaCha20-Poly1305 decrypts and verifies authenticity
|
v
Decrypted fields are merged into local database (plaintext)
Security properties
Authenticated encryption (AEAD)
Every encrypted record includes Associated Authenticated Data (AAD) that binds the ciphertext to its context:
workspace_id: prevents moving encrypted data between workspaces
table_name: prevents swapping data between different table types
primary_key: prevents swapping records within the same table
If any of these values are tampered with, decryption fails. This prevents a wide class of ciphertext manipulation attacks.
Nonce management
- A fresh random 24-byte nonce is generated for every encryption operation
- With XChaCha20's 192-bit nonce space, the probability of accidental collision is negligible (even after billions of operations)
- The nonce is stored alongside the ciphertext and is not secret
Replay protection
- Each encrypted record includes a timestamp (
_e2e_encrypted_at)
- During decryption, TensorPM checks if the encryption timestamp is plausible relative to the record's update timestamp
- Stale ciphertexts (replayed old data) are detected and flagged
Device isolation and revocation
- Each device has its own keypair independent of other devices
- Revoking a device prevents it from receiving future workspace keys
- Important limitation: if a device is compromised, the attacker may access workspace keys that device had already received. This means historical data encrypted with those key versions could be decryptable. After revoking a compromised device, rotating the workspace key ensures that new data is protected with a key the revoked device never received
Recovery
Device-based recovery
If you have at least one active device, you can register a new device and it will automatically receive workspace key envelopes from your existing devices.
Password-based escrow recovery
TensorPM offers optional password-based escrow as a safety net:
- Your workspace keys are encrypted with a key derived from your password using Argon2id
- The encrypted escrow blob is stored on TensorPM servers
- TensorPM cannot decrypt the escrow (zero-knowledge: no password, no access)
- To recover: enter your password, Argon2id derives the key, workspace keys are restored
Important: if all devices and recovery options are lost
If you lose all your devices and have no escrow recovery set up, your encrypted cloud data cannot be recovered. TensorPM cannot decrypt your data. This is the fundamental trade-off of true zero-knowledge encryption.
Recommendation: Always keep at least two devices registered, or set up password-based escrow recovery in your account security settings.
E2E status indicators
In the app, cloud workspaces show their encryption status:
- E2E Setup... - Encryption is being initialized for this workspace
- E2E Pending - Waiting for key distribution to complete
- E2E Encrypted - Workspace is fully encrypted and operational
- Keyring - OS secure storage is unavailable (encryption keys cannot be stored safely)
Summary
| Property | Detail |
|----------|--------|
| Architecture | Zero-knowledge end-to-end encryption |
| Data encryption | XChaCha20-Poly1305 (AEAD, 256-bit keys) |
| Key exchange | X25519 (Elliptic Curve Diffie-Hellman) |
| Key derivation (escrow) | Argon2id (64 MB, 3 iterations, 4 threads) |
| Crypto library | libsodium (NaCl) |
| Nonce size | 192-bit (24 bytes), random per operation |
| What is encrypted | All sensitive workspace content (37+ tables) |
| What stays in cleartext | Structural metadata for sync/access control |
| Key storage | OS keychain (macOS Keychain / Windows DPAPI / Linux libsecret) |
| Server access to plaintext | None (zero-knowledge) |
| Recovery options | Multi-device, password-based escrow (Argon2id) |
| Server location | Germany (Hetzner, Nuremberg) |
| Transport encryption | TLS 1.3 |