Bridges secrets from vault backends and infrastructure outputs into sops-encrypted files, then deploys them to NixOS machines. Also spawns pre-credentialed shells and exports all secrets as environment variables.
Crate:
dogma-rust— Binary:dogma
# dogma.yml
name: myproject
env: [dev, prod]
admin:
- gpg: 3246EC6F403D8F3403E34A90BCDABC277B1ED8AB
vault:
hcloud-token:
pass: myproject/{env}/hcloud-token
stripe-webhook-secret:
pass: myproject/{env}/stripe-webhook-secret
infra:
credentials:
TF_VAR_hcloud_token:
from: vault
ref: hcloud-token
machines:
backend:
hostname: myproject-{env}
ip: { from: infra, unit: hetzner, output: server_ip }
secrets: [backend]
secrets:
backend:
stripe_webhook_secret: { from: vault, ref: stripe-webhook-secret }
server_ip: { from: infra, unit: hetzner, output: server_ip }$ dogma deploy prod --new
dogma: checking dependencies...
dogma: normalizing config...
dogma: refreshing infra cache (hetzner)...
dogma: generating .sops.yaml from SSH host keys...
dogma: encrypting secrets for myproject-prod...
dogma: deploying backend (1.2.3.4)...
→ nixos-rebuild switch --target-host root@1.2.3.4 --flake .#myproject-prod
dogma: tagging deploy/v26.06.0001
dogma: done in 142s
$ dogma infra apply prod hetzner
dogma: normalizing config...
dogma: resolving credentials...
dogma: tofu init...
dogma: tofu apply...
hcloud_server.backend: Creating...
hcloud_server.backend: Creation complete (id=12345678)
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
$ dogma shell prod
dogma: resolving TF_VAR_hcloud_token...
dogma: entering prod shell (exit to return)
[dogma-prod 14:23:01] ~/myproject $
$ dogma env prod
export BACKEND_STRIPE_WEBHOOK_SECRET='whsec_...'
export BACKEND_SERVER_IP='1.2.3.4'
cargo install dogma-rust # binary installed as `dogma`
# or from source
cargo build --release # binary at target/release/dogmaSet DOGMA_VAULT per developer in .bashrc or per project in shell.nix:
export DOGMA_VAULT=pass # or: envvar (default)Bash — add to ~/.bashrc:
source <(dogma completions bash)Zsh — add to ~/.zshrc:
source <(dogma completions zsh)Fish — install once:
dogma completions fish > ~/.config/fish/completions/dogma.fish<env> is completed from dogma.yml. <unit> is completed from subdirectories of infra.path. <host> is completed from machine names in dogma.yml.
Prints export VAR='value' for all infra.credentials. Eval to load.
eval $(dogma credentials dev)Prints export GROUP_FIELD='value' for every secret, uppercased.
eval $(dogma env dev)
# → export BACKEND_API_KEY='...'
# → export BACKEND_SERVER_IP='...'Reads from .dogma/cache/<env>.json.
dogma output dev # full JSON
dogma output dev hetzner # one unit
dogma output dev hetzner server_ip # raw valueSpawns a new shell with infra.credentials loaded. Prompt shows [dogma-<env> HH:MM:SS]. Exit to return.
dogma shell devRuns: normalize → validate → tofu init → tofu apply. Credentials injected as env vars.
dogma infra apply dev hetzner
dogma infra apply dev hetzner --migrate-stateSame as apply but runs tofu destroy.
Full deploy pipeline.
dogma deploy dev --new # new CalVer version, full pipeline
dogma deploy dev --latest # promote latest deploy/* tag
dogma deploy dev --version deploy/v26.06.0001 # promote specific tag
dogma deploy dev # interactive: pick from tag list
dogma deploy dev backend --new # one host only--new pipeline:
| Step | Action |
|---|---|
| 0 | Dep check: ssh-keyscan, ssh-to-age, sops, nixos-rebuild |
| 1 | Normalize + validate dogma.yml |
| 2 | Dirty tree check — commit or abort |
| 3 | Pre-deploy hooks |
| 4 | Infra cache refresh (all envs) |
| 5 | Generate .sops.yaml from SSH host keys |
| 6 | Encrypt secrets with sops, commit |
| 7 | nixos-rebuild switch per host |
| 8 | Git tags + push |
| 9 | Post-deploy hooks |
Flags:
| Flag | Description |
|---|---|
--new |
Create new CalVer version, run full pipeline |
--latest |
Promote latest deploy/* tag (no hooks) |
--version <tag> |
Promote specific tag (no hooks) |
--skip-infra |
Use existing infra cache |
--skip-sops |
Use existing .sops.yaml |
--refetch |
Clear all caches and re-fetch |
-m <msg> |
Commit message for dirty tree (only with --new) |
Global flag. Prints elapsed ms to stderr after any command.
dogma --time deploy dev --newname: myproject # used in auto-derived vault paths
env: # list of environments
- dev
- prod
admin: # age/gpg/ssh keys for sops encryption
- gpg: <fingerprint>
- age: age1...
- ssh: ~/.ssh/id_ed25519.pub
infra:
cli: tofu # or: terraform (default: tofu)
path: ./infra # default: ./infra
credentials:
STATIC_VAR: value # injected as-is into tofu
SECRET_VAR:
from: vault
ref: <vault-key>
vault:
<key>:
pass: path/{env}/key # {env} substituted per environment
envvar: MY_VAR # flat string or per-env map; auto-derived if omitted
machines:
<name>:
hostname: host-{env} # {env} substituted; or per-env map
user: deployer # default: root
ip: "1.2.3.4" # static IP
ip: # or from infra output
from: infra
unit: <unit>
output: <output-key>
secrets: # secret groups to encrypt for this machine
- <group>
deployer: nixos-rebuild # default; only option currently
secrets:
<group>:
<field>:
from: vault
ref: <vault-key>
<field>:
from: infra
unit: <unit>
output: <output-key>
nix:
path: ./nix # default: ./nix
secrets: ./nix/secrets # default: ./nix/secrets
sops: ./nix/.sops.yaml # default: ./nix/.sops.yaml
deploy:
strategy: nixos-rebuild # default
hooks:
pre-deploy:
- ./custom/bump-version.sh
post-deploy:
- ./custom/notify-slack.shSet DOGMA_VAULT per developer — not in dogma.yml.
| Backend | DOGMA_VAULT |
How it resolves |
|---|---|---|
envvar |
(default) | Reads $VAR from process env. Var name from vault.<key>.envvar.<env>, auto-derived as UPPER_SNAKE if omitted |
pass |
pass |
Calls pass <path>. Path from vault.<key>.pass.<env>, auto-derived as <name>/<env>/<key> if omitted |
Scripts must be executable. Both hooks receive:
| Variable | Example | Notes |
|---|---|---|
DOGMA_VERSION |
deploy/v26.06.0001 |
CalVer tag |
DOGMA_ENV |
dev |
Target environment |
DOGMA_HOSTS |
backend\nworker |
Newline-separated machine names |
DOGMA_DEPLOYED_IPS |
deployer@1.2.3.4 |
Post-deploy only |
#!/usr/bin/env bash
# custom/bump-version.sh — write version into package.json
version="${DOGMA_VERSION#deploy/}"
jq --arg v "$version" '.version = $v' backend/package.json > tmp && mv tmp backend/package.jsonAdd to .gitignore:
.dogma/dogma-expanded.yml
.dogma/cache/
.dogma/age-keys/
| File | Purpose |
|---|---|
.dogma/dogma-expanded.yml |
Normalized config with all defaults filled in |
.dogma/cache/<env>.json |
Cached infra outputs |
.dogma/age-keys/<host>.pub |
Cached SSH→age host keys |
The binary runs on any Linux with no runtime deps. External tools are checked lazily — only when the command that needs them actually runs.
| Tool | Required by |
|---|---|
pass |
DOGMA_VAULT=pass |
tofu / terraform |
infra apply, infra destroy, deploy |
ssh-keyscan |
deploy --new |
ssh-to-age |
deploy --new |
sops |
deploy --new |
nixos-rebuild |
deploy (nixos-rebuild strategy) |