The chart supports four ways to provide secrets to Vigil pods. Pick whichever fits your existing secret-management setup; all four produce the same Kubernetes Secret shape that backend, daemon, llm-worker, and the db-init Job reference.
| Pattern | Secret lives | When to pick | Docs |
|---|---|---|---|
| Plain values | values.yaml (or --set) |
Local dev, one-off demos | HELM.md — Secrets |
Pre-created Secret (secrets.existingSecret) |
Kubernetes | You already manage secrets out-of-band | HELM.md — Secrets |
ExternalSecrets Operator (secrets.externalSecret.enabled) |
AWS SM / Vault / GCP SM | Secrets already in an external store | HELM.md — Secrets |
| SealedSecrets / SOPS | Git, encrypted | GitOps without an external secret store | This doc |
All patterns require the same set of keys in the final Kubernetes Secret. At minimum:
ANTHROPIC_API_KEY— Claude API keyPOSTGRES_PASSWORD— used by the DB-init Job and all app podsJWT_SECRET_KEY— required whenconfig.DEV_MODE=false
Plus whichever integrations you use: SPLUNK_PASSWORD, SLACK_BOT_TOKEN,
VIRUSTOTAL_API_KEY, etc. See env.example for the full list.
Pattern 1 — Bitnami SealedSecrets
SealedSecrets lets you commit
encrypted Secret manifests to git. The controller in the cluster decrypts them
at apply time, producing a regular Secret resource.
Prerequisites
# Controller (once per cluster)
helm install sealed-secrets \
--namespace kube-system \
--repo https://bitnami-labs.github.io/sealed-secrets sealed-secrets
# CLI (once per dev machine)
brew install kubeseal # or download from github.com/bitnami-labs/sealed-secrets/releases
Workflow
-
Write a plain Secret manifest with all the keys you need:
# secret.yaml apiVersion: v1 kind: Secret metadata: name: vigil-secrets namespace: vigil type: Opaque stringData: ANTHROPIC_API_KEY: sk-ant-... POSTGRES_PASSWORD: "a-strong-password" JWT_SECRET_KEY: "generated-with-secrets.token_urlsafe-64" SLACK_BOT_TOKEN: xoxb-...Do not commit this file.
-
Seal it:
kubeseal -o yaml < secret.yaml > vigil-sealed-secret.yamlThe output is safe to commit — only the controller’s private key can decrypt it.
-
Apply the sealed manifest and install the chart:
kubectl apply -f vigil-sealed-secret.yaml # The controller creates the vigil-secrets Secret automatically. helm install vigil ./infra/helm/vigil \ -n vigil --create-namespace \ --set secrets.existingSecret=vigil-secrets -
To rotate: re-run step 1 with new values, re-seal, re-apply. The chart does not need to be upgraded — the app pods pick up the new Secret on the next restart (the chart annotates backend/daemon pods with a Secret checksum so they restart automatically on
helm upgrade; for out-of-band Secret changes, trigger a rollout manually:kubectl rollout restart -n vigil deploy).
See docs/examples/sealed-secret.yaml for a reference template.
Pattern 2 — Mozilla SOPS
SOPS encrypts file contents with a KMS,
age, or PGP key. Unlike SealedSecrets, there’s no in-cluster component —
you decrypt at deploy time and pipe the plaintext into kubectl apply.
Prerequisites
brew install sops age
# Generate an age key (one per team or per environment)
age-keygen -o ~/.sops/vigil.txt
# Commit the PUBLIC half to your repo in .sops.yaml
Create .sops.yaml at the repo root:
creation_rules:
- path_regex: secrets/.*\.yaml$
age: >-
age1your-public-key-here
Workflow
-
Write the plain Secret manifest:
# secrets/vigil-secrets.yaml apiVersion: v1 kind: Secret metadata: name: vigil-secrets namespace: vigil type: Opaque stringData: ANTHROPIC_API_KEY: sk-ant-... POSTGRES_PASSWORD: "a-strong-password" JWT_SECRET_KEY: "..." -
Encrypt in place:
sops -e -i secrets/vigil-secrets.yamlCommit the encrypted file.
-
Deploy:
# Decrypt and apply in one pipe export SOPS_AGE_KEY_FILE=~/.sops/vigil.txt sops -d secrets/vigil-secrets.yaml | kubectl apply -f - helm install vigil ./infra/helm/vigil \ -n vigil --create-namespace \ --set secrets.existingSecret=vigil-secrets -
Rotation: edit the encrypted file directly with
sops secrets/vigil-secrets.yaml(opens in your$EDITORwith decrypted plaintext; saves back encrypted).
See docs/examples/sops-config.yaml for a
reference .sops.yaml.
SOPS + Helm (alternative: helm-secrets plugin)
If you want helm install to handle decryption itself, install the
helm-secrets plugin and commit a
sops-encrypted values file:
helm plugin install https://github.com/jkroepke/helm-secrets
# Encrypted values file
sops -e -i infra/helm/vigil/values-prod-secrets.yaml
# Install
helm secrets install vigil ./infra/helm/vigil \
-n vigil --create-namespace \
-f infra/helm/vigil/values-prod-secrets.yaml
This lets you use secrets.anthropicApiKey: ... directly (not existingSecret)
because the values file itself is encrypted at rest.
Picking between them
-
ExternalSecrets Operator (pattern 3 in the main doc) is best when you already have AWS Secrets Manager, Vault, or GCP Secret Manager as the source of truth. Secrets rotate in the external store; the cluster pulls fresh values on the refresh interval.
-
SealedSecrets (pattern 1 here) is best when secrets must live in git (GitOps) and you want zero external dependencies beyond the cluster itself.
-
SOPS (pattern 2 here) is best when you already standardize on encrypting config files and don’t want a cluster-side decryption controller.
All three work cleanly with Vigil’s secrets.existingSecret. Never mix —
pick one and stick with it for the full Vigil install.