Your credentials never leave your KMS context.
Connector credentials and LLM API keys are encrypted with AES-256-GCM envelope encryption. Each organization's data encryption key is wrapped by your AWS KMS customer master key with a per-tenant EncryptionContext — the KMS enforces tenant isolation at the cryptographic layer.
KMS-backed encryption (v2) is the production default. Local KMS is available for development. Bring-your-own LLM key is available on all paid plans (Team+).
How envelope encryption works
When Gateco stores a connector credential or LLM API key, it: (1) generates a unique data encryption key (DEK) for your organization; (2) encrypts the secret with the DEK using AES-256-GCM; (3) wraps the DEK with your AWS KMS customer master key, including {"organization_id": "<your_org_uuid>"} as the EncryptionContext. The ciphertext and the wrapped DEK are stored in the database — the plaintext DEK is never persisted.
When Gateco needs to decrypt a credential, it calls KMS to unwrap the DEK, passing the same EncryptionContext. KMS rejects any unwrap attempt where the organization_id does not match the value bound at wrap time. This means a credential encrypted for Organization A cannot be decrypted even if an attacker has the full database contents — they also need KMS access with the correct context.
Bring your own LLM API key
Each organization can add its own OpenAI API key in Organization Settings → API Keys → LLM API Key. The key is encrypted using the same envelope architecture as connector credentials, with the same per-tenant KMS context binding. Paid-tier organizations (Team/Growth/Enterprise) receive 100 lifetime fallback synthesis calls on Gateco's shared key before their own key is required.
Key rotation is a single operation: add a new key in Organization Settings. Gateco switches immediately. The previous key is discarded.
Environment configuration
KMS_PROVIDER=aws is required in production. Set KMS_KEY_ID to your AWS CMK ARN and AWS_REGION to the CMK's region. KMS_PROVIDER=local is available for development environments (requires DEBUG=true). KMS_PROVIDER=null (the default when KMS_PROVIDER is unset) fails loudly on any decrypt attempt — this is intentional to prevent silent credential exposure in misconfigured environments.
Legacy v1 (Fernet) credentials are re-encrypted to v2 lazily on first use. A bulk migration script (scripts/rotate_org_credentials.py) is available for eager migration. The legacy_credential_migrated audit event confirms each row's migration.
# Production environment (required) KMS_PROVIDER=aws KMS_KEY_ID=arn:aws:kms:us-east-1:123456789012:key/mrk-abc123 AWS_REGION=us-east-1 # Development environment KMS_PROVIDER=local LOCAL_DEV_KMS_MASTER_KEY=<base64-fernet-key> # stable across restarts
Frequently asked questions
Can I use my own KMS key (BYOK at the KMS level)?
Yes. Set KMS_KEY_ID to your own AWS CMK ARN. Gateco uses your key for all envelope encryption operations. You manage key rotation, access policies, and key deletion. If you delete the CMK, all encrypted credentials become permanently unreadable.
How is tenant isolation enforced?
The EncryptionContext {"organization_id": "<org_uuid>"} is bound at DEK wrap time. KMS rejects any unwrap attempt with a different context. This is enforced by AWS KMS at the API level — it is not application-level logic that can be bypassed.
What happens if KMS is unavailable?
Connector health checks and new retrieval requests that require credential decryption will fail with a decryption error. Gateco fails closed — it does not fall back to plaintext or cached credentials. KMS availability should be included in your infrastructure SLA.