This commit is contained in:
StellaOps Bot
2025-12-13 02:22:15 +02:00
parent 564df71bfb
commit 999e26a48e
395 changed files with 25045 additions and 2224 deletions

View File

@@ -1,66 +1,63 @@
# Signals Reachability API Contract (draft placeholder)
# Signals API (Reachability)
**Status:** Draft v0.2 · owner-proposed
**Status:** Working contract (aligns with `src/Signals/StellaOps.Signals/Program.cs`).
## Scope
- `/signals/callgraphs`, `/signals/facts`, reachability scoring overlays feeding UI/Web.
- Deterministic fixtures for SIG-26 chain (columns/badges, call paths, timelines, overlays, coverage).
## 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: <space-separated scopes>` (example: `X-Scopes: signals:write`).
- **Sealed mode:** when enabled, Signals may return `503` with `{ "error": "sealed-mode evidence invalid", ... }`.
## Endpoints
- `GET /signals/callgraphs` — returns call paths contributing to reachability.
- `GET /signals/facts` — returns reachability/coverage facts.
Common headers: `Authorization: DPoP <token>`, `DPoP: <proof>`, `X-StellaOps-Tenant`, optional `If-None-Match`.
Pagination: cursor via `pageToken`; default 50, max 200.
ETag: required on responses; clients must send `If-None-Match` for cache validation.
### Health & status
### Callgraphs response (draft)
```jsonc
{
"tenantId": "tenant-default",
"assetId": "registry.local/library/app@sha256:abc123",
"paths": [
{
"id": "path-1",
"source": "api-gateway",
"target": "jwt-auth-service",
"hops": [
{ "service": "api-gateway", "endpoint": "/login", "timestamp": "2025-12-05T10:00:00Z" },
{ "service": "jwt-auth-service", "endpoint": "/verify", "timestamp": "2025-12-05T10:00:01Z" }
],
"evidence": { "traceId": "trace-abc", "spanCount": 2, "score": 0.92 }
}
],
"pagination": { "nextPageToken": null },
"etag": "sig-callgraphs-etag"
}
```
- `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`)
### Facts response (draft)
```jsonc
{
"tenantId": "tenant-default",
"facts": [
{
"id": "fact-1",
"type": "reachability",
"assetId": "registry.local/library/app@sha256:abc123",
"component": "pkg:npm/jsonwebtoken@9.0.2",
"status": "reachable",
"confidence": 0.88,
"observedAt": "2025-12-05T10:10:00Z",
"signalsVersion": "signals-2025.310.1"
}
],
"pagination": { "nextPageToken": "..." },
"etag": "sig-facts-etag"
}
```
### Callgraph ingestion & retrieval
### Samples
- Callgraphs: `docs/api/signals/samples/callgraph-sample.json`
- Facts: `docs/api/signals/samples/facts-sample.json`
- `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`)
### Outstanding
- Finalize score model, accepted `type` values, and max page size.
- Provide OpenAPI/JSON schema and error codes.
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`)