📖  Documentation

Deploy, connect & operate InsightResolve

InsightResolve runs inside your environment — your cloud, your alert sources, your model. Start on a laptop with the demo stack, point real webhooks at it, then take the same containers to production. This is how it fits together.

InsightResolve is three containers and a database: the API, the console, and the InsightWorker engine that executes runbook steps. Alerts arrive as webhooks; everything the agent learns is written to Postgres and shown live in the console.

Architecture at a glance

Three tiers, the same shape as every Insight product: a React console, a FastAPI service, and the InsightWorker agent engine. State lives in Postgres; the console gets live updates over SSE.

flowchart TB PD["PagerDuty"] -- webhook --> API AM["Alertmanager"] -- webhook --> API GF["Grafana"] -- webhook --> API CU["Custom source"] -- webhook --> API U["On-call · SRE
(console)"] -- REST + SSE --> API API["InsightResolve API
webhook router · matcher · runbook engine · incident manager · SSE bus"] API --> DB[("Postgres
schema: resolve")] API -- dispatch --> WK["InsightWorker
(agent engine · skills)"] WK --> PROM["Prometheus · Grafana · Alertmanager"] WK --> K8S["Kubernetes · Airflow · dbt · SQL"] WK --> OUT["PagerDuty · Jira · Slack · GitHub"] WK --> LLM["Model provider
Bedrock · Azure OpenAI · Vertex · OpenAI-compatible"] API --> IDP["Identity provider
OIDC · local"]

The API and the worker are stateless. Scale them by adding replicas. Incidents, executions, step outputs, RCAs and every runbook version live in Postgres, which you run as a managed service with backups.

Components

ComponentWhat it isScales byState
ConsoleStatic React app (nginx) — dashboard, incidents, runbook editor, users & access, integrationsReplicas behind the LB / CDNStateless
APIFastAPI — webhooks, incidents, runbooks, executions, identity, connection registryHorizontal replicasStateless
InsightWorkerThe agent engine that executes runbook steps with the operations skillsAutoscale on queued executionsStateless
PostgresIncidents, executions, runbooks & versions, RCAs, metrics, identity, connectionsVertical + replicasDurable
Model providerYour chosen endpoint for the ai.* stepsProvider concurrencyExternal
Identity providerOkta, Entra ID, Google or any OIDC issuer; local accounts for a labManagedExternal

Demo stack — Docker Compose

The demo brings up everything the loop needs on one machine. PagerDuty is SaaS, so a stand-in with the same three surfaces (Events API in, incident REST out, webhook v3 events) takes its place.

git clone https://github.com/verticalserve/insightresolve-api
cd insightresolve-api/demo
cp .env.example .env
docker compose up -d --build        # ~3 minutes the first time
./demo.sh errors                    # api-gateway fails 30% of requests
./demo.sh payment-latency           # payment-service slows down
./demo.sh recover                   # sources send resolved; incidents close with MTTR
WhatWhere
InsightResolve consolehttp://localhost:5218admin@demo.local / the password in .env
API & OpenAPIhttp://localhost:8118/docs
Grafanahttp://localhost:3000/d/resolve-demo
PagerDuty (mock) · Alertmanager · Prometheus:8085 · :9093 · :9090

Within a minute of ./demo.sh errors, Prometheus fires HighErrorRate, Alertmanager routes it to PagerDuty, PagerDuty webhooks InsightResolve, and the incident appears matched to API Gateway High Error Rate Triage with its execution queued.

The incident loop

  • Receive. A webhook is normalized into one NormalizedAlert: source, external id, alert name, title, severity, service, labels, URL, start time, resolved flag.
  • Deduplicate. A fingerprint of source, alert name, service and key labels (env, namespace, cluster, region, instance, pod, host, dag_id, job). A repeat inside the window folds into the open incident; a resolved event closes it and records MTTR.
  • Match. Every active rule of every active runbook is scored (see matching). No match → an ai_triage execution.
  • Execute. An execution pins the runbook version and creates one row per step. InsightWorker runs them; each output is saved as evidence and becomes a variable.
  • Resolve. The classification step produces category, confidence and reasoning; the gate step resolves the page or escalates with a note; the RCA artifact is written.
  • Learn. Effectiveness and average resolution per runbook, per-incident metrics, and detected patterns with suggested actions.

Webhooks & sources

Four public endpoints. When RESOLVE_WEBHOOK_TOKEN is set, callers present it as ?token= or X-Resolve-Webhook-Token. A connection query parameter names the registered connection the sender belongs to.

EndpointAcceptsNotes
POST /api/webhooks/pagerdutyWebhook v3 events (incident.triggered, .acknowledged, .resolved)Custom details become labels, so the Prometheus labels ride through PagerDuty
POST /api/webhooks/alertmanagerAlertmanager webhook groupsOne incident per alert; status: resolved closes it
POST /api/webhooks/grafanaGrafana unified alerting webhookPanel and dashboard URLs kept on the incident
POST /api/webhooks/generic{alert_name, title, severity, service, labels, external_id, url, status}A list, or {alerts: [...]}, is several alerts

Runbook model

A runbook is normalized into rows, not stored as one JSON document. That is what lets the console reorder a step with one update, two people edit different steps at once, and a version diff say “changed threshold in step 3”.

TableHolds
runbookname, slug, category, team, status (draft · active · archived), severity filter, tags, effectiveness, times executed, current version
runbook_stepordered steps: action type, parameters (JSONB), save_as, timeout, retries, continue-on-fail
runbook_step_conditionrun_if · skip_if · branch with an expression and, for branch, a target step
runbook_variable{{name}} → expression or literal, with a default
runbook_match_rulealert-name and service globs, severity set, label containment, source, expression, priority
runbook_versionan immutable snapshot of all of the above, with a change note and an auto-generated diff summary

Variables resolve in order: incident context ({{incident.labels.service}}), then runbook variables, then the outputs of earlier steps ({{error_rate}}, {{classification.category}}).

Step action types

FamilyActions
PagerDutypagerduty.get_incident · acknowledge · resolve · add_note
Prometheus & Alertmanagerprometheus.instant · range · alerts · alertmanager.alerts · silence
Grafanagrafana.dashboard · annotate
Kubernetesk8s.pods · logs · events
Data pipelinesairflow.dag_status · task_logs · trigger · dbt.test_results · source_freshness
Data & ticketspostgres.query · jira.create · jira.search · slack.post · http.request · bash.exec
AIai.classify · ai.summarize · ai.decide
Logicgate · assign · wait · notify · loop · parallel · human.approve

Matching & conditions

A rule scores one point for each field it sets that matches the alert; a field it sets that does not match disqualifies it. The best score wins; ties go to rule priority, then to the runbook’s effectiveness. Test any rule from the console against a past incident or a mock payload.

{{variable}} == 'value'        {{variable}} != 'value'
{{variable}} > 5.0             {{variable}} contains 'OOMKilled'
{{variable}} in ['critical', 'high']
{{variable}} matches 'error.*timeout'

Configuration

VariableMeaning
INSIGHT_DATABASE_URL / _SECRET / _SCHEMAPostgres without a password in the URL; the password is a secret reference (AWS SM, Key Vault, GCP SM, OCI Vault, a mounted file). Schema resolve.
INSIGHT_JWT_KEY_REFThe token-signing key pair, as a secret reference. One pair across Insight products gives single sign-on.
INSIGHT_LLM_PROVIDER · INSIGHT_CHAT_MODELbedrock · azure_openai · vertex · oci · openai (any OpenAI-compatible endpoint)
RESOLVE_WEBHOOK_TOKENShared token webhook senders must present
DEDUPE_WINDOW_MINUTESHow long a repeat folds into the open incident (60)
WORKER_URL / WORKER_CLIWhere InsightWorker is

Identity, roles & API keys

Sign-in is local or any OIDC provider (Okta, Entra ID, Google). Group-to-role rules map directory groups onto roles at every sign-in. admin holds every role; curator edits runbooks; every signed-in person reads and acts on incidents. API keys are created under Users & access, shown once, and carry scopes as roles. Every change is in the audit trail.

API

Every response is the envelope {"status": "SUCCESS" | "FAIL", "data": ..., "error_message": ...}. Authenticate with a bearer token from POST /api/auth/login or an API key. The full OpenAPI document is at /docs on your instance.

POST /api/auth/login                        {"username": "...", "password": "..."}
GET  /api/dashboard
GET  /api/incidents?status=open&severity=critical
GET  /api/incidents/{id}                    executions, steps, RCA
POST /api/incidents/{id}/retry              match again, open a fresh execution
POST /api/incidents/{id}/escalate
GET  /api/runbooks   POST /api/runbooks     PUT/DELETE /api/runbooks/{id}
POST /api/runbooks/{id}/activate  /clone    POST /api/runbooks/from-template/{tid}
GET/POST /api/runbooks/{id}/steps|rules|variables|versions
POST /api/runbooks/{id}/steps/reorder       POST /api/runbooks/{id}/rules/test
POST /api/runbooks/match                    which active runbook takes this alert
GET  /api/user/getAll  /roles  /providers  /api-keys
GET  /api/connections  /connectors

Want a walkthrough on your stack? Talk to us — we’ll connect a demo instance to your alert sources and port two of your runbooks live.