The KRI

A Key Risk Indicator has:

  • A slug — the stable contract between connector and KRI (e.g. critical_cves_unpatched_gt_30d).
  • A name and description for humans.
  • A source — the kri_source that writes values.
  • Thresholds (warn, critical) and a direction (higher-is-worse / lower-is-worse).
  • Links to one or more risks and a business outcome.

A KRI without links to risks and outcomes is valid but useless — it shows up on the dashboard but doesn’t roll up. The onboarding flow won’t let you finish until each KRI is wired.

The source

A kri_source is a per-tenant connection to an integration. It stores:

  • Source type + vendor ID — the pair that resolves to a connector.
  • API endpoint — the vendor URL (auto-filled for fixed-URL vendors).
  • Auth type and encrypted credentials.
  • Scheduledaily (default), hourly, or manual.
  • Extra config — non-secret JSON (e.g. {"customer_id":"abc"} for Google Workspace).

How the runner writes values

  1. The scheduler wakes up and resolves every source whose next-run time has passed.
  2. For each source, the dispatcher looks up the connector by vendor_id and calls run(ctx).
  3. The connector fetches data from the vendor, computes each KRI value, and runs UPDATE kri SET value = ?, updated_at = ? WHERE source_id = ? AND id = ?.
  4. If a KRI slug is missing, the row is counted as rowsSkipped — it means the tenant hasn’t wired that KRI yet. Not an error.
  5. The run row is written with rowsWritten, rowsSkipped, and a summary of items fetched.

Rollups

Dashboards compute three levels of rollup:

  • Per-KRI colour (green / amber / red) from its thresholds.
  • Per-risk severity from the KRIs and controls linked to that risk.
  • Per-outcome severity from the risks linked to the outcome.

See Platform overview for how KRIs, risks, controls, and outcomes relate.

Manual KRIs

Not every KRI needs an integration. For values that only a human knows (e.g. “has the quarterly board risk review happened?”), create the KRI without a source and edit its value by hand. The updated_at ages the same way as an automated value, so stale manual values show up in the Draxis staleness report.

API

  • GET /api/kri-sources — list sources.
  • PUT /api/kri-sources/<id> — create / update.
  • POST /api/kri-sources/<id>/test — probe connectivity.
  • POST /api/kri-sources/<id>/run — trigger a run now.
  • GET /api/kri-sources/<id>/runs — run history.
  • GET /api/kris, PUT /api/kris/<id> — KRI CRUD.