Skip to main content

Choose a secrets backend

AgentArea handles two different kinds of secret, and they are stored in different places by different mechanisms. Confusing them is the usual reason a deployment ends up with credentials somewhere the operator did not expect. This guide covers both. The secret manager backend is the choice with consequences; the deployment secrets are mostly about supplying your own instead of letting the chart generate them.

Prerequisites

  • A running or planned deployment — see Kubernetes or Docker Compose
  • For Infisical: a project, a machine identity, and its client ID and client secret

Steps

1. Choose a workspace secret backend

Two backends exist. SECRET_MANAGER_TYPE selects between them, and any other value raises Invalid SECRET_MANAGER_TYPE at startup rather than falling back. database (default). Secrets are encrypted with Fernet and stored in the encrypted_secrets table in the platform’s own PostgreSQL database, scoped by workspace_id. Pick this when you do not already run a secret manager: it adds no operational surface, and the ciphertext is covered by your existing database backup. The tradeoff is that the encryption key travels as an environment variable next to the database that holds the ciphertext, so anyone who can read both the pod environment and the database can read every stored credential. infisical. Secrets are read from and written to an Infisical instance through the infisical-sdk client. Pick this when you already operate Infisical and want credential storage outside the application database.

2. Configure the database backend

Generate a Fernet key. It must be a real Fernet key, not an arbitrary string.
On Kubernetes the chart generates this for you into the agentarea-app-secrets Secret under key encryption-key, and reuses the existing value on every subsequent upgrade. To supply your own, create the Secret before installing:
All four keys must be present. The chart reads each by name, and a Secret missing encryption-key leaves the API crash-looping at startup. Under Compose, set it in .env:
This key cannot be rotated in place. Changing it makes every secret already written unreadable — Fernet raises InvalidToken on decrypt, and there is no re-encryption path in the platform. Treat it as permanent for the life of the database, and back it up separately from the database itself, or a restore gives you ciphertext you cannot open.

3. Configure the infisical backend

Set the same variables on the worker. The worker resolves secrets when it runs agent activities, so a backend configured on the API alone leaves task execution unable to read provider credentials. SECRET_MANAGER_ENDPOINT defaults to https://app.infisical.com when unset. Both SECRET_MANAGER_ACCESS_KEY and SECRET_MANAGER_SECRET_KEY are required; the factory raises Infisical credentials not configured at startup if either is missing, before any request is served. The infisical-sdk package must be installed in the image. If it is not, the factory raises Infisical SDK not installed.

4. Supply the deployment secrets

On Kubernetes the chart generates any of these that do not already exist, and annotates each with helm.sh/resource-policy: keep so helm uninstall leaves them behind. On upgrade it looks each one up and reuses the existing value, so credentials do not rotate underneath a running deployment. Rename any of them through global.secrets.postgresql, global.secrets.redis, global.secrets.rustfs, and global.secrets.application to point at Secrets you manage yourself — with an external secrets operator, for instance. When you disable a bundled dependency, the chart stops generating its Secret. Setting postgresql.enabled=false means you must create agentarea-postgresql-secret with username and password yourself, because the migration Jobs and every service still read it.

5. Replace the development credentials under Compose

.env.example ships values that are fine to develop against and not fine to deploy:
  • SECRET_MANAGER_ENCRYPTION_KEY — a real Fernet key, committed to the repository
  • SANDBOX_ACTIVATION_AUTH_SECRET and SANDBOX_CLEANUP_AUTH_SECRET — placeholders that say change-in-prod; both must be at least 32 bytes
  • KRATOS_JWKS_B64 — a test JWKS including the private key d, so anyone with the repository can mint tokens the API will accept
  • POSTGRES_PASSWORD=postgres, RUSTFS_ACCESS_KEY=minioadmin, RUSTFS_SECRET_KEY=minioadmin
Compose declares the two sandbox secrets with ${VAR:?...}, so an empty value aborts the run instead of starting an unauthenticated sandbox activation path. The others have working defaults and will not stop you.

Verify

Confirm the backend the API actually selected. SecretManagerFactory logs its type on initialization:
Under Compose:
Then confirm a round trip through the real path: add an LLM provider key in the UI under the provider configuration, and check that a row appears without the plaintext being visible.
The encrypted_value prefix should be Fernet ciphertext (gAAAAA...), not the key you typed. If you configured Infisical, the table stays empty and the secret appears in your Infisical project instead.

Troubleshooting

API exits at startup with SECRET_MANAGER_ENCRYPTION_KEY environment variable must be set. The database backend has no key. This is validated in SecretManagerFactory.__init__, so it fails at construction rather than on the first secret read — the process will not serve traffic in a state where secret storage is broken. Reads fail with InvalidToken after a restore or a redeploy. The encryption key no longer matches the ciphertext. Restore the original key. There is no recovery path from the database side, and no re-encryption command; if the key is genuinely lost, every stored credential has to be re-entered. Agents fail on tool calls with a missing credential, but the UI shows the secret saved. The worker and the API disagree on SECRET_MANAGER_TYPE or on the key. Both processes construct their own secret manager. Compare the Initialized SecretManagerFactory with type: line in each. helm upgrade rotates a password and breaks the database connection. This happens when the Secret was deleted between operations — the chart’s lookup finds nothing and generates a fresh value, while the PostgreSQL volume still has the old one. The helm.sh/resource-policy: keep annotation prevents this on uninstall, but not against a manual kubectl delete secret. Infisical is configured but startup raises Infisical SDK not installed. The infisical-sdk package is absent from the image. It is an optional dependency of the platform.