Scanner Endpoint Registration Matrix

Verified against commit 942a18727c (2026-09-26). Re-verify with: grep -nE "^(app|apiGroup)\.Map" src/Scanner/StellaOps.Scanner.WebService/Program.cs and grep -rnE "RequireAuthorization|AllowAnonymous" src/Scanner/StellaOps.Scanner.WebService/Endpoints/.

This file is the Scanner WebService source-of-truth for endpoint registration intent and authorization posture. Program.cs is the authority; policy names below are the ScannerPolicies constants (src/Scanner/StellaOps.Scanner.WebService/Security/ScannerPolicies.cs), and the scopes each policy accepts are bound in Program.cs (see architecture.md section 10). Routes under apiGroup are relative to scanner:api:basePath (default /api/v1); every apiGroup route passes through ScannerCapacityPauseFilter.

Q-25 assigns canonical HTTP import/status/manifest/validate, carrier quarantine/CAS, lifecycle and import-ledger ownership to OfflineKit at /api/offlinekit/v1. Scanner accepts the exact Scanner artifact-BOM part through its bounded owner seam; MapOfflineKitEndpoints and the route-only MapObservabilityEndpoints are not Scanner endpoint maps and must not be reintroduced. /metrics is not an active Scanner route.

Captured openapi_* snapshots describe their capture-time route surface and are not current source authority; the live document is GET /openapi/v1.json (anonymous, app.MapOpenApi()).

Host-level routes (outside the API base path)

RegistrationRoutesAuthorizationNotes
MapHealthEndpointsGET /healthz, GET /readyzScansReadHealth and readiness are authenticated.
MapServiceDoctorEndpoints("scanner-web")doctor-check/v1 read surfaceDoctorHealth (ops:health)Runs the doctor.scanner.* checks from StellaOps.Scanner.Operations.
MapBuildInfoEndpointGET /buildinfo.jsonanonymousBuild identity baked into the image.
MapScannerArtifactBomExchangeEndpointsGET /api/internal/v1/offlinekit/artifact-boms; POST /api/internal/v1/offlinekit/artifact-boms/activateArtifactBomExchangeBounded exact-tenant export/activation for scanner/artifact-boms.v1.json; Scanner remains the sole persistence owner.
inlineGET /internal/plugins/status, POST /internal/plugins/probeanonymousSigned analyzer bundle status/probe for the mounted plug-in directory.
MapControllersapi/v1/triage/* (gating, gated buckets, unified evidence, evidence export, replay commands, rationale), api/v1/findings/{findingId}/evidence + evidence/batch, api/v1/vulnerabilities*per controller: TriageRead (triage, findings evidence); ScansRead (vulnerabilities)Every controller must declare a named policy or a registered anonymous opt-out (ScannerControllerAuthorizationConformanceTests); the host has no fallback policy. VulnerabilitiesController serves the development fixture only (404 scanner.vulnerabilities.not_available otherwise). The VEX gate controller (api/v1/scans/{scanId}/gate-*, gate-policy, vex-reachability/filter) was withdrawn by PCD-222 (architecture.md section 5.5.6).
MapOpenApi/openapi/v1.jsonanonymousRead by the gateway aggregator.
MapSliceEndpoints/api/slices (query, {digest}, replay, cache/stats)ScansRead; cache/stats AdminReachability slices.
MapOciReferrersReadEndpoints`GETHEAD /v2/(anonymous ping),GETHEAD /v2/{**path}`
MapFunctionMapEndpoints/api/v1/function-maps (list, create, get, delete, verify, coverage)ScansRead / ScansWriteRelocated from Platform.

API-group maps (registered in Program.cs)

Map methodBase pathAuthorizationNotes
MapScanEndpoints/scansScansEnqueue (submit), ScansRead, ScansWriteSubmit, status, entropy, events, entrytrace, ruby/bun packages; nests the maps below.
nested MapScanDigestDetailEndpoints/scans/by-digest/{imageDigest}/detailScansDigestDetailReadDC-15 digest-scoped read (ScanCompletedContract.DetailRouteTemplate). Tenant from the validated claim only; 404 when no succeeded scan exists.
nested MapScanCompletedStreamEndpoints (inventory)/scans/completed/inventoryScansCompletedProjectionReadFenced current successful inventory for the authenticated tenant.
nested MapScanCompletedStreamEndpoints/scans/completed/events, POST /scans/completed/consumers/{consumerId}ScansCompletedProjectionReadDC-15 tenant catch-up feed and remote-consumer cursor.
nested MapCallGraphEndpointsPOST /scans/{scanId}/callgraphsCallGraphIngest
nested MapSbomEndpointsPOST /scans/{scanId}/sbomScansWrite
nested MapLayerSbomEndpoints/scans/{scanId}/layers, /layers/{layerDigest}/sbom, /composition-recipe, POST /composition-recipe/verifyScansReadServed from the Worker’s surface-manifest layer.fragments artifact (DEF-11).
nested MapReachabilityEndpoints/scans/{scanId}/compute-reachability, /reachability/{components,findings,explain,traces/export}ScansWrite / ScansRead
nested MapReachabilityDriftScanEndpoints/scans/{scanId}/driftScansRead
nested MapExportEndpoints/scans/{scanId}/exports/{sbom,sarif,cdxr,openvex,signed-sbom-archive}, POST .../signed-sbom-materialScansRead; material SignedSbomMaterialWriteSBOM export reads the Worker-persisted CycloneDX document (DAY-3).
nested MapEvidenceEndpoints/scans/{scanId}/evidence, /evidence/{**findingId}ScansRead
nested MapApprovalEndpoints/scans/{scanId}/approvals[/{findingId}]ScansApprove / ScansReadResolves the scan through the tenant-aware IScanCoordinator first; foreign or unknown scan: 404, no body (PCD-864).
nested MapManifestEndpoints/scans/{scanId}/manifest, /proofs[/{rootHash}]ScansReadResolves the scan through the tenant-aware IScanCoordinator first; foreign, unknown or ownerless scan: 404, no body (PCD-864).
nested MapGitHubCodeScanningEndpoints/scans/{scanId}/github/{upload-sarif,upload-status/{sarifId},alerts[/{alertNumber}]}ScansWrite / ScansRead503 github_code_scanning_not_configured when disabled.
MapSbomReadinessEndpoints/sbom-readiness, /sbom-attachmentsScansReadRelease gate readiness contract.
MapSourcesEndpoints/sourcesSourcesRead / SourcesWrite / SourcesAdminTenant-aware source CRUD and source runs.
MapScmImportEndpoints/scm/{integrationId}/bulk-import, /scm/imports/{batchId}SourcesWrite / SourcesRead
MapWebhookEndpoints/webhooksAllowAnonymousQuarantined: every route returns 501 source_scan_not_available.
MapSbomUploadEndpoints/byos-sboms, /byos-sboms/{sbomId}; nests /sbom/hot-lookup/* and /sbom/{scanId}/evidenceScansWrite / ScansRead
MapOciReferrerIngestEndpointsPOST /oci-referrersScansWriteCLI/CI referrer ingest into the internal store.
MapReachabilityDriftRootEndpoints/drift/{driftId}/sinksScansRead
MapDeltaCompareEndpoints/delta (compare, quick, {comparisonId})ScansRead
MapSmartDiffEndpoints/smart-diff (scans/{scanId}/changes, scans/{scanId}/sarif)ScansReadMaterial-change retrieval and its SARIF rendering; no automatic generation pipeline. The VEX-candidate list and review routes were withdrawn by PCD-222.
MapBaselineEndpoints/baselinesScansReadReserved shapes; deterministic 501 scanner_baseline_recommendations_unavailable.
MapActionablesEndpoints/actionablesScansRead
MapCounterfactualEndpoints/counterfactualsScansRead
MapProofSpineEndpoints/spines/{spineId}, /scans/{scanId}/spinesScansReadTenant required.
MapReplayEndpointsPOST /replay/{scanId}/attachScansWrite
MapScoreReplayEndpoints (feature-gated)/scans/{scanId}/score/{replay,bundle,verify,history}ScansRead / ScansWriteOnly when scanner:scoreReplay:enabled=true. No /score/{scanId} alias. Same tenant gate as MapManifestEndpoints (PCD-864).
MapWitnessEndpoints/witnessesScansRead
MapEpssEndpoints/epssScansReadReads EPSS from the Vulnerabilities hub source.
MapTriageStatusEndpoints/triage/findings/{findingId}, /status, /vex, /query, /summaryTriageRead / TriageWriteOwned status/VEX mutations return 501 scanner.triage.mutation_unavailable; missing/foreign findings remain 404. No decision, snapshot or VEX record is created.
MapTriageInboxEndpoints/triage/inboxTriageReadRequires artifactDigest; returns canonical bare UUIDs in findingIds[].
MapBatchTriageEndpoints/triage/inbox/clusters/stats, /triage/inbox/clusters/{pathId}/actionsTriageRead / TriageWriteOwned cluster actions return 501 scanner.triage.mutation_unavailable before returning updated IDs or an action record. Missing/foreign clusters remain 404; statistics remain readable.
MapProofBundleEndpointsPOST /triage/proof-bundleTriageWriteReserved shape; IProofBundleGenerator has no implementation or DI registration.
MapUnknownsEndpoints/unknownsScansReadTenant-scoped list/detail/evidence/history/stats/bands.
MapSecretDetectionSettingsEndpoints/secrets/config/{settings,exceptions,rules}SecretSettings* / SecretExceptions*
MapScannerTenantSettingsEndpoints/scanner/settingsOperationsRead / OperationsWriteStrong ETag compare-and-swap.
MapScannerAnalyzerEndpoints/scanner/analyzersOperationsRead
MapScannerPerformanceEndpoints/scanner/performanceOperationsRead
MapSecretsBundleEndpoints/secrets/bundlesSecretsBundleRead / SecretsBundleWrite
MapScanPolicyEndpoints/scan-policiesScansRead / ScansWrite
MapPolicyEndpoints (feature-gated)/policy (schema, diagnostics, preview, runtime, overlay; linksets withdrawn 2026-09-27, PCD-47)ReportsRegistered when scanner:features:enablePolicyPreview is true (the default).
MapReportEndpoints/reports, /reports/artifact-vulnerabilitiesReports
MapScanAttestationKeyEndpointsGET /scan-attestation/keysanonymousPublic JWKS for scan-result attestation verification.
MapRuntimeEndpoints/runtime/events, /runtime/reconcileRuntimeIngest
MapReachabilityStackEndpoints/reachability/{findingId}/stack, /stack/layer/{layerNumber}, /reachability/{scanId}/stack/framesScansRead
MapReachabilityEvidenceEndpoints/reachability/{analyze,result/{jobId},mapping/{cveId},vex,verdicts/{imageDigest}[/{vulnerabilityId}]}ScansRead / ScansWriteAnalyze, result and VEX store and read job results under the claim tenant; no claim: 400 tenant_missing (PCD-864).
MapAdvancedAssuranceReachabilityFixtureEndpoints, MapAdvancedAssuranceSbomFixtureEndpoints/qa/fixtures/advanced-assurance-golden/*ScansWriteQA fixture import/readback surfaces. The reachability import stores results under the claim tenant (PCD-864).
__auth-probe (Testing environment only)/__auth-probeScansEnqueueRegistered only when the host environment is Testing.

Webhook tenant contract (name routes)

All webhook routes are quarantined: RejectQuarantinedScanTrigger answers 501 source_scan_not_available before any dispatch (source-triggered scanning has no production IScanJobQueue; see src/Scanner/AGENTS.md). The tenant rules below describe the preserved, unwired receiver contract.

Triage tenant contract

Unknowns tenant contract

Reachability + SmartDiff tenant contract (SCAN-TEN-11)

Source-runs + secret-exception tenant contract (SCAN-TEN-13)

Deferred endpoint maps (present in source, not registered)

Map methodSourceReasonActivation prerequisite
MapValidationEndpointsEndpoints/ValidationEndpoints.cs (/api/v1/sbom/validate, /validators)Validator service graph and option bindings are not part of the startup contract.Explicit validation DI and contract docs, then registration in Program.cs.
MapFidelityEndpointsEndpoints/FidelityEndpoints.cs (/api/v1/scan/analyze, /findings/{findingId}/upgrade)Experimental; needs an analyzer service contract.Product sign-off and DI contract hardening.
MapDeltaEvidenceEndpointsEndpoints/DeltaEvidenceEndpoints.cs (/delta/evidence/*)Not wired in Program.cs.Contract decision and a backing evidence store.

The Scanner-side /api/v1/registries/* group and the fabricated /api/v1/security/* adapter were removed; Platform owns both surfaces.