Architecture Decisions
ADR-0007: 1Password Secret Management
Documents the use of 1Password as single source of truth for all secrets
ADR-0007: 1Password Secret Management
Status
Accepted
Date: 2026-03-05
Context
The monorepo manages secrets for 8+ apps across three environments (local development, CI/CD, and Kubernetes production). The traditional approach of .env files poses significant risks:
- Accidental commits:
.envfiles with real secrets can be committed despite.gitignore - Environment drift: Local, CI, and production secrets diverge silently
- No audit trail: No visibility into who accessed or changed secrets
- Manual rotation: Updating a secret means touching multiple places
- Onboarding friction: New developers need secrets shared via insecure channels
Key requirements:
- Single source of truth for all secrets across all environments
- Zero secrets on developer machines (ephemeral injection only)
- Automated sync to Kubernetes without manual
kubectl create secret - Support for environment-specific secrets (prod, staging, preview)
Decision
Use 1Password as the single source of truth for all secrets, with environment-specific injection:
Local Development
.env.opfiles containop://URI references and are committed to git (zero secrets in the file)op run --env-fileresolves references at runtime, injecting secrets ephemerally into the process- Secrets never touch disk; they exist only in process memory
scripts/op-dev.shwraps theop runinvocation for convenience
Kubernetes Production
- 1Password Operator watches
OnePasswordItemcustom resources in the cluster - Operator syncs vault items to native Kubernetes Secrets automatically
- Secrets are referenced in deployment manifests via standard
envFromorenv[].valueFrom - Operator preserves original casing from 1Password items
CI/CD (GitHub Actions)
- Minimal set of deploy credentials stored as GitHub Actions secrets (Harbor credentials, kubeconfig)
- Application secrets are not needed in CI since apps are built with
CI=true(skips env validation)
Vault Organization
- Items named
{app}-{env}(e.g.,lexilink-prod,lexilink-staging) - Convex admin keys named
convex-{app}-{env} - All items stored in the
k3vault
Consequences
Positive
- Zero secrets on disk at any point in the development or deployment lifecycle
- Single source of truth eliminates environment drift
- 1Password audit log provides visibility into secret access
- Auto-rotation possible via 1Password’s API
- Operator automatically syncs changes to Kubernetes without manual intervention
.env.opfiles serve as self-documenting lists of required environment variables
Negative
- Requires
OP_SERVICE_ACCOUNT_TOKENfor local development (one-time setup) - 1Password Operator is an additional dependency in the cluster
- 1Password availability is a hard dependency (outage blocks local dev and secret rotation)
- Vault item titles with em-dashes break
op read(must useop item get <id>instead)
Neutral
harbor-registrysecret remains manually managed (.dockerconfigjsonkey format is incompatible with the operator)- t3-env still validates environment variables at runtime, providing a safety net
- Non-secret defaults (localhost URLs, app names) live in
.env.developmentand are merged into.env.op
Alternatives Considered
Alternative 1: HashiCorp Vault
- Description: Self-hosted secret management with dynamic secrets
- Pros: Dynamic secrets, fine-grained policies, enterprise features
- Cons: Significant operational overhead, complex setup, overkill for team size
- Why not chosen: Too complex for a small team; 1Password provides sufficient functionality with much less overhead
Alternative 2: SOPS/age Encrypted Files
- Description: Encrypt
.envfiles with age keys, decrypt at deploy time - Pros: Simple, git-native, no external service dependency
- Cons: Secrets still on disk (encrypted), manual rotation, no audit trail, key management overhead
- Why not chosen: Secrets on disk (even encrypted) is a weaker security posture; no centralized management
Alternative 3: Doppler
- Description: SaaS secret management platform
- Pros: Good DX, environment management, integrations
- Cons: Another SaaS dependency, per-seat pricing, less control than 1Password (already in use)
- Why not chosen: Already using 1Password for team password management; consolidating on one tool reduces complexity
References
- 1Password CLI documentation
- 1Password Kubernetes Operator
- t3-env documentation
- Related: ADR-0001 (Monorepo Structure)
Notes
scripts/generate-env-op.shcan regenerate.env.opfiles from 1Password item fields- Lexilink has additional
.env.op.stagingand.env.op.previewfiles for multi-environment support - When adding a new app, create the vault item first, then generate the
.env.opfile