Skip to content

Secrets

Norn v2 uses SOPS + age for secret management. Secrets are encrypted at rest and decrypted at deploy time, then injected as environment variables into Nomad tasks.

How It Works

  1. Secrets are stored in a secrets.enc.yaml file alongside the app's infraspec.yaml
  2. During the submit step of the deploy pipeline, Norn decrypts the file using SOPS
  3. Decrypted key-value pairs are merged into the environment variables passed to nomad.Translate()
  4. Nomad injects them as environment variables in the running containers

v1 vs v2

In v1, secrets were synced to Kubernetes Secrets and mounted into pods. In v2, there is no Kubernetes — secrets go directly from SOPS to Nomad environment variables during the pipeline.

Setup

1. Generate an age key

bash
age-keygen -o ~/.config/sops/age/keys.txt

macOS

SOPS on macOS looks for age keys at ~/Library/Application Support/sops/age/keys.txt. Symlink to avoid duplicating:

bash
ln -sf ~/.config/sops/age/keys.txt \
  ~/Library/Application\ Support/sops/age/keys.txt

2. Create a .sops.yaml in the app directory

yaml
creation_rules:
  - age: >-
      age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

3. Create and encrypt secrets

bash
# Create a plaintext file
cat > secrets.yaml <<EOF
DATABASE_URL: postgres://user:pass@host:5432/db
API_KEY: sk-1234567890
EOF

# Encrypt it
sops -e secrets.yaml > secrets.enc.yaml
rm secrets.yaml

4. Declare expected keys in infraspec

yaml
secrets:
  - DATABASE_URL
  - API_KEY

The secrets list is informational — it tells the UI and CLI which keys to expect. The actual values come from secrets.enc.yaml.

CLI Management

bash
# List secret keys for an app
norn secrets myapp

# Compare declared, encrypted, and plaintext secret state
norn secrets status
norn secrets status myapp

# Plan value-safe plaintext env migrations
norn secrets migrate-plan
norn secrets migrate-plan myapp

# Set or update a secret
norn secrets set myapp DATABASE_URL=postgres://...

# Delete a secret
norn secrets delete myapp OLD_KEY

The status command compares the secrets list in infraspec.yaml with encrypted keys in secrets.enc.yaml and reports plaintext secret-like values still present in env blocks. Secret values are never printed.

The migrate-plan command turns those plaintext findings into a value-safe checklist. It shows the app, field, key, whether the key is declared in secrets, whether it already exists in secrets.enc.yaml, and the recommended action. It does not print or move secret values; operators still make the actual secrets.enc.yaml change with SOPS or norn secrets set.

Use the strict gate when you want plaintext secret-like env values to block validation:

bash
norn validate --strict-secrets
NORN_STRICT_SECRETS=true norn preflight myapp HEAD

The gate is opt-in so older apps can be migrated deliberately. Once an app is clean, keep NORN_STRICT_SECRETS=true in the platform runtime env to prevent drift.

The set command decrypts the file, updates the key, and re-encrypts.

Secrets Flow