teriac server Dashboard and API

Dashboard and API

The teriac server dashboard: runs with their risk, locks with unlocking, drift, the Guardrails page, backups, audit log and policy findings; sign-in with OIDC, passwords or a token; the REST API and Prometheus metrics.

Open the server's URL in a browser.

The demo ends in the dashboard: runs, date ranges, metrics, resource usage and unlocking.

What it shows

  • an overview of runs, failures, locks and drift, over a date range you pick, with a banner while a change freeze is on;
  • every plan and apply, with its risk level, change list, redacted log and resource usage; a run shows why it got its risk level, and a banner when the secret scan hid something;
  • locks, with an Unlock button;
  • drift schedules, findings, who last changed each resource, and a link to the fix PR when fix_pr opened one;
  • the audit log, including Audit → Policy findings;
  • Guardrails: everything that can stop an apply, in one place (see below);
  • Access: emergency access grants (see Emergency access);
  • Metrics: runs, timings and resource use, plus delivery tiles: PR to applied, failed applies, time to fix, applies per week.

It has light and dark themes and loads nothing from third parties.

Role Can
Viewer See everything
Admin Also release locks, start drift checks, start a backup and revoke emergency access
Grantor Give and revoke emergency access on the Access page: people listed in break_glass.grantors

Guardrails

New in 0.2.1.

The Guardrails page shows the safety settings the server is running with, so nobody has to read repos.yaml to know why an apply was refused:

  • change freezes, and whether one is on right now (and until when);
  • protected resources, and who may override them;
  • risk settings: production globs, size limits and extra approvals;
  • policy and secret scan settings;
  • each repo entry's approval settings (reapprove_on_change, stale_base, verify_plan, …);
  • database backups: where they go, the next one, the latest results, and Back up now for admins (see Backups).

Sign-in

Sign-in Settings
Okta or any OIDC provider TERIAC_OIDC_ISSUER, TERIAC_OIDC_CLIENT_ID, TERIAC_OIDC_CLIENT_SECRET, TERIAC_OIDC_ADMIN_GROUPS, TERIAC_OIDC_ALLOWED_GROUPS. Redirect URI: <public url>/auth/oidc/callback
Username and password dashboard.users in repos.yaml, or TERIAC_DASHBOARD_USERS_FILE
API token TERIAC_API_TOKEN (admin)

OIDC (Okta, Entra ID, Google, Keycloak, …)

  1. Create a web application in your identity provider.
  2. Set the redirect URI to https://teriac.acme.dev/auth/oidc/callback.
  3. Make sure the ID token includes a groups claim.
  4. Configure teriac:
TERIAC_PUBLIC_URL=https://teriac.acme.dev
TERIAC_OIDC_ISSUER=https://acme.okta.com/oauth2/default
TERIAC_OIDC_CLIENT_ID=0oa1b2c3d4
TERIAC_OIDC_CLIENT_SECRET_FILE=/run/secrets/oidc
TERIAC_OIDC_ADMIN_GROUPS=platform-admins
TERIAC_OIDC_ALLOWED_GROUPS=engineering,sre
Setting Default
TERIAC_OIDC_ADMIN_GROUPS Groups that become admins
TERIAC_OIDC_ALLOWED_GROUPS Groups that become viewers. Everyone else is refused
TERIAC_OIDC_SCOPES openid,profile,email,groups Scopes to request
TERIAC_OIDC_GROUPS_CLAIM groups Claim that holds the groups
TERIAC_OIDC_NAME Okta for Okta issuers, else single sign-on Text on the sign-in button

Username and password

Make a password hash:

node packages/server/src/hash-password.ts

(source). Or from the image:

docker run --rm -it ghcr.io/marcdomain/teriac-server:0.2.1 node packages/server/src/hash-password.ts

Then list users in repos.yaml, or in a separate file named by TERIAC_DASHBOARD_USERS_FILE:

dashboard:
  users:
    - { username: alice, password_hash: "scrypt$…", role: admin, name: Alice Martin }
    - { username: bob,   password_hash: "scrypt$…", role: viewer }

With Helm

Method Values Roles
Okta / any OIDC provider dashboard.oidc.* adminGroups → admin, allowedGroups → viewer, everyone else is refused
Username and password dashboard.users or usersExistingSecret role: admin or viewer per user
API token dashboard.apiToken.existingSecret Admin. For automation and /api

Sessions

Sessions are signed, HttpOnly cookies (also Secure when the public URL is https), and last TERIAC_SESSION_HOURS (12 by default). Set TERIAC_SESSION_SECRET (32+ characters) or sessions end when the server restarts; the Helm chart generates one and keeps it across upgrades. Login attempts are rate limited.

API

These need a dashboard session or Authorization: Bearer $TERIAC_API_TOKEN:

Request What it does
GET /api/runs Plans and applies
GET /api/locks Current locks
GET /api/audit Audit log
GET /api/drift Drift findings
GET /api/drift/schedules Drift schedules, with the key for each
GET /api/projects Projects teriac knows about
GET /api/stats Numbers for the overview
GET /api/me Who you're signed in as
POST /api/drift/run?key=… Start a drift check (admin)
POST /api/locks/unlock Release a lock (admin)
GET /api/grants Emergency access grants.
POST /api/grants Give emergency access (grantors).
POST /api/grants/<id>/revoke Revoke a grant (admins and grantors).
GET /api/safety The Guardrails page: freezes, protected resources, risk, policies, repo settings
GET /api/backups Backup settings and recent results
POST /api/backups/run Start a backup now (admin)
export TERIAC_API_TOKEN=…
curl -H "Authorization: Bearer $TERIAC_API_TOKEN" https://teriac.acme.dev/api/locks
curl -H "Authorization: Bearer $TERIAC_API_TOKEN" https://teriac.acme.dev/api/drift/schedules
curl -X POST -H "Authorization: Bearer $TERIAC_API_TOKEN" \
  "https://teriac.acme.dev/api/drift/run?key=<key from /api/drift/schedules>"

Health and metrics

These are open, with no sign-in:

Path
/healthz The process is up (used by the image's HEALTHCHECK)
/readyz Ready to take webhooks
/metrics Prometheus metrics

Metrics include teriac_webhooks_total (by platform and kind), teriac_runs_total (by kind and status), and, with backups on, teriac_backup_last_ok and teriac_backup_last_success_timestamp_seconds. With Helm, set serviceMonitor.enabled: true to scrape /metrics.

# Failed applies in the last day
sum(increase(teriac_runs_total{kind="apply", status="failed"}[1d]))