Encryption & Secrets¶
Lango encrypts all sensitive data using AES-256-GCM and manages cryptographic keys through a registry backed by the Ent ORM. Two security modes are available depending on your deployment.
Security Modes¶
Local Mode (Default)¶
Local mode uses a Master Key (MK) envelope architecture. A random 32-byte Master Key encrypts all data (AES-256-GCM). The passphrase does not encrypt data directly — it derives a Key Encryption Key (KEK) that wraps/unwraps the MK.
Passphrase → PBKDF2 → KEK → wraps Master Key (AES-256-GCM)
├── Data Encryption: secrets, config profiles
└── DB Key: HKDF(MK, "lango-db-encryption")
During onboarding (lango onboard) or first startup, Lango prompts for a passphrase:
Enter encryption passphrase (min 8 characters): ********
Confirm passphrase: ********
For CI/CD or server deployments, provide a keyfile:
echo "your-secure-passphrase" > ~/.lango/keyfile
chmod 600 ~/.lango/keyfile
lango serve
Recovery Mnemonic
Lango supports a BIP39 recovery mnemonic as an alternative KEK slot. If you lose your passphrase, the mnemonic can unwrap the Master Key and restore access. Set it up with:
lango security recovery setup
Without a recovery mnemonic, losing your passphrase means permanent loss of all encrypted data.
Key Hierarchy:
| Layer | Parameter | Value |
|---|---|---|
| KEK derivation | Algorithm | PBKDF2-SHA256, 100,000 iterations |
| KEK derivation | Salt | 128 bits (16 bytes), per-slot |
| MK wrapping | Cipher | AES-256-GCM |
| Data encryption | Key | Master Key (256 bits, random) |
| Data encryption | Cipher | AES-256-GCM, 96-bit nonce |
| DB key | Derivation | HKDF-SHA256(MK, "lango-db-encryption") |
| Identity key | Derivation | HKDF-SHA256(MK, "lango-identity-ed25519") → Ed25519 |
Identity Bundle:
When the Master Key is available, Lango derives an Ed25519 identity key and creates an Identity Bundle (~/.lango/identity-bundle.json). The bundle contains the Ed25519 signing key, secp256k1 settlement key (from wallet), and dual proofs. A content-addressed DID v2 (did:lango:v2:<hash>) is computed from the bundle. The legacy v1 DID (did:lango:<secp256k1-hex>) is preserved for backward compatibility.
Envelope File:
The Master Key envelope is stored at ~/.lango/envelope.json (0600 permissions). It contains KEK slots (passphrase, mnemonic), KDF metadata, and crash recovery flags. The envelope is readable without opening the database.
Change Passphrase:
To change the passphrase without re-encrypting data:
lango security change-passphrase
This re-wraps the Master Key with a new passphrase-derived KEK. Because the MK itself does not change, no data re-encryption or DB rekey is needed — the operation is O(1).
Legacy Command
lango security migrate-passphrase is deprecated. Use change-passphrase instead.
See Envelope Migration for details on upgrading from legacy installations.
RPC Mode (Production)¶
RPC mode delegates all cryptographic operations (sign, encrypt, decrypt) to an external signer -- typically a hardware-backed companion app or HSM. Private keys never leave the secure hardware boundary.
sequenceDiagram
participant Agent
participant RPCProvider
participant Companion as Companion App / HSM
Agent->>RPCProvider: Encrypt(keyID, plaintext)
RPCProvider->>Companion: encrypt.request (JSON-RPC)
Companion-->>RPCProvider: encrypt.response (ciphertext)
RPCProvider-->>Agent: ciphertext
Each RPC request has a 30-second timeout. If the companion is unreachable, the Composite Provider automatically falls back to local mode (if configured).
Configure RPC mode:
Settings:
lango settings→ Security
{
"security": {
"signer": {
"provider": "rpc",
"rpcUrl": "https://companion.local:8443",
"keyId": "primary-signing-key"
}
}
}
Cloud KMS Mode¶
Cloud KMS mode delegates cryptographic operations to a managed key service. Four backends are supported:
| Backend | Provider | Build Tag | Key Types |
|---|---|---|---|
| AWS KMS | aws-kms |
kms_aws |
ECDSA_SHA_256 signing, SYMMETRIC_DEFAULT encrypt/decrypt |
| GCP Cloud KMS | gcp-kms |
kms_gcp |
AsymmetricSign SHA-256, symmetric encrypt/decrypt |
| Azure Key Vault | azure-kv |
kms_azure |
ES256 signing, RSA-OAEP encrypt/decrypt |
| PKCS#11 HSM | pkcs11 |
kms_pkcs11 |
CKM_ECDSA signing, CKM_AES_GCM encrypt/decrypt |
Build with the appropriate tag to include the Cloud SDK dependency:
# Single provider
go build -tags kms_aws ./cmd/lango
# All providers
go build -tags kms_all ./cmd/lango
Without a build tag, the provider returns a stub error at runtime. Even with the matching build tag, the runtime still depends on bootstrap-backed storage wiring so the key registry and secrets store can be initialized around the KMS-backed provider.
The CompositeCryptoProvider wraps any KMS backend with automatic local fallback when kms.fallbackToLocal is enabled. KMS calls include exponential backoff retry logic for transient errors (throttling, network timeouts) and a health checker with a 30-second probe cache.
During encrypted profile bootstrap, Lango must acquire credentials before it can load profile configuration. If KMS is selected through LANGO_KMS_PROVIDER, set LANGO_KMS_FALLBACK_TO_LOCAL=false to fail closed on KMS provider initialization or unwrap failures instead of falling back to the local passphrase prompt. Leaving the variable unset, setting it to true, or providing an invalid boolean value preserves the default fallback-enabled behavior.
Configure Cloud KMS:
Settings:
lango settings→ Security
{
"security": {
"signer": {
"provider": "aws-kms"
},
"kms": {
"region": "us-east-1",
"keyId": "arn:aws:kms:us-east-1:123456789012:key/example-key",
"fallbackToLocal": true,
"timeoutPerOperation": "5s",
"maxRetries": 3
}
}
}
For Azure Key Vault, also specify the vault URL:
{
"security": {
"signer": { "provider": "azure-kv" },
"kms": {
"keyId": "my-signing-key",
"azure": {
"vaultUrl": "https://myvault.vault.azure.net"
}
}
}
}
For PKCS#11 HSM:
{
"security": {
"signer": { "provider": "pkcs11" },
"kms": {
"pkcs11": {
"modulePath": "/usr/lib/softhsm/libsofthsm2.so",
"slotId": 0,
"keyLabel": "lango-signing-key"
}
}
}
}
PKCS#11 PIN
Set the PIN via LANGO_PKCS11_PIN environment variable instead of storing it in configuration.
Secret Management¶
Agents manage encrypted secrets through tool workflows. Secrets are stored in the Ent database with AES-256-GCM encryption and referenced by name -- plaintext values never appear in logs or agent output.
How It Works¶
- A secret is stored via CLI or tool call with a name and value
- The value is encrypted using the default encryption key from the Key Registry
- The agent receives a reference token (
{{secret:name}}) instead of the plaintext - When a tool needs the actual value, the RefStore resolves the token just before execution
- The output scanner replaces any leaked values with
[SECRET:name]placeholders
User: "Store my API key: sk-abc123"
Agent stores → {{secret:api_key}}
Tool uses → RefStore resolves {{secret:api_key}} → sk-abc123
Output → Scanner replaces sk-abc123 → [SECRET:api_key]
Output Scanning¶
The secret scanner monitors all agent output for plaintext secret values. When a match is found, it replaces the value with a safe placeholder:
Agent output: "Connected using key sk-abc123"
Scanned output: "Connected using key [SECRET:api_key]"
This prevents accidental secret leakage through chat messages, logs, or tool output.
Hardware Keyring Integration¶
Lango can store the master passphrase using hardware-backed security, eliminating the need for keyfiles or interactive prompts on every startup. Only hardware-backed backends are supported to prevent same-UID attacks.
Passphrase Source Priority:
- Hardware keyring (Touch ID / TPM when available and a passphrase is stored)
- Keyfile (
~/.lango/keyfileorLANGO_KEYFILEpath) - Interactive prompt (terminal input)
- Stdin (piped input for CI/CD)
If the piped stdin source reaches EOF without any passphrase bytes, Lango treats that as an empty passphrase input instead of surfacing a low-level read error.
Supported Hardware Backends:
| Platform | Backend | Security Level |
|---|---|---|
| macOS | Touch ID (Secure Enclave) | Biometric |
| Linux | TPM 2.0 sealed storage | Hardware |
Manage via CLI:
lango security keyring store # Store passphrase in hardware backend
lango security keyring status # Check hardware keyring availability
lango security keyring clear # Remove stored passphrase
No Hardware Backend
On systems without Touch ID or TPM 2.0, the keyring commands are unavailable. Use keyfile or interactive prompt instead.
Database Encryption¶
The current runtime no longer supports SQLCipher page-level database encryption. Instead, Lango uses broker-managed payload protection: sensitive content is encrypted with AES-256-GCM at the application layer while redacted plaintext projections remain available for FTS5 search and recall.
Current behavior:
- The SQLite database is opened with the default pure-Go runtime driver.
- Sensitive payloads are encrypted and decrypted through the storage broker using keys derived from the Master Key envelope.
- Searchable fields store redacted projections, not raw plaintext secrets.
- Session messages, learning payloads, inquiries, and agent memory use ciphertext for original values and keep only redacted projections in plaintext search columns.
- Projection length limits are applied without splitting UTF-8 characters, so multilingual content remains valid text after redaction and truncation.
- Production app and CLI paths consume these records through storage facade capabilities instead of generic raw Ent/SQL handles.
Legacy compatibility:
security.dbEncryption.*is still parsed from older configs, but ignored by the runtime.lango security db-migrateandlango security db-decryptremain only as remediation signposts and return an unsupported message.- If the runtime sees a non-SQLite DB header, it treats it as a legacy encrypted or unreadable DB and fails fast with remediation guidance.
{
"security": {
"dbEncryption": {
"enabled": false,
"cipherPageSize": 4096
}
}
}
Legacy SQLCipher Databases
If you still have an old SQLCipher-encrypted database, use an older build to export or decrypt it before upgrading. The current runtime does not attempt to unlock or migrate SQLCipher files in place.
Key Registry¶
The Key Registry is an Ent-backed store that manages encryption and signing keys. Each key has a type, a name, and an optional remote key ID (for RPC mode).
Key Types:
| Type | Purpose |
|---|---|
encryption |
AES-256-GCM encryption/decryption |
signing |
HMAC-SHA256 signing (local) or remote signature (RPC) |
The registry tracks key metadata including creation time and last-used timestamp, enabling key rotation auditing.
Wallet Key Security¶
When blockchain payments are enabled, wallet private keys are managed through the same security infrastructure:
| Mode | Storage | Signing |
|---|---|---|
| Local | Derived from passphrase, stored encrypted | In-process ECDSA |
| RPC | Keys remain on companion/hardware signer | Remote signing via RPC |
Spending Limits provide an additional safety layer:
Settings:
lango settings→ Security
{
"payment": {
"limits": {
"maxPerTx": "1.00",
"maxDaily": "10.00"
}
}
}
Companion Connectivity¶
Experimental
Companion-backed RPC signing is still experimental and may change in future releases.
The current runtime does not ship automatic mDNS/Bonjour companion discovery.
Instead, companion apps connect to the gateway's /companion WebSocket endpoint,
and Lango routes approval or RPC crypto requests through currently connected companions.
If no companion is connected, companion-backed approval and RPC signing paths are unavailable until a companion connects to the gateway.
CLI Commands¶
Security Status¶
lango security status
Displays the current security configuration: encryption mode, key count, secret count, and companion connection status.
Passphrase Migration¶
lango security migrate-passphrase
Re-encrypts all secrets and keys under a new passphrase. Prompts for both old and new passphrase.
Secret Management¶
# List all secrets (metadata only, no values)
lango security secrets list
# Store a secret
lango security secrets set <name> <value>
# Delete a secret
lango security secrets delete <name>
Secret Names
Use descriptive, namespaced names for secrets: openai/api-key, telegram/bot-token, wallet/private-key. This makes it easier to manage secrets across integrations.
Configuration Reference¶
Settings:
lango settings→ Security
{
"security": {
"interceptor": {
"enabled": true,
"redactPii": true,
"approvalPolicy": "dangerous"
},
"signer": {
"provider": "local",
"rpcUrl": "",
"keyId": ""
},
"dbEncryption": {
"enabled": false,
"cipherPageSize": 4096
},
"kms": {
"region": "",
"keyId": "",
"fallbackToLocal": true,
"timeoutPerOperation": "5s",
"maxRetries": 3,
"azure": {
"vaultUrl": ""
},
"pkcs11": {
"modulePath": "",
"slotId": 0,
"keyLabel": ""
}
}
}
}