title: Infisical Secrets Integration tags: [infisical, secrets, infrastructure, session] created: 2026-05-25 updated: 2026-05-25 status: active related:
Session: Infisical Secrets Integration
Summary
Integrated self-hosted Infisical as the centralized secrets manager for the entire Lemna platform. Replaced scattered .env files with a single root .env containing 4 bootstrap variables that unlock all other secrets from the Infisical server.
What Changed
Core SDK (sdk/environ.py)
- Added
load_infisical()— authenticates with Infisical using 4 bootstrap vars, fetches all project secrets, injects intoos.environ. Lenient: warns and continues if Infisical is unreachable. - Added
load_env()— convenience function that callsload_dotenv()thenload_infisical(). - Kept
load_dotenv()for backward compatibility. - All call sites updated:
load_dotenv→load_envinclient.py,backends.py,cas.py,database.py,auth.py,conftest.py. - Pantry CLI: added
_load_infisical()alongside_load_dotenv().
Infisical Server
- Deployed at
pipeline/backend/secrets/(Docker Compose: infisical-backend, postgres, redis). - Dashboard:
http://localhost:8192 - Project:
lemna-platform(ID:29f05bd6-5ef2-4557-b8f6-d952861f59ad) - Environments:
dev(localhost),staging(empty),prod(production) - 9 secrets per environment:
API_URL,API_KEY,R2_ENDPOINT_URL,R2_ACCESS_KEY_ID,R2_SECRET_ACCESS_KEY,RUNPOD_API_KEY,OLLAMA_API_KEY,LINEAR_API_KEY,GITHUB_TOKEN
Docker Compose
- All 9 endpoint compose files: 5 env passthroughs → 4 Infisical bootstrap vars
database/docker-compose.yml: removedenv_file: .env, added Infisical passthrough + hardcoded MongoDB configLocalBackend.submit(): 5 hardcoded env vars → 4 Infisical bootstrap vars
Dependencies
infisicalsdk>=1.0.16added tolemna-sdk,database, andpantrypython-dotenvremoved fromdatabase(no longer needed)
Files Deleted
pipeline/backend/refactor/.env— obsoletepipeline/backend/tests/.env— regenerated byload_env()pipeline/backend/bucket/.env— dev RustFS creds now in Infisicaldevenvironment- Root
.env.old— backup, then deleted
Root .env
Trimmed from 12 secrets to 4 bootstrap vars:
INFISICAL_CLIENT_ID=01fd2bad-f309-4ce0-8fe3-fffa69a1fa05
INFISICAL_CLIENT_SECRET=00a7c93eb9de7c6aed8c51342fde785a1ea41415949365cff8989cc491a619c3
INFISICAL_API_URL=http://localhost:8192
INFISICAL_ENV=dev
Not Secrets (Hardcoded in Docker Compose)
DATABASE_URL=mongodb://mongodb:27017/?retryWrites=false— container networkingDATABASE_NAME=lemna— just a nameME_CONFIG_MONGODB_URL,ME_CONFIG_BASICAUTH_USERNAME/PASSWORD— dev-only trivial creds- MongoDB has no auth enabled currently
Secrets Skill
- Created
.opencode/skills/secrets/SKILL.md— comprehensive documentation - Updated
AGENTS.md— added to skills table, key terms, initialization list - Updated
essentialsskill — fixed endpoint env gotcha, added Secrets gotchas section - Updated
mapskill — updated backend directory structure, key terms, addedsecrets/directory
Key Decisions
- Lenient over strict —
load_infisical()warns and continues if Infisical is unreachable. Allows offline work and graceful degradation. - Pattern 3 (host injection) —
load_env()in code, notinfisical runCLI wrapper. No CLI dependency needed, works inside Docker containers via env passthrough. bucket/.envdeleted — dev RustFS creds moved to Infisicaldevenvironment.DATABASE_URLhardcoded — container networking, not a secret. Avoids circular dependency on Infisical for database startup.- URL-safe PostgreSQL password —
openssl rand -base64 24 | tr '+/' '-_'to avoid/chars breakingDB_CONNECTION_URI.
Gotchas Hit During Implementation
POSTGRES_PASSWORDwith/chars brokeDB_CONNECTION_URI. Fixed by using URL-safe base64.infisicalsdkmethod names differ from docs:create_secret_by_namenotcreate_secret,list_secretsreturnsListSecretsResponse.secretsnot a plain list.- Machine identity creation — must be scoped to the project, not org level. Org-level identities return 401.
Architecture
Root .env (4 bootstrap vars)
│
├─ load_dotenv() → reads .env, puts 4 vars into os.environ
├─ load_infisical() → authenticates → fetches 9+ secrets → injects
└─ load_env() → calls both in order
All services use load_env() — backward compatible with old .env files if Infisical is unavailable.