Configuration files

Keep 3AM's connections and policy in files, for GitOps or Ansible, instead of setting them in the console. And how to reference secrets.

You never need configuration files: the console sets everything. But if you manage infrastructure as code, you can keep connections and policy in files, review them, and roll them out like anything else.

Put the files in one folder and give it to 3AM, read-only:

  • On a server: ./install.sh --config /path/to/folder
  • On Kubernetes: a ConfigMap, with config.existingConfigMap. The licence and packs go in Secrets (licence.existingSecret, packs.existingSecret).
FileWhat it holds
connectors.jsonThe tools 3AM connects to
policy.jsonAutonomy, approval rules, budgets
estate-overrides.jsonCorrections to the services 3AM discovered
licence.jsonThe licence

Anything defined in a file wins over the console. The console shows it as managed in a file, and won't let anyone edit it there.

connectors.json

{
  "connectors": [
    {"name": "prometheus", "kind": "prometheus",
     "settings": {"url": "https://prometheus.example.internal:9090",
                  "alertmanager_url": "https://alertmanager.example.internal:9093",
                  "bearer_token": {"file": "/etc/3am/secrets/prometheus/token"}}},
    {"name": "pagerduty", "kind": "pagerduty",
     "settings": {"api_token": {"env": "PD_API_TOKEN"}, "from_email": "3am@example.com",
                  "watch_services": ["PXXXXXX"]}}
  ]
}

Each connector's page lists its settings.

Secrets are references

A secret is never written into the file. It points to where the secret is:

FormReads the secret from
{"file": "/etc/3am/secrets/…"}A file, such as a mounted Kubernetes Secret
{"env": "NAME"}An environment variable of the 3AM container

3AM refuses to start if it finds a secret written inline, because it would end up in backups, diffs and support bundles.

policy.json

{
  "default_autonomy": "shadow",
  "approval_chain": [{"channel": "slack"}, {"channel": "pagerduty", "after_s": 300}],
  "rules": [
    {"match": {"action": "mysql.start"}, "deny": true, "reason": "database restarts go through the DBA team"},
    {"match": {"risk": "high"}, "quorum": 2, "hours": "07:00-22:00"}
  ],
  "budgets": {"per_service_per_hour": 3, "global_per_hour": 10}
}
KeyWhat it does
default_autonomyWhere new services start: shadow (recommended) or off. Raising a service to L1 or L2 always takes two approvers in the console, never a file
approval_chainWhere to ask, in order. after_s is how long to wait before moving on to the next channel
rulesChecked in order; the first match wins. Match on the action, its risk, the service or the environment, then deny it, require more approvers, or limit the hours
budgetsThe most fixes per service per hour, and in total

The rest of the policy (per-service approvers, business impact, the limits for L2) is explained in Safety rails.

estate-overrides.json

When discovery links something wrongly, correct it here. Overrides always win:

{"services": {"payments": {"repos": ["example/payments-service"], "owner": "ana@example.com", "team": "Payments"}}}

The Estate page in the console writes the same corrections.

On this page