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: .env files 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.op files contain op:// URI references and are committed to git (zero secrets in the file)
  • op run --env-file resolves references at runtime, injecting secrets ephemerally into the process
  • Secrets never touch disk; they exist only in process memory
  • scripts/op-dev.sh wraps the op run invocation for convenience

Kubernetes Production

  • 1Password Operator watches OnePasswordItem custom resources in the cluster
  • Operator syncs vault items to native Kubernetes Secrets automatically
  • Secrets are referenced in deployment manifests via standard envFrom or env[].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 k3 vault

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.op files serve as self-documenting lists of required environment variables

Negative

  • Requires OP_SERVICE_ACCOUNT_TOKEN for 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 use op item get <id> instead)

Neutral

  • harbor-registry secret remains manually managed (.dockerconfigjson key 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.development and 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 .env files 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

Notes

  • scripts/generate-env-op.sh can regenerate .env.op files from 1Password item fields
  • Lexilink has additional .env.op.staging and .env.op.preview files for multi-environment support
  • When adding a new app, create the vault item first, then generate the .env.op file
HanseNexus 2026