Olympus Docs
CookbookIntegrations & billing

Policy files in git (config-as-code)

Treat Olympus config like code

Olympus has many config files: kratos.yml, hydra.yml, Caddyfile, identity schemas, policies (Cedar / OPA), email templates. Treat them as code in a git repo.

Repo layout

olympus-config/
├── kratos.yml
├── hydra.yml
├── identity-schemas/
│   ├── default.schema.json
│   └── customer-x.schema.json
├── courier-templates/
│   └── ...
├── policies/
│   ├── cedar/
│   │   └── orders.cedar
│   └── opa/
│       └── billing.rego
├── caddy/
│   └── Caddyfile
└── .github/workflows/
    └── deploy.yml

Version-controlled. Reviewable. Reproducible.

Validation

CI checks each commit:

# .github/workflows/validate.yml
name: Validate config
on: [push]
jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Validate Kratos config
        run: docker run -v $PWD:/config oryd/kratos:v1.4.0 kratos validate config /config/kratos.yml
      
      - name: Validate Hydra config
        run: docker run -v $PWD:/config oryd/hydra:v2.2.0 hydra validate /config/hydra.yml
      
      - name: Validate identity schemas
        run: |
          for schema in identity-schemas/*.json; do
            ajv validate -s "$schema" -d test-data/sample.json
          done
      
      - name: Lint Cedar policies
        run: |
          for p in policies/cedar/*.cedar; do
            cedar lint --policies "$p"
          done
      
      - name: Caddy syntax check
        run: docker run -v $PWD:/config caddy:2 caddy validate --config /config/caddy/Caddyfile

Catch errors before they hit prod.

Deploy

On merge to main:

- name: Deploy config
  run: |
    rsync -av config/ deploy@$HOST:/opt/olympus/config/
    ssh deploy@$HOST 'cd /opt/olympus && podman-compose restart'

Or with finer control:

- name: Deploy and reload
  run: |
    rsync -av config/kratos.yml deploy@$HOST:/opt/olympus/config/
    ssh deploy@$HOST 'podman-compose restart ciam-kratos'

Only restarts service whose config changed.

Diff-based deploys

For multiple files, only deploy changed:

- name: Detect changes
  run: |
    changed_files=$(git diff --name-only HEAD~1)
    if echo "$changed_files" | grep -q "kratos.yml"; then
      KRATOS_CHANGED=1
    fi
    # ... per file

- if: env.KRATOS_CHANGED == '1'
  run: ssh deploy "cd /opt/olympus && podman-compose restart ciam-kratos"

Faster, less disruptive.

Secret separation

Don't commit secrets. Use templating:

# kratos.yml.template
secrets:
  cookie:
    - ${KRATOS_SECRETS_COOKIE}
  cipher:
    - ${KRATOS_SECRETS_CIPHER}

At deploy time:

envsubst < kratos.yml.template > kratos.yml

Or use Helm-like tools (kustomize, jsonnet, etc.).

For secret storage:

  • Vault.
  • AWS Secrets Manager.
  • Doppler.
# Pull secrets at deploy
doppler run -- envsubst < kratos.yml.template > kratos.yml

Environment branches

Different config per env:

git branches:
  main → prod
  staging → staging
  dev → dev

Merging: dev → staging → main (promotion).

Or files:

config/
├── base/
├── dev/
├── staging/
└── prod/

Build script picks based on --env.

Review workflow

Pull request for every change:

1. Engineer makes config change.
2. PR opened.
3. CI validates (Kratos / Hydra accepts the config).
4. Reviewer approves.
5. Merge → deploy.

Bad config blocked at CI. Don't deploy then debug.

Per-tenant config

For multi-tenant white-label, config per tenant:

config/
├── tenants/
│   ├── acme/
│   │   ├── branding.json
│   │   └── identity-schema.json
│   └── bigcorp/
│       └── ...

Deploy specific tenant:

./scripts/deploy-tenant.sh acme

Updates only Acme's pocket. Doesn't disturb others.

Versioning configs

Tag releases:

git tag config-v1.4.0 -m "Production config v1.4.0"
git push --tags

Rollback to tag:

git checkout config-v1.3.5
./scripts/deploy.sh

Instant revert to known-good.

Drift detection

Periodically check that deployed config matches git:

# Cron daily
diff <(ssh prod cat /opt/olympus/config/kratos.yml) ./config/kratos.yml

If different: someone made a manual change. Investigate.

Or use Atlantis / similar for "are we in sync?"

Multiple environments tested

Each env runs CI:

deploy_dev:
  needs: validate
  steps: [...]

deploy_staging:
  needs: deploy_dev
  steps: [...]

deploy_prod:
  needs: deploy_staging
  environment: prod-approval  # manual gate
  steps: [...]

Promotion through environments. Each stable for hours/days before next.

Audit

Git history is the audit:

git log --follow kratos.yml
# Each change traceable.

Pair with deploy logs:

INSERT INTO config_deploys (env, commit_sha, files_changed, deployed_at, deployed_by)
VALUES ('prod', 'abc123', ['kratos.yml'], NOW(), 'github-actions');

Trace "when was MFA enforced for admins?" → audit log + git.

Failures

If deploy fails partway:

- name: Deploy
  run: ssh prod "..."

- if: failure()
  run: |
    ssh prod "cd /opt/olympus && git checkout HEAD~1 && podman-compose restart"
    slack-notify "Deploy failed, reverted"

Auto-revert on failure. Investigate, fix, redeploy.

Documentation in repo

Each non-trivial change → comment in commit or separate doc:

# decisions/2026-05-13-enable-passkey.md

Decision: enable WebAuthn / passkeys for all users.

Why:
- Phishing-resistant.
- Reduces password fatigue.

Trade-offs:
- Some users won't enroll (free choice).
- Older browsers don't support.

Mitigation: keep password as fallback.

Approver: Bobby
Date: 2026-05-13

Decision log alongside code.

On this page