Findings gateway contract and historical Web V design

Current serving contract (source verified 2026-10-07)

The serving owner is src/Findings/StellaOps.Findings.WebService. At commit 34b153ca04145478a0a87faf14cb5bf4002689e9, its Program.cs composes the ledger and scoring groups from src/Findings/__Libraries/StellaOps.Findings.Ledger.Application/Endpoints/. Current source routes include /api/findings/v1/ledger/* and /api/findings/v1/scoring/*; use the scoring reference and Findings architecture for the mapped owner contracts.

Ledger write/export and scoring read/write/admin policies share the configured findings:ledger:Authority:RequiredScopes array, defaulting to vuln:operate. Runtime trace reads/writes have separate findings:read / findings:write policies; those runtime scopes do not replace ledger-plane authorization. Tenant resolution follows the validated token/envelope and RequireTenant(); the legacy tenant header does not select a tenant. See API conventions.

The Web V workflow route /ledger/findings/{findingId}/actions, its proposed idempotency TTL, retry budget, ETags and error envelope below remain an unimplemented design. They are not an operator or client contract. Recheck the current host composition and endpoint source before treating any proposed behavior as available. This review makes no live deployment claim.

Historical Web V proxy proposal (2025-12-01; withdrawn guidance 2026-10-07)

The following predecessor design is retained for design history. Its examples, authentication assumptions and delivery guarantees are not supported current instructions; the source-backed contract above takes precedence.

This contract defines how the Stella Ops Web gateway forwards vulnerability-finding workflow actions (open, ack, close, reopen, export) to the Findings Ledger service. It covers the required headers, idempotency and correlation semantics, retry and offline-kit behavior, and the deterministic error envelope clients can rely on.

Status

Purpose

Required Headers

NameRequirementNotes
Authorization: Bearer <jwt>RequiredRS256/ES256 service token. The Findings Ledger service enforces findings:read (reads) and findings:write (writes/appends); see StellaOps.Findings.Ledger.WebService/Program.cs. (There is no ledger:read/ledger:write scope; the aud=stellaops-ledger audience shown elsewhere is illustrative.)
X-StellaOps-TenantIdRequiredTenant slug/UUID (must align with tenant-auth contract).
X-StellaOps-ProjectConditionalRequired for project-scoped findings. (Legacy alias X-Stella-Project is accepted only when the gateway is started with EnableLegacyHeaders=true.)
X-Idempotency-KeyRequired on POST/PUTDeterministic BLAKE3-256(base64url(tenant + route + canonical_body)); 44 chars. TTL: 24h.
X-Correlation-IdRequiredUUID/ULID stable across gateway → ledger → notifier; echoed by responses.
Content-TypeRequiredapplication/json.
If-MatchOptionalWhen present, ledger enforces optimistic concurrency using the last ETag value.

Behavior

Payload Contract

{
  "action": "ack",               // open|ack|close|reopen|export
  "finding_id": "f-7e12d9",
  "reason_code": "triage_accept",
  "comment": "Owner acknowledged risk and started fix",
  "attachments": [ { "name": "triage.pdf", "digest": "sha256-..." } ],
  "actor": { "subject": "svc-console", "type": "service" },
  "metadata": { "policy_version": "2025.11.0", "vex_statement_id": "vex-123" }
}

Example Request

curl -X POST https://gateway.stellaops.local/ledger/findings/f-7e12d9/actions \
  -H "Authorization: Bearer $LEDGER_TOKEN" \
  -H "X-StellaOps-TenantId: acme-tenant" \
  -H "X-Correlation-Id: 01HXYZABCD1234567890" \
  -H "X-Idempotency-Key: 3cV1..." \
  -H "Content-Type: application/json" \
  --data '{"action":"ack","finding_id":"f-7e12d9","reason_code":"triage_accept","actor":{"subject":"svc-console","type":"service"}}'

Example Response

{
  "status": "accepted",
  "ledger_event_id": "ledg-01HF7T4X6E4S7A6PK8",
  "etag": "W/\"01-2a9c\"",
  "trace_id": "01HXYZABCD1234567890",
  "correlation_id": "01HXYZABCD1234567890"
}

Open Questions