# Signals API (Reachability) **Status:** Working contract (aligns with `src/Signals/StellaOps.Signals/Program.cs`). ## Auth, scopes, sealed mode - **Scopes:** `signals:read`, `signals:write`, `signals:admin` (endpoint-specific; see below). - **Dev fallback:** when Authority auth is disabled, requests must include `X-Scopes: ` (example: `X-Scopes: signals:write`). - **Sealed mode:** when enabled, Signals may return `503` with `{ "error": "sealed-mode evidence invalid", ... }`. ## Endpoints ### Health & status - `GET /healthz` (anonymous) - `GET /readyz` (anonymous; `503` when not ready or sealed-mode blocked) - `GET /signals/ping` (scope: `signals:read`, response: `204`) - `GET /signals/status` (scope: `signals:read`) ### Callgraph ingestion & retrieval - `POST /signals/callgraphs` (scope: `signals:write`) - Body: `CallgraphIngestRequest` (`language`, `component`, `version`, `artifactContentBase64`, …). - Response: `202 Accepted` with `CallgraphIngestResponse` and `Location: /signals/callgraphs/{callgraphId}`. - Graph hash is computed deterministically from normalized nodes/edges/roots; see `graphHash` in the response. - `GET /signals/callgraphs/{callgraphId}` (scope: `signals:read`) - `GET /signals/callgraphs/{callgraphId}/manifest` (scope: `signals:read`) Sample request: `docs/api/signals/samples/callgraph-sample.json` ### Runtime facts ingestion - `POST /signals/runtime-facts` (scope: `signals:write`) - Body: `RuntimeFactsIngestRequest` with `subject`, `callgraphId`, and `events[]`. - `POST /signals/runtime-facts/ndjson?callgraphId=...&scanId=...` (scope: `signals:write`) - Body: NDJSON of `RuntimeFactEvent` objects; `Content-Encoding: gzip` supported. - `POST /signals/runtime-facts/synthetic` (scope: `signals:write`) - Generates a small deterministic sample set of runtime events for a callgraph to unblock testing. ### Unknowns ingestion & retrieval - `POST /signals/unknowns` (scope: `signals:write`) - Body: `UnknownsIngestRequest` (`subject`, `callgraphId`, `unknowns[]`). - `GET /signals/unknowns/{subjectKey}` (scope: `signals:read`) ### Reachability scoring & facts - `POST /signals/reachability/recompute` (scope: `signals:admin`) - Body: `ReachabilityRecomputeRequest` (`callgraphId`, `subject`, `entryPoints[]`, `targets[]`, optional `runtimeHits[]`, optional `blockedEdges[]`). - Response: `200 OK` with `{ id, callgraphId, subject, entryPoints, states, computedAt }`. - `GET /signals/facts/{subjectKey}` (scope: `signals:read`) - Response: `ReachabilityFactDocument` (per-target states, `score`, `riskScore`, unknowns pressure, optional uncertainty states, runtime facts snapshot). Sample fact: `docs/api/signals/samples/facts-sample.json` ### Reachability union bundle ingestion (CAS layout) - `POST /signals/reachability/union` (scope: `signals:write`) - Body: `application/zip` bundle containing `nodes.ndjson`, `edges.ndjson`, `meta.json`. - Optional header: `X-Analysis-Id` (defaults to a new GUID if omitted). - Response: `202 Accepted` with `ReachabilityUnionIngestResponse` and `Location: /signals/reachability/union/{analysisId}/meta`. - `GET /signals/reachability/union/{analysisId}/meta` (scope: `signals:read`) - `GET /signals/reachability/union/{analysisId}/files/{fileName}` (scope: `signals:read`)