Skip to main content

Overview

Infisical is the secret management platform the GAIA team uses to share environment variables and API keys. It gives you:
  • Central management of secrets across dev/staging/production environments
  • Access control with per-user and per-service permissions
  • Audit logging for every secret read and change
  • Instant revocation when a credential leaks or a teammate leaves
Infisical is optional. Self-hosters and open-source contributors should skip this page entirely and use plain .env files — every app falls back to local environment variables automatically. Infisical is only required in production (ENV=production).

How Infisical Works with GAIA

At startup, each app (API, bots, voice agent) calls its Infisical loader (inject_infisical_secrets() in Python, injectInfisicalSecrets() in TypeScript). The Next.js web app deliberately does not — see Frontend (Web).
  1. Authentication: authenticates with a machine identity (Universal Auth client ID + secret)
  2. Secret retrieval: fetches all secrets for the environment matching your ENV value (development, staging, or production)
  3. Environment injection: injects each secret into the process environment — only if that variable is not already set
  4. Application startup: the app reads its settings from the combined environment
Local environment variables always win. The loaders never overwrite a variable that is already set in your shell, .env file, or container environment. Infisical only fills the gaps. (The infisical run CLI flow below is the one exception — it injects secrets before the process starts, so those values are already “local” by the time .env files load.)
If no Infisical variables are configured:
  • In development, the loader logs a message and continues with your local .env
  • In production, the app refuses to start
The default when ENV is unset differs by runtime. The Python apps (API, voice agent) default to production and so refuse to boot without Infisical — apps/api/.env.example ships ENV=development, so copy it rather than writing .env from scratch. The bots default to development, keeping a bare checkout runnable with no setup; their deployed containers cannot reach that fallback because apps/bots/Dockerfile pins ENV=production.
Team members authenticate as themselves with the Infisical CLI — no machine identity credentials on laptops. You get personal permissions, audit-logged access, personal secret overrides, and instant revocation on offboarding.
1

Install the Infisical CLI

Other platforms: see the Infisical CLI docs.
2

Log in

This opens a browser window. Use infisical login -i inside containers or WSL2.
3

Run any task through the CLI

Prefix any mise task with infisical:
This wraps the task in infisical run --env=development -- mise run <task>, injecting every secret from the development environment before the processes start. Pass task flags after -- so mise forwards them instead of reading them itself:
Any other command works the same way without mise:
The repo’s .infisical.json tells the CLI which project to use — it contains only the project ID and default environment, no secrets, so it is committed and needs no setup on your part. The project ID is an identifier, not a credential: fetching secrets still requires infisical login with an account that has access to the project. Infra URLs: the development environment stores localhost URLs (mongodb://localhost:27017, postgresql://...@localhost:5432/...), because mise dev runs the API and web on the host and reaches dockered infra through published ports. Containers do not use these — infra/docker/docker-compose.yml sets the mongo / postgres service hostnames explicitly, and those take precedence over env_file. Putting Docker hostnames in Infisical instead breaks every native run. Local tweaks: use personal overrides in the Infisical dashboard instead of editing .envinfisical run applies your personal value while teammates keep the shared one. Offline: the CLI caches previously fetched secrets, so infisical run keeps working without a network connection.

Self-Hosters and Contributors

You do not need Infisical. Copy .env.example to .env in each app directory and fill in the values. See Environment Variables. If you run your own Infisical project and want GAIA to pull from it in a headless environment, configure a machine identity:
  1. Create a project at app.infisical.com and add your secrets
  2. Go to Organization Access Control → Identities → Create Identity with Universal Auth
  3. Grant it read access to your project
  4. Add the credentials to your .env:
The three INFISICAL_* variables bootstrap the connection, so they must live in the local environment — they cannot be stored in Infisical itself. Setting all three enables the fetch; setting only some of them logs a warning in development and fails the boot in production.

Production: Machine Identity

Production authenticates with a single machine identity shared by every service (see infra/docker/docker-compose.prod.yml).
  1. In Infisical: Organization Access Control → Identities → Create Identity (Universal Auth)
  2. Grant it read access to the project, environment production
  3. On the Swarm manager, store its credentials:
Every service attaches these three secrets directly, and scripts/docker-entrypoint.sh reads them from /run/secrets/gaia_infisical_machine_identity_* before exec.

Scoping per service later

One identity means any compromised container can read the whole project. To limit that blast radius, create an identity per service, scope each to the paths that service needs, and store its credentials under its own secret names. Then attach them with Compose’s long syntax, which maps a per-service secret onto the canonical name the entrypoint reads:
Only the compose file changes — the entrypoint and application code are identical either way.

Frontend (Web)

The Next.js app is intentionally not wired to Infisical — it calls no loader. Its environment is six NEXT_PUBLIC_* variables plus one server-only secret (BLOG_BEARER_TOKEN, used by src/app/api/blog/route.ts). NEXT_PUBLIC_* values are compiled into the browser bundle at build time, so they are public by definition — anyone can read them from the shipped JavaScript. Storing them in Infisical adds ceremony without adding secrecy. Keep them in apps/web/.env.local locally and in the build environment for CI. In development the app still picks up Infisical values under mise infisical dev: the CLI injects them into the process environment before Next starts, and Next ranks process.env above .env.local. In production web deploys to Cloudflare Workers (OpenNext), not the Swarm, so the server-only secret belongs in Cloudflare’s own store:
Do not bootstrap Infisical into the Worker. Fetching secrets at the edge means shipping the machine identity’s client ID and secret into the Worker — trading one narrow secret for a credential that unlocks the entire project, plus an auth round-trip on cold start.

Troubleshooting

Solution: The app resolved ENV to production — either explicitly, or because it is unset and the Python loaders default to production — but has no Infisical credentials. Provide the machine identity credentials (in production these arrive as Docker Swarm secrets), or set ENV=development for local work.
Solution: Only some of INFISICAL_PROJECT_ID, INFISICAL_MACHINE_IDENTITY_CLIENT_ID, and INFISICAL_MACHINE_IDENTITY_CLIENT_SECRET are set. Set all three to enable Infisical, or remove all three to use local .env only.
Solution: Verify the client ID and client secret, ensure the machine identity has read access to the project, and check that the identity is enabled.
Solution: Verify the environment slug matches your ENV value (development/staging/production), check that the secrets exist in that environment of the Infisical project, and confirm the identity’s role covers the secret path.
Solution: A local environment variable with the same name is set — local values always take precedence over Infisical. Unset the local variable (check your shell and .env files) to use the Infisical value.

Debug Mode

Check the application logs during startup — the loaders log connection, fetch, and injection timing, and any Infisical errors.

Security Best Practices

Humans use personal logins

Team members authenticate with infisical login, never with shared machine identity credentials. Offboarding a person revokes their access instantly.

Scope identities to shrink blast radius

Production currently shares one machine identity across services, so any compromised container can read the whole project. Splitting it per service is a compose-only change — see Scoping per service later.

Environment separation

Use the development/staging/production environments within the project, and separate secrets per environment.

Rotate and audit

Set client-secret TTLs where possible, rotate leaked credentials immediately, and review the audit log for unexpected access.

Next Steps

Environment Variables

Configure your environment variables and learn about Infisical integration

Docker Setup

Deploy GAIA with Docker Compose and Infisical secrets
For more detailed Infisical documentation, visit the official Infisical docs.