Skip to content

x71c9/dogma

Repository files navigation

dogma

Dogma bridges secrets from vault backends and infrastructure outputs into sops-encrypted files, then deploys them to your machines. It also comes with a set of tools — pre-credentialed shells, environment variable exports, and more — to make the whole workflow seamless.

Crate: dogma-rust — Binary: dogma

Quick start

# 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: 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'

Install

cargo install dogma-rust          # binary installed as `dogma`
# or from source
cargo build --release             # binary at target/release/dogma

Set DOGMA_VAULT per developer in .bashrc or per project in shell.nix:

export DOGMA_VAULT=pass           # or: envvar (default)

Shell completion

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. <pipeline> is completed from pipeline names in dogma.yml.

Commands

dogma credentials <env>

Prints export VAR='value' for all infra.credentials. Eval to load.

eval $(dogma credentials dev)

dogma env <env>

Prints export GROUP_FIELD='value' for every secret, uppercased.

eval $(dogma env dev)
# → export BACKEND_API_KEY='...'
# → export BACKEND_SERVER_IP='...'

dogma output <env> [unit] [key]

Reads from .dogma/cache/<env>.json.

dogma output dev                        # full JSON
dogma output dev hetzner                # one unit
dogma output dev hetzner server_ip      # raw value

dogma shell <env>

Spawns a new shell with all secrets loaded as environment variables (same as dogma env). Prompt shows [dogma-<env> HH:MM:SS]. Exit to return.

dogma shell dev

dogma infra auth <env>

Spawns a shell with infra.credentials loaded. Prompt shows [dogma-<env> HH:MM:SS]. Exit to return.

dogma infra auth dev

dogma infra apply <env> <unit>

Runs: normalize → validate → tofu inittofu apply. Credentials injected as env vars.

dogma infra apply dev hetzner
dogma infra apply dev hetzner --migrate-state

dogma infra destroy <env> <unit>

Same as apply but runs tofu destroy.


dogma infra init <env> <unit>

Runs: normalize → validate → tofu init. Always re-runs init, bypassing the already-initialized check that apply/destroy use. Escape hatch when the local .terraform/ is out of sync in a way the check can't detect (deleted plugin cache, manual edits).

dogma infra init dev hetzner
dogma infra init dev hetzner --upgrade

dogma deploy <env> [pipeline]

Full deploy pipeline. Always deploys to all hosts. When no pipelines are declared in dogma.yml, an implicit nixos pipeline is used. When exactly one pipeline is declared, [pipeline] may be omitted; with several declared it is required.

A pipeline whose env attribute is set can also be run by name alone: dogma deploy <pipeline>. The single argument is resolved against declared env and pipeline names; a name that matches both is an error and requires the explicit two-argument form.

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                 # named pipeline 'backend'
dogma deploy publish-prod --new                # pipeline with env attribute set

--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
--refetch Clear all caches and re-fetch
-m <msg> Commit message for dirty tree (only with --new)

--time

Global flag. Prints elapsed ms to stderr after any command.

dogma --time deploy dev --new

dogma.yml reference

name: 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:                      # for the implicit default pipeline — only valid
  post-deploy:              # when no pipeline: block is declared below
    - ./custom/notify-slack.sh

pipeline:                   # named pipelines (dogma deploy <env> [pipeline])
  - name: backend
    type: nixos             # nixos | custom (default: custom)
    env: dev                # optional fixed env — enables `dogma deploy backend`
    version_prefix: deploy  # default: deploy → tags deploy/v26.06.0001
    version_scheme: calver  # calver (default) | semver | custom
    version_script: ./custom/next-version.sh  # required when version_scheme: custom
    deployed_prefix: deployed  # default: deployed → tags deployed-<env>-v26.06.0001
    hooks:                  # only two hook names: pre-deploy, post-deploy
      pre-deploy:
        - ./custom/bump-version.sh
      post-deploy:
        - ./custom/notify-slack.sh
  - name: publish
    type: custom            # requires command; {env} {version} {pipeline} substituted
    command: ./scripts/publish.sh {env} {version}

Vault backends

Set 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

Hooks

Declared per pipeline under pipeline[].hooks. When no pipeline: block is declared, a top-level hooks: block applies to the implicit default pipeline instead; if pipelines are declared, a top-level hooks: block is ignored with a warning. Exactly two hook names exist (kebab-case):

  • pre-deploy — runs only on --new deploys (promotions via --latest/--version skip it). Tracked file changes made by the hooks are folded into the deploy commit.
  • post-deploy — runs after a successful deploy.

Each entry is a path to an executable script, relative to the repo root, optionally with arguments. {env}, {version}, and {pipeline} placeholders are substituted before running. Unknown hook names are ignored with a warning at normalize time.

Both hooks receive:

Variable Example Notes
DOGMA_VERSION deploy/v26.06.0001 Version tag
DOGMA_ENV dev Target environment
DOGMA_PIPELINE backend Pipeline name
#!/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.json

Generated files

Add 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

Runtime dependencies

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)

About

Pulls secrets from vault backends and infrastructure into encrypted files, deploys them to remote machines, and provides a set of tools — pre-credentialed shells, environment exports, and more — to make the whole secrets workflow seamless.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages