the stack file

A stack is a single-document YAML file with an apiVersion and kind so the schema can evolve without breaking older files. pngr.dev/v1 is the only version today.

complete example

apiVersion: pngr.dev/v1
kind: Stack
metadata:
  org: acme                          # org slug — must match the auth context's org
spec:
  channels:
    - name: ops-alerts               # name is the upsert key; unique per (org, kind)
      type: slack
      enabled: true
      config:
        webhook_url: "${SLACK_OPS_WEBHOOK}"   # ${VAR} expanded from env at apply time
    - name: oncall-email
      type: email
      config:
        recipients: ["[email protected]"]

  monitors:
    - name: marketing-site
      url: https://acme.dev
      check_type: http
      interval_seconds: 60
      timeout_seconds: 10
      expected_status_codes: [200, 301, 302]
      tags: [public, marketing]
    - name: api
      url: https://api.acme.dev/healthz
      check_type: http
      interval_seconds: 30
      expected_body_substring: "ok"

  rules:
    - name: prod-pager
      trigger: monitor_down
      failure_threshold: 2
      renotify_minutes: 30
      applies_to:
        monitors: [marketing-site, api]    # by name; backend resolves to ids
      escalations:
        - level: 1
          channels: [ops-alerts]
        - level: 2
          delay_minutes: 10
          channels: [oncall-email]

field conventions

  • name is the upsert key for every resource — (org, kind, name) is unique. Renaming a resource in the file is treated as delete + create unless you set an explicit previous_name.
  • Cross-references use name, never id. A rule's applies_to.monitors and channels lists reference resources by name; the apply pipeline resolves names to ids server-side. If a referenced name doesn't exist in the same file, apply fails with one readable error listing every unresolved name.
  • Fields mirror the REST API (snake_case, to match the API reference), so a monitors[] block round-trips to and from the monitors endpoint.

secrets

Credential fields support ${ENV_VAR} interpolation, expanded at apply time:

config:
  webhook_url: "${SLACK_OPS_WEBHOOK}"
note

The unrendered file is safe to commit — interpolation happens in CI with secrets injected from your secret store. A dump never writes secrets in plaintext; it emits ${SECRET_NAME} placeholders derived from the channel name and field, which you wire up to real values on your side.

warning

One file, one org. A stack file targets a single org (metadata.org). Multi-org files aren't supported. Templating beyond ${ENV_VAR} is out of scope too — no Go templates, Jsonnet, or Helm-style values. If you need that, generate the YAML upstream and feed the result to apply.

next