up
This commit is contained in:
@@ -232,6 +232,17 @@ Slim wrapper used by CLI; returns 204 on success or `ERR_POL_001` payload.
|
||||
|
||||
> Schema reference: canonical policy run request/status/diff payloads ship with the Scheduler Models guide (`src/Scheduler/__Libraries/StellaOps.Scheduler.Models/docs/SCHED-MODELS-20-001-POLICY-RUNS.md`) and JSON fixtures under `samples/api/scheduler/policy-*.json`.
|
||||
|
||||
### 6.0 Reachability evidence inputs (Signals)
|
||||
|
||||
Policy Engine evaluations may be enriched with reachability facts produced by Signals. These facts are expected to be:
|
||||
|
||||
- **Deterministic:** referenced by `metadata.fact.digest` (sha256) and versioned via `metadata.fact.version`.
|
||||
- **Evidence-linked:** per-target states include `path[]` and `evidence.runtimeHits[]` (and any future CAS/DSSE pointers).
|
||||
|
||||
Signals contract & scoring model:
|
||||
- `docs/api/signals/reachability-contract.md`
|
||||
- `docs/reachability/lattice.md`
|
||||
|
||||
### 6.1 Trigger Run
|
||||
|
||||
```
|
||||
|
||||
@@ -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`)
|
||||
|
||||
@@ -1,23 +1,16 @@
|
||||
{
|
||||
"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
|
||||
"language": "java",
|
||||
"component": "pkg:maven/com.acme/demo-app@1.0.0?type=jar",
|
||||
"version": "1.0.0",
|
||||
"artifactContentType": "application/json",
|
||||
"artifactFileName": "callgraph.json",
|
||||
"artifactContentBase64": "eyJzY2hlbWFfdmVyc2lvbiI6IjEuMCIsInJvb3RzIjpbeyJpZCI6ImZ1bmM6amF2YTpjb20uYWNtZS5BcHAubWFpbiIsInBoYXNlIjoicnVudGltZSIsInNvdXJjZSI6InN0YXRpYyJ9XSwibm9kZXMiOlt7ImlkIjoiZnVuYzpqYXZhOmNvbS5hY21lLkFwcC5tYWluIiwibmFtZSI6Im1haW4iLCJraW5kIjoiZnVuY3Rpb24iLCJuYW1lc3BhY2UiOiJjb20uYWNtZSIsImZpbGUiOiJBcHAuamF2YSIsImxpbmUiOjEsImxhbmd1YWdlIjoiamF2YSJ9LHsiaWQiOiJmdW5jOmphdmE6Y29tLmFjbWUuQXV0aC52ZXJpZnkiLCJuYW1lIjoidmVyaWZ5Iiwia2luZCI6ImZ1bmN0aW9uIiwibmFtZXNwYWNlIjoiY29tLmFjbWUuYXV0aCIsImZpbGUiOiJBdXRoLmphdmEiLCJsaW5lIjo0MiwibGFuZ3VhZ2UiOiJqYXZhIn1dLCJlZGdlcyI6W3siZnJvbSI6ImZ1bmM6amF2YTpjb20uYWNtZS5BcHAubWFpbiIsInRvIjoiZnVuYzpqYXZhOmNvbS5hY21lLkF1dGgudmVyaWZ5Iiwia2luZCI6ImNhbGwiLCJjb25maWRlbmNlIjowLjl9XSwiYW5hbHl6ZXIiOnsibmFtZSI6ImRlbW8iLCJ2ZXJzaW9uIjoiMC4wLjAifX0=",
|
||||
"metadata": {
|
||||
"scanId": "scan-0001"
|
||||
},
|
||||
"schemaVersion": "1.0",
|
||||
"analyzer": {
|
||||
"name": "demo",
|
||||
"version": "0.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,70 @@
|
||||
{
|
||||
"tenantId": "tenant-default",
|
||||
"facts": [
|
||||
"id": "fact0000000000000000000000000000001",
|
||||
"callgraphId": "callgraph-0001",
|
||||
"subject": {
|
||||
"scanId": "scan-0001",
|
||||
"imageDigest": "sha256:abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc1"
|
||||
},
|
||||
"entryPoints": [
|
||||
"func:java:com.acme.App.main"
|
||||
],
|
||||
"states": [
|
||||
{
|
||||
"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"
|
||||
"target": "func:java:com.acme.Admin.debug",
|
||||
"reachable": false,
|
||||
"confidence": 0.25,
|
||||
"bucket": "unreachable",
|
||||
"weight": 0.0,
|
||||
"score": 0.0,
|
||||
"path": [],
|
||||
"evidence": {
|
||||
"runtimeHits": [],
|
||||
"blockedEdges": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "fact-2",
|
||||
"type": "coverage",
|
||||
"assetId": "registry.local/library/app@sha256:abc123",
|
||||
"metric": "sensors_present",
|
||||
"value": 0.94,
|
||||
"observedAt": "2025-12-05T10:11:00Z"
|
||||
"target": "func:java:com.acme.Auth.verify",
|
||||
"reachable": true,
|
||||
"confidence": 0.9,
|
||||
"bucket": "runtime",
|
||||
"weight": 0.45,
|
||||
"score": 0.405,
|
||||
"path": [
|
||||
"func:java:com.acme.App.main",
|
||||
"func:java:com.acme.Auth.verify"
|
||||
],
|
||||
"evidence": {
|
||||
"runtimeHits": [
|
||||
"func:java:com.acme.Auth.verify"
|
||||
],
|
||||
"blockedEdges": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"pagination": {
|
||||
"nextPageToken": "eyJmYWN0SWQiOiJmYWN0LTIifQ"
|
||||
}
|
||||
"runtimeFacts": [
|
||||
{
|
||||
"symbolId": "func:java:com.acme.Auth.verify",
|
||||
"codeId": "code:java:com.acme.Auth.verify",
|
||||
"purl": "pkg:maven/com.acme/demo-app@1.0.0?type=jar",
|
||||
"processId": 1234,
|
||||
"processName": "demo-app",
|
||||
"containerId": "containerd://0000000000000000",
|
||||
"hitCount": 3,
|
||||
"observedAt": "2025-12-12T00:00:00Z",
|
||||
"metadata": {
|
||||
"source": "synthetic-probe"
|
||||
}
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"fact.digest": "sha256:0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"fact.digest.alg": "sha256",
|
||||
"fact.version": "1"
|
||||
},
|
||||
"score": 0.2025,
|
||||
"riskScore": 0.2025,
|
||||
"unknownsCount": 0,
|
||||
"unknownsPressure": 0.0,
|
||||
"computedAt": "2025-12-12T00:00:00Z",
|
||||
"subjectKey": "scan-0001"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user