sansaagent

Grounding

If it is not in the bundle,

Trust in this product is not a brand color. It is path allowlists, claim overlap, outbound URL policy, and a review state machine. Generated language that cannot point at evidence stays in the report with a reason — never promoted to a fact.

Path allowlists

Reads are limited to data, configs, artifacts, and eval. Writes stay in artifacts. Traversal and absolute escapes are rejected before the pipeline starts.

Claim citations

Each sentence is scored against evidence overlap. Unsupported claims stay visible with a reason — they are not deleted in shame, and they are not promoted to facts.

Outbound policy

Prometheus hosts and webhook URLs are allowlisted. Private networks and plain HTTP require an explicit opt-in. Failed deliveries are still logged.

No local model

The workstation does not host weights. Heuristic compose is deterministic. OpenAI is optional and keyed only via INCIDENT_AGENT_OPENAI_API_KEY.

Review workflow

Draft → reviewed → approved | rejected. Every transition stores reviewer, note, and timestamp. Webhooks only fire for approved reports.

Credential hygiene

.env is gitignored. .env.example never carries secret values. The generic OPENAI_API_KEY variable is ignored so a stray shell export cannot silently activate a provider.

Threat model, local edition

The API is for a trusted workstation. We still refuse the usual foot-guns.

What we assume

An operator on the same machine, or a trusted network. There is no end-user auth. Do not expose :8000 to the public internet.

What we still block

Path traversal, writes outside artifacts, webhook URLs that are not exact allowlist matches, Prometheus hosts that were never opted in.

What we record

Request ids, stage durations, grounding summaries, review history, webhook delivery attempts. If it happened, it is in JSON.

Grounding policy

warn by default, fail when you are ready

configs/default.yaml sets grounding.enabled and grounding.policy. Policy warn keeps unsupported claims and flags them. Policy fail drops the report if overlap is below minimum_support_overlap (0.34 by default). Use warn while you are tuning detectors. Use fail before a report is allowed to leave through a webhook.

supported

Overlap with an evidence id or hypothesis rationale above minimum_support_overlap (0.34).

unsupported

Fluent sentence, no bundle overlap. Stays in the report with a reason.

contradictory

Reserved for claims that fight detector output. Policy fail can drop the report.

not_applicable

Remediations and advice. They are not facts about the past.

Review machine

Four states. One of them may leave the building.

  1. draft

    Default after compose. The machine wrote it. A human has not looked.

  2. reviewed

    A named reviewer looked and left a note. Still not allowed to leave the building.

  3. approved

    Owner agrees. The only state that may POST to a webhook.

  4. rejected

    Graph or copy was wrong. JSON stays. The note is how the next run is not gaslit.

Allowlists

Reads, writes, hosts, and URLs are named. Everything else is refused.

Read

  • data/
  • configs/
  • artifacts/
  • eval/

Traversal and absolute escapes are rejected before the pipeline starts. Bring logs from inside those trees.

Write & outbound

  • artifacts/ only
  • webhook allowed_urls exact match
  • Prometheus allowed_hosts
  • HTTP / private nets default off

Failed deliveries still append JSONL. A missing reviewer name is a 400, not a silent approve.

What we do not store

No phone-home. No weights. No stray OPENAI_API_KEY.

Trusted workstation

No end-user auth. Path allowlists and URL policy are foot-gun guards, not a multi-tenant security model.

Jobs are memory

Restarting uvicorn clears the board. Artifacts remain. Re-run a scenario to refill incidents and reports.

Heuristic compose

Default narrative is assembled from evidence JSON. It is precise, not lyrical. OpenAI is opt-in on top of the same contracts.

Graph is yours

Wrong edges produce wrong origins. Reject the report and fix configs/service_dependencies.yaml — do not edit facts.

Credential hygiene: .env is gitignored. .env.example never carries secret values. Only INCIDENT_AGENT_OPENAI_API_KEY can activate the optional rewriter. A generic OPENAI_API_KEY export in the shell is ignored on purpose.