consolidation of some of the modules, localization fixes, product advisories work, qa work

This commit is contained in:
master
2026-03-05 03:54:22 +02:00
parent 7bafcc3eef
commit 8e1cb9448d
3878 changed files with 72600 additions and 46861 deletions

View File

@@ -24,43 +24,36 @@ This directory contains architecture documentation for all StellaOps modules.
| Module | Path | Description |
|--------|------|-------------|
| [Authority](./authority/) | `src/Authority/` | Authentication, authorization, OAuth/OIDC, DPoP |
| [Gateway](./gateway/) | `src/Gateway/` | API gateway with routing and transport abstraction |
| [Router](./router/) | `src/Router/` | Transport-agnostic messaging (TCP/TLS/UDP/RabbitMQ/Valkey). Note: also contains a `StellaOps.Gateway.WebService` for binary protocol bridging, separate from `src/Gateway/`. |
| [Authority](./authority/) | `src/Authority/` | Authentication, authorization, OAuth/OIDC, DPoP. Includes IssuerDirectory (Sprint 216). |
| [Router](./router/) | `src/Router/` | Transport-agnostic messaging (TCP/TLS/UDP/RabbitMQ/Valkey). Also contains `StellaOps.Gateway.WebService` for HTTP ingress and binary protocol bridging. |
| [Platform](./platform/) | `src/Platform/` | Platform architecture and Platform Service aggregation APIs |
### Data Ingestion
| Module | Path | Description |
|--------|------|-------------|
| [Concelier](./concelier/) | `src/Concelier/` | Vulnerability advisory ingestion and merge engine |
| [Excititor](./excititor/) | `src/Excititor/` | VEX document ingestion and export |
| [Concelier](./concelier/) | `src/Concelier/` | Vulnerability advisory ingestion and merge engine. Includes Feedser and Excititor (Sprint 203). |
| [VexLens](./vex-lens/) | `src/VexLens/` | VEX consensus computation across issuers |
| [VexHub](./vex-hub/) | `src/VexHub/` | VEX distribution and exchange hub |
| [IssuerDirectory](./issuer-directory/) | `src/IssuerDirectory/` | Issuer trust registry (CSAF publishers) |
| [Feedser](./feedser/) | `src/Feedser/` | Evidence collection library for backport detection |
| [Mirror](./mirror/) | `src/Mirror/` | Vulnerability feed mirror and distribution |
### Scanning & Analysis
| Module | Path | Description |
|--------|------|-------------|
| [Scanner](./scanner/) | `src/Scanner/` | Container scanning with SBOM generation |
| [BinaryIndex](./binary-index/) | `src/BinaryIndex/` | Binary identity extraction and fingerprinting |
| [AdvisoryAI](./advisory-ai/) | `src/AdvisoryAI/` | AI-assisted advisory analysis |
| [Symbols](./symbols/) | `src/Symbols/` | Symbol resolution and debug information |
| [Scanner](./scanner/) | `src/Scanner/` | Container scanning with SBOM generation. Includes Cartographer (Sprint 201). |
| [BinaryIndex](./binary-index/) | `src/BinaryIndex/` | Binary identity extraction and fingerprinting. Includes Symbols (Sprint 202). |
| [AdvisoryAI](./advisory-ai/) | `src/AdvisoryAI/` | AI-assisted advisory analysis. Includes OpsMemory (Sprint 213). |
| [ReachGraph](./reach-graph/) | `src/ReachGraph/` | Reachability graph service |
### Artifacts & Evidence
| Module | Path | Description |
|--------|------|-------------|
| [Attestor](./attestor/) | `src/Attestor/` | in-toto/DSSE attestation generation |
| [Signer](./signer/) | `src/Signer/` | Cryptographic signing operations |
| [Attestor](./attestor/) | `src/Attestor/` | in-toto/DSSE attestation generation. Includes Signer and Provenance (Sprint 204). |
| [SbomService](./sbom-service/) | `src/SbomService/` | SBOM storage, versioning, and lineage ledger |
| [EvidenceLocker](./evidence-locker/) | `src/EvidenceLocker/` | Sealed evidence storage and export |
| [ExportCenter](./export-center/) | `src/ExportCenter/` | Batch export and report generation |
| [Provenance](./provenance/) | `src/Provenance/` | SLSA/DSSE attestation tooling |
| [Provcache](./prov-cache/) | Library | Production provenance cache shared library family |
### Policy & Risk
@@ -68,20 +61,15 @@ This directory contains architecture documentation for all StellaOps modules.
| Module | Path | Description |
|--------|------|-------------|
| [Policy](./policy/) | `src/Policy/` | Policy engine with K4 lattice logic |
| [RiskEngine](./risk-engine/) | `src/RiskEngine/` | Risk scoring runtime |
| [VulnExplorer](./vuln-explorer/) | `src/VulnExplorer/` | Vulnerability exploration and triage |
| [Unknowns](./unknowns/) | `src/Unknowns/` | Unknown component tracking registry |
| [Findings](./findings-ledger/) | `src/Findings/` | Centralized findings aggregation and evidence graphs |
| [Unknowns](./unknowns/) | `src/Unknowns/` | Unknown component tracking registry (boundary preserved, Sprint 206) |
| [Findings](./findings-ledger/) | `src/Findings/` | Centralized findings aggregation and evidence graphs. Includes RiskEngine and VulnExplorer (Sprint 207). |
### Release & Orchestration
| Module | Path | Description |
|--------|------|-------------|
| [ReleaseOrchestrator](./release-orchestrator/) | `src/ReleaseOrchestrator/` | Central release control plane (active development) |
| [Orchestrator](./orchestrator/) | `src/Orchestrator/` | Workflow orchestration and task coordination |
| [Scheduler](./scheduler/) | `src/Scheduler/` | Job scheduling and queue management |
| [TaskRunner](./taskrunner/) | `src/TaskRunner/` | Task pack execution engine |
| [PacksRegistry](./packs-registry/) | `src/PacksRegistry/` | Task packs registry |
| [ReleaseOrchestrator](./release-jobengine/) | `src/ReleaseOrchestrator/` | Central release control plane (active development) |
| [JobEngine](./jobengine/) | `src/JobEngine/` | Workflow orchestration, job scheduling, task execution, and pack registry. Includes Scheduler, TaskRunner, and PacksRegistry (Sprint 208); renamed from Orchestrator (Sprint 221). |
| [Remediation](./remediation/) | `src/Remediation/` | Fix template marketplace for CVE remediation |
### Operations & Observability
@@ -89,11 +77,9 @@ This directory contains architecture documentation for all StellaOps modules.
| Module | Path | Description |
|--------|------|-------------|
| [Doctor](./doctor/) | `src/Doctor/` | Diagnostic framework for system health validation |
| [Notify](./notify/) | `src/Notify/` | Notification toolkit (Email, Slack, Teams, Webhooks) |
| [Notifier](./notifier/) | `src/Notifier/` | Notifications Studio host |
| [OpsMemory](./opsmemory/) | `src/OpsMemory/` | Decision ledger with similarity-based suggestions |
| [Timeline](./timeline/) | `src/Timeline/` | Timeline query service for event browsing |
| [TimelineIndexer](./timeline-indexer/) | `src/TimelineIndexer/` | Timeline event indexing |
| [Notify](./notify/) | `src/Notify/` | Notification toolkit (Email, Slack, Teams, Webhooks). Boundary preserved with Notifier (Sprint 209). |
| [Notifier](./notifier/) | `src/Notifier/` | Notifications Studio host. Boundary preserved with Notify (Sprint 209). |
| [Timeline](./timeline/) | `src/Timeline/` | Timeline query, event indexing, and replay. Includes TimelineIndexer (Sprint 210). |
| [Replay](./replay/) | `src/Replay/` | Deterministic replay engine |
### Integration & Clients
@@ -105,10 +91,7 @@ This directory contains architecture documentation for all StellaOps modules.
| [Web/UI](./ui/) | `src/Web/` | Angular 21 frontend SPA |
| [API](./api/) | `src/Api/` | OpenAPI contracts and governance |
| [Registry](./registry/) | `src/Registry/` | Container registry integration |
| [Integrations](./integrations/) | `src/Integrations/` | Integration hub for external systems (SCM, CI, registries, secrets) |
| [Extensions](./extensions/) | `src/Extensions/` | IDE extensions for JetBrains and VS Code |
| [Sdk](./sdk/) | `src/Sdk/` | Client SDK generator and release SDK |
| [DevPortal](./devportal/) | `src/DevPortal/` | Developer portal static site |
| [Integrations](./integrations/) | `src/Integrations/` | Integration hub for external systems (SCM, CI, registries, secrets). Includes IDE extensions (VS Code, JetBrains) under `__Extensions/` (Sprint 214). |
### Infrastructure & Libraries
@@ -123,7 +106,6 @@ This directory contains architecture documentation for all StellaOps modules.
| [AOC](./aoc/) | `src/Aoc/` | Append-Only Contract enforcement |
| [Plugin](./plugin/) | `src/Plugin/` | Plugin SDK, registry, sandbox, and host framework |
| [RuntimeInstrumentation](./runtime-instrumentation/) | `src/RuntimeInstrumentation/` | Tetragon-based eBPF runtime instrumentation |
| [Cartographer](./cartographer/) | `src/Cartographer/` | Infrastructure topology discovery |
| [Facet](./facet/) | Library | Production cross-module faceting library (Scanner + Policy) |
### Testing & Benchmarks
@@ -131,9 +113,7 @@ This directory contains architecture documentation for all StellaOps modules.
| Module | Path | Description |
|--------|------|-------------|
| [Benchmark](./benchmark/) | Scanner library | Competitive benchmarking (accuracy comparison) |
| [Bench](./bench/) | `src/Bench/` | Performance benchmarks |
| [Tools](./tools/) | `src/Tools/` | Developer utility tools (fixtures, golden pairs, smoke tests) |
| [Verifier](./verifier/) | `src/Verifier/` | Standalone evidence bundle verification CLI |
| [Tools](./tools/) | `src/Tools/` | Developer utility tools, benchmarks, SDK generator, verifier, dev portal. Includes Bench, Verifier, Sdk, DevPortal (Sprint 212). |
### Cross-Cutting Concepts
@@ -231,14 +211,14 @@ On-premises OIDC/OAuth2 identity service issuing short-lived, sender-constrained
---
### Bench
- **Source**: `src/Bench/`
- **Docs**: [`docs/modules/bench/`](./bench/)
### Bench (archived -- absorbed into Tools)
- **Source**: `src/Tools/StellaOps.Bench/`
- **Docs**: [`docs/modules/tools/`](./tools/)
- **Type**: Tool
- **Database**: None
- **Endpoints**: None
Performance benchmark harnesses (BenchmarkDotNet) for critical platform subsystems including Link-Not-Merge, VEX, Notify, Policy Engine, and Scanner analyzers. Results establish performance baselines and detect regressions.
Performance benchmark harnesses for critical platform subsystems including Link-Not-Merge, VEX, Notify, Policy Engine, and Scanner analyzers. Absorbed into `src/Tools/` as of Sprint 212.
**Dependencies**: None (standalone benchmarks).
@@ -257,14 +237,14 @@ Vulnerable binaries database enabling detection of vulnerable code at the binary
---
### Cartographer
- **Source**: `src/Cartographer/`
- **Docs**: [`docs/modules/cartographer/`](./cartographer/)
### Cartographer (absorbed into Scanner -- Sprint 201)
- **Source**: `src/Scanner/StellaOps.Scanner.Cartographer/` (moved from `src/Cartographer/`)
- **Docs**: Historical doc archived at `docs-archived/modules/cartographer/README.md`; active contract is [`docs/modules/scanner/`](./scanner/)
- **Type**: Service
- **Database**: None
- **Endpoints**: Defined in Program.cs
- **Endpoints**: Defined in Scanner Program.cs
Infrastructure topology discovery and service mapping for container environments. Produces SBOM snapshots and topology graphs consumed by the Graph Indexer. Environment topology and promotion lanes are now owned by the Release Orchestrator.
Infrastructure topology discovery and service mapping for container environments. Produces SBOM snapshots and topology graphs consumed by the Graph Indexer. Environment topology and promotion lanes are now owned by the Release Orchestrator. Consolidated into Scanner per Sprint 201.
**Dependencies**: Graph, Scanner.
@@ -309,14 +289,14 @@ Pluggable cryptographic primitives supporting regional standards (eIDAS, FIPS, G
---
### DevPortal
- **Source**: `src/DevPortal/`
- **Docs**: [`docs/modules/devportal/`](./devportal/)
### DevPortal (archived -- absorbed into Tools)
- **Source**: `src/Tools/StellaOps.DevPortal.Site/`
- **Docs**: [`docs/modules/tools/`](./tools/)
- **Type**: Static Site
- **Database**: None
- **Endpoints**: None
Developer portal static site providing API documentation, integration guides, SDK references, and getting-started tutorials. Aggregates OpenAPI specifications from all services for third-party developers and integrators.
Developer portal static site providing API documentation, integration guides, SDK references, and getting-started tutorials. Absorbed into `src/Tools/` as of Sprint 212.
**Dependencies**: None (static site).
@@ -344,20 +324,20 @@ Diagnostic framework for validating system health, configuration, integration co
Tamper-proof, immutable evidence storage for vulnerability scan evidence, audit logs, and compliance artifacts with cryptographic sealing. Evidence is content-addressable. Once sealed, evidence cannot be modified. Supports threads, verdicts, bundle packaging, and portable bundles for offline compliance audits.
**Dependencies**: Signer, Attestor, Authority, object storage.
**Dependencies**: Attestor, Authority, object storage.
---
### Excititor
- **Source**: `src/Excititor/`
- **Docs**: [`docs/modules/excititor/`](./excititor/)
### Excititor (absorbed into Concelier -- Sprint 203)
- **Source**: `src/Concelier/` (moved from `src/Excititor/`)
- **Docs**: [`docs/modules/excititor/`](./excititor/) (historical reference; see [`docs/modules/concelier/`](./concelier/))
- **Type**: Service
- **Database**: PostgreSQL (10 SQL migrations)
- **Database**: PostgreSQL (10 SQL migrations, separate ExcititorDbContext)
- **Endpoints**: 11 (attestation, evidence, ingest, linkset, mirror, mirror registration, observation, policy, Rekor attestation, resolve, risk feed)
VEX ingestion and consensus pipeline converting heterogeneous VEX statements (OpenVEX, CSAF VEX, CycloneDX VEX) into immutable observations with provenance-preserving linksets. Does not decide PASS/FAIL; supplies evidence with statuses, justifications, and provenance weights. Conflicting observations are preserved unchanged.
VEX ingestion and consensus pipeline converting heterogeneous VEX statements (OpenVEX, CSAF VEX, CycloneDX VEX) into immutable observations with provenance-preserving linksets. Does not decide PASS/FAIL; supplies evidence with statuses, justifications, and provenance weights. Conflicting observations are preserved unchanged. Consolidated into Concelier per Sprint 203; DbContext remains separate.
**Dependencies**: Policy Engine, Concelier, Attestor / Signer, Graph.
**Dependencies**: Policy Engine, Concelier, Attestor, Graph.
---
@@ -370,31 +350,31 @@ VEX ingestion and consensus pipeline converting heterogeneous VEX statements (Op
Evidence and policy overlay packaging service producing reproducible, deterministic export bundles in multiple formats (JSON, SARIF, offline kit). Enforces AOC guardrails and produces deterministic manifests with optional signing and distribution to OCI registries or object storage.
**Dependencies**: Findings Ledger, Policy Engine, Orchestrator, Authority, Signer, object storage.
**Dependencies**: Findings Ledger, Policy Engine, JobEngine, Authority, Attestor, object storage.
---
### Extensions
- **Source**: `src/Extensions/`
- **Docs**: [`docs/modules/extensions/`](./extensions/)
- **Type**: IDE Extensions
### Extensions (absorbed into Integrations -- Sprint 214)
- **Source**: `src/Integrations/__Extensions/` (moved from `src/Extensions/`)
- **Docs**: See [Integrations architecture](./integrations/architecture.md#ide-extensions-vs-code-jetbrains)
- **Type**: IDE Extensions (non-.NET: TypeScript/Kotlin)
- **Database**: None
- **Endpoints**: None
IDE extensions for JetBrains IDEs and Visual Studio Code providing inline vulnerability information, policy status, and StellaOps workflow integration directly within the developer's editor environment.
IDE extensions for JetBrains IDEs and Visual Studio Code providing inline vulnerability information, policy status, and StellaOps workflow integration directly within the developer's editor environment. Now lives under the Integrations module.
**Dependencies**: Platform API.
**Dependencies**: Platform API, JobEngine API, Authority.
---
### Feedser
- **Source**: `src/Feedser/`
- **Docs**: [`docs/modules/feedser/`](./feedser/)
### Feedser (absorbed into Concelier -- Sprint 203)
- **Source**: `src/Concelier/` (moved from `src/Feedser/`)
- **Docs**: [`docs/modules/feedser/`](./feedser/) (historical reference; see [`docs/modules/concelier/`](./concelier/))
- **Type**: Library
- **Database**: None
- **Endpoints**: None
Evidence collection library for backport detection and binary fingerprinting supporting the four-tier backport proof system. Extracts patch signatures from unified diffs and binary fingerprints from compiled code. Consumed primarily by Concelier's ProofService layer. All outputs are deterministic with canonical JSON serialization.
Evidence collection library for backport detection and binary fingerprinting supporting the four-tier backport proof system. Extracts patch signatures from unified diffs and binary fingerprints from compiled code. Consumed primarily by Concelier's ProofService layer. All outputs are deterministic with canonical JSON serialization. Consolidated into Concelier per Sprint 203.
**Dependencies**: None (consumed as a library by Concelier).
@@ -413,16 +393,16 @@ Centralized findings aggregation service providing backport tracking, evidence g
---
### Gateway
- **Source**: `src/Gateway/`
- **Docs**: [`docs/modules/gateway/`](./gateway/)
- **Type**: Service
- **Database**: None (stateless)
- **Endpoints**: None (reverse proxy)
### Gateway (deleted -- Sprint 200)
- **Source**: _(deleted)_ -- Gateway WebService now lives under `src/Router/StellaOps.Gateway.WebService/`
- **Docs**: [`docs-archived/modules/gateway/`](../docs-archived/modules/gateway/) (historical reference)
- **Type**: _(deleted)_
- **Database**: None
- **Endpoints**: None
Single HTTP ingress point for all external traffic providing authentication, routing, OpenAPI aggregation, health monitoring, rate limiting, and tenant propagation. A separate `StellaOps.Gateway.WebService` also exists under `src/Router/` which serves as the transport-layer gateway for the Router's binary protocol.
The standalone `src/Gateway/` module was deleted in Sprint 200. The canonical Gateway WebService (`StellaOps.Gateway.WebService`) now lives under `src/Router/`. The Router module owns HTTP ingress, binary protocol bridging, routing, and transport abstraction.
**Dependencies**: Authority, Router, all microservices (proxied requests).
**Dependencies**: See Router.
---
@@ -452,14 +432,14 @@ Integration hub managing connections to external systems (SCM, CI, registries, s
---
### IssuerDirectory
- **Source**: `src/IssuerDirectory/`
- **Docs**: [`docs/modules/issuer-directory/`](./issuer-directory/)
### IssuerDirectory (absorbed into Authority -- Sprint 216)
- **Source**: `src/Authority/` (moved from `src/IssuerDirectory/`)
- **Docs**: [`docs/modules/issuer-directory/`](./issuer-directory/) (historical reference; see [`docs/modules/authority/`](./authority/))
- **Type**: Service
- **Database**: PostgreSQL (1 SQL migration)
- **Database**: PostgreSQL (1 SQL migration, separate IssuerDirectoryDbContext)
- **Endpoints**: 3 (issuer, issuer key, issuer trust)
Centralized trusted VEX/CSAF publisher metadata registry enabling issuer identity resolution, key management, and trust weight assignment. Key lifecycle management validates Ed25519, X.509, and DSSE public keys with fingerprint deduplication. On startup, imports default CSAF publishers into the global tenant.
Centralized trusted VEX/CSAF publisher metadata registry enabling issuer identity resolution, key management, and trust weight assignment. Key lifecycle management validates Ed25519, X.509, and DSSE public keys with fingerprint deduplication. On startup, imports default CSAF publishers into the global tenant. Consolidated into Authority per Sprint 216; DbContext remains separate for security isolation.
**Dependencies**: Authority.
@@ -504,42 +484,43 @@ Rules-driven, tenant-aware notification engine providing event consumption, oper
---
### OpsMemory
- **Source**: `src/OpsMemory/`
- **Docs**: [`docs/modules/opsmemory/`](./opsmemory/)
### OpsMemory (consolidated into AdvisoryAI)
- **Source**: `src/AdvisoryAI/__Libraries/StellaOps.OpsMemory/` (library), `src/AdvisoryAI/StellaOps.OpsMemory.WebService/` (service)
- **Docs**: [`docs/modules/advisory-ai/architecture.md` section 15](./advisory-ai/architecture.md#15-opsmemory-operational-memory-and-rag)
- **Archived docs**: `docs-archived/modules/opsmemory/`
- **Type**: Service
- **Database**: PostgreSQL (via shared infrastructure, schema managed programmatically)
- **Endpoints**: 1 (OpsMemoryEndpoints)
Decision ledger capturing the lifecycle of security decisions with similarity-based suggestion retrieval for organizational learning. Uses similarity vectors to suggest relevant precedents for new situations. Deterministic with fixed similarity formulas, no randomness in ranking, and multi-tenant isolation.
Decision ledger capturing the lifecycle of security decisions with similarity-based suggestion retrieval for organizational learning. Uses similarity vectors to suggest relevant precedents for new situations. Deterministic with fixed similarity formulas, no randomness in ranking, and multi-tenant isolation. Consolidated from standalone `src/OpsMemory/` module into `src/AdvisoryAI/` per Sprint 213.
**Dependencies**: AdvisoryAI, Authority.
**Dependencies**: AdvisoryAI, Authority, Findings Ledger.
---
### Orchestrator
- **Source**: `src/Orchestrator/`
- **Docs**: [`docs/modules/orchestrator/`](./orchestrator/)
### JobEngine (formerly Orchestrator)
- **Source**: `src/JobEngine/`
- **Docs**: [`docs/modules/jobengine/`](./jobengine/)
- **Type**: Service
- **Database**: PostgreSQL (via shared infrastructure)
- **Endpoints**: 25 (approvals, audit, circuit breakers, DAG, dead letter, export jobs, first signal, health, jobs, KPIs, ledger, OpenAPI, pack registry, pack runs, quotas, governance, release control v2, release dashboard, releases, runs, scale, SLOs, sources, streams, workers)
Source and job orchestration service managing job lifecycle, rate-limit governance, DAG execution, circuit breakers, and worker coordination. Applies quotas and rate limits per tenant/jobType, manages leasing to workers, handles completion tracking with retry policies, and supports replay. SDK bridges exist for Go and Python workers.
Source and job orchestration service managing job lifecycle, rate-limit governance, DAG execution, circuit breakers, and worker coordination. Applies quotas and rate limits per tenant/jobType, manages leasing to workers, handles completion tracking with retry policies, and supports replay. SDK bridges exist for Go and Python workers. Renamed from Orchestrator (Sprint 221). Now includes Scheduler, TaskRunner, and PacksRegistry (Sprint 208).
**Dependencies**: TaskRunner, Concelier / Excititor / Scheduler / ExportCenter / Policy (job producers), Valkey or NATS, Authority.
**Dependencies**: Concelier / Excititor / ExportCenter / Policy (job producers), Valkey or NATS, Authority.
---
### PacksRegistry
- **Source**: `src/PacksRegistry/`
- **Docs**: [`docs/modules/packs-registry/`](./packs-registry/)
### PacksRegistry (absorbed into JobEngine -- Sprint 208)
- **Source**: `src/JobEngine/` (moved from `src/PacksRegistry/`)
- **Docs**: [`docs/modules/packs-registry/`](./packs-registry/) (historical reference; see [`docs/modules/jobengine/`](./jobengine/))
- **Type**: Service
- **Database**: PostgreSQL (`PacksRegistryDbContext`, EF Core managed)
- **Endpoints**: Defined in WebService Program.cs
- **Database**: PostgreSQL (`PacksRegistryDbContext`, EF Core managed, stub)
- **Endpoints**: Defined in JobEngine Program.cs
Centralized registry for distributable task packs, policy packs, and analyzer bundles with versioned management and integrity verification. All packs are content-addressed. Pack execution is handled by TaskRunner.
Centralized registry for distributable task packs, policy packs, and analyzer bundles with versioned management and integrity verification. All packs are content-addressed. Consolidated into JobEngine per Sprint 208.
**Dependencies**: TaskRunner, object storage, Authority.
**Dependencies**: JobEngine, object storage, Authority.
---
@@ -582,16 +563,16 @@ Deterministic policy evaluation engine and gateway service compiling stella-dsl
---
### Provenance
- **Source**: `src/Provenance/`
- **Docs**: [`docs/modules/provenance/`](./provenance/)
### Provenance (absorbed into Attestor -- Sprint 204)
- **Source**: `src/Attestor/` (moved from `src/Provenance/`)
- **Docs**: [`docs/modules/provenance/`](./provenance/) (historical reference; see [`docs/modules/attestor/`](./attestor/))
- **Type**: Library / Tool
- **Database**: None
- **Endpoints**: None
Provenance attestation library and CLI tool for generating and verifying supply-chain provenance records. Creates in-toto attestation statements linking build artifacts to source materials, build systems, and parameters. A separate provenance cache library exists at `src/__Libraries/StellaOps.Provcache.Postgres/`.
Provenance attestation library and CLI tool for generating and verifying supply-chain provenance records. Creates in-toto attestation statements linking build artifacts to source materials, build systems, and parameters. A separate provenance cache library exists at `src/__Libraries/StellaOps.Provcache.Postgres/`. Consolidated into Attestor per Sprint 204.
**Dependencies**: Signer, Attestor.
**Dependencies**: Attestor.
---
@@ -623,7 +604,7 @@ Docker registry bearer token service issuing short-lived tokens for private or m
### ReleaseOrchestrator
- **Source**: `src/ReleaseOrchestrator/`
- **Docs**: [`docs/modules/release-orchestrator/`](./release-orchestrator/)
- **Docs**: [`docs/modules/release-jobengine/`](./release-jobengine/)
- **Type**: Service (Active Development)
- **Database**: PostgreSQL (planned, via Platform migrations)
- **Endpoints**: 1
@@ -661,8 +642,8 @@ Deterministic replay engine ensuring vulnerability assessments can be reproduced
---
### RiskEngine
- **Source**: `src/RiskEngine/`
- **Docs**: [`docs/modules/risk-engine/`](./risk-engine/)
- **Source**: `src/Findings/StellaOps.RiskEngine.*` (consolidated into Findings, Sprint 207)
- **Docs**: [`docs-archived/modules/risk-engine/`](../../docs-archived/modules/risk-engine/)
- **Type**: Service
- **Database**: PostgreSQL (via shared infrastructure)
- **Endpoints**: 1 (exploit maturity)
@@ -680,9 +661,9 @@ Risk scoring runtime computing deterministic, explainable risk scores by aggrega
- **Database**: None
- **Endpoints**: 4
Internal service transport using binary protocol (TCP/TLS/UDP) for microservice-to-gateway communication with pluggable transports. Includes a unified plugin, shared libraries, and example microservices. The Router's `StellaOps.Gateway.WebService` bridges binary protocol connections to HTTP; this is separate from `src/Gateway/` which is the HTTP ingress gateway.
Internal service transport using binary protocol (TCP/TLS/UDP) for microservice-to-gateway communication with pluggable transports. Includes a unified plugin, shared libraries, and example microservices. The `StellaOps.Gateway.WebService` under Router serves as both the HTTP ingress gateway and binary protocol bridge (the standalone `src/Gateway/` was deleted in Sprint 200).
**Dependencies**: Gateway, all microservices, Valkey.
**Dependencies**: Authority, all microservices, Valkey.
---
@@ -725,27 +706,27 @@ Deterministic SBOM generation and vulnerability scanning engine for container im
---
### Scheduler
- **Source**: `src/Scheduler/`
- **Docs**: [`docs/modules/scheduler/`](./scheduler/)
### Scheduler (absorbed into JobEngine -- Sprint 208)
- **Source**: `src/JobEngine/` (moved from `src/Scheduler/`)
- **Docs**: [`docs/modules/scheduler/`](./scheduler/) (historical reference; see [`docs/modules/jobengine/`](./jobengine/))
- **Type**: Service
- **Database**: PostgreSQL (11 SQL migrations)
- **Database**: PostgreSQL (11 SQL migrations, separate SchedulerDbContext)
- **Endpoints**: 8 (event webhook, failure signature, graph job, policy run, policy simulation, run, schedule, resolver job)
Re-evaluation scheduler keeping scan results current by pinpointing affected images when new advisories or VEX claims arrive. Default mode is analysis-only (no image pull). Includes event webhooks, failure signature tracking, graph jobs, policy runs/simulations, and vulnerability resolver jobs.
Re-evaluation scheduler keeping scan results current by pinpointing affected images when new advisories or VEX claims arrive. Default mode is analysis-only (no image pull). Includes event webhooks, failure signature tracking, graph jobs, policy runs/simulations, and vulnerability resolver jobs. Consolidated into JobEngine per Sprint 208; DbContext remains separate.
**Dependencies**: Scanner.WebService, Policy Engine, Concelier / Excititor, Notify, Orchestrator.
**Dependencies**: Scanner.WebService, Policy Engine, Concelier / Excititor, Notify, JobEngine.
---
### Sdk
- **Source**: `src/Sdk/`
- **Docs**: [`docs/modules/sdk/`](./sdk/)
### Sdk (archived -- absorbed into Tools)
- **Source**: `src/Tools/StellaOps.Sdk.Generator/`, `src/Tools/StellaOps.Sdk.Release/`
- **Docs**: [`docs/modules/tools/`](./tools/)
- **Type**: Library / Code Generator
- **Database**: None
- **Endpoints**: None
Client SDK generator and release SDK for producing typed API clients across multiple languages from OpenAPI specifications. Includes `StellaOps.Sdk.Generator` (code generator) and `StellaOps.Sdk.Release` (publishing SDK).
Client SDK generator and release SDK for producing typed API clients across multiple languages from OpenAPI specifications. Absorbed into `src/Tools/` as of Sprint 212.
**Dependencies**: Gateway / OpenAPI specs.
@@ -764,14 +745,14 @@ Unified evidence-weighted scoring system aggregating reachability, runtime obser
---
### Signer
- **Source**: `src/Signer/`
- **Docs**: [`docs/modules/signer/`](./signer/)
### Signer (absorbed into Attestor -- Sprint 204)
- **Source**: `src/Attestor/` (moved from `src/Signer/`)
- **Docs**: [`docs/modules/signer/`](./signer/) (historical reference; see [`docs/modules/attestor/`](./attestor/))
- **Type**: Service
- **Database**: PostgreSQL (`KeyManagementDbContext`, 2 SQL migrations)
- **Database**: PostgreSQL (`KeyManagementDbContext`, 2 SQL migrations, separate DbContext)
- **Endpoints**: 3 (ceremony, key rotation, signer)
The only service permitted to produce Stella Ops-verified DSSE signatures over SBOMs and reports, enforcing entitlement (PoE), sender-constrained auth, and supply-chain integrity. Does not push to Rekor (Attestor does). Stateless for the hot path with keys in KMS/HSM or ephemeral (keyless mode). Supports multi-algorithm signing (ECDSA, EdDSA, eIDAS, FIPS, GOST, SM).
The only service permitted to produce Stella Ops-verified DSSE signatures over SBOMs and reports, enforcing entitlement (PoE), sender-constrained auth, and supply-chain integrity. Does not push to Rekor (Attestor does). Stateless for the hot path with keys in KMS/HSM or ephemeral (keyless mode). Supports multi-algorithm signing (ECDSA, EdDSA, eIDAS, FIPS, GOST, SM). Consolidated into Attestor per Sprint 204; DbContext remains separate for security isolation.
**Dependencies**: Authority, Cryptography library, KMS/HSM.
@@ -790,29 +771,29 @@ Remote service for Chinese SM2/SM3/SM4 cryptographic operations enabling soverei
---
### Symbols
- **Source**: `src/Symbols/`
- **Docs**: [`docs/modules/symbols/`](./symbols/)
### Symbols (absorbed into BinaryIndex -- Sprint 202)
- **Source**: `src/BinaryIndex/StellaOps.Symbols.*` (moved from `src/Symbols/`)
- **Docs**: [`docs/modules/binary-index/architecture.md`](./binary-index/architecture.md) (Symbols section)
- **Type**: Service
- **Database**: None (content-addressed storage)
- **Endpoints**: 1 (symbol source)
Symbol resolution and debug information management service for native binary analysis. Maps symbols to packages, manages debug information, and supports stripped binary analysis. Includes marketplace architecture for community-contributed symbol sources and offline symbol stores.
Symbol resolution and debug information management service for native binary analysis. Maps symbols to packages, manages debug information, and supports stripped binary analysis. Includes marketplace architecture for community-contributed symbol sources and offline symbol stores. Consolidated into BinaryIndex per Sprint 202.
**Dependencies**: Scanner, BinaryIndex.
---
### TaskRunner
- **Source**: `src/TaskRunner/`
- **Docs**: [`docs/modules/taskrunner/`](./taskrunner/)
### TaskRunner (absorbed into JobEngine -- Sprint 208)
- **Source**: `src/JobEngine/` (moved from `src/TaskRunner/`)
- **Docs**: [`docs/modules/taskrunner/`](./taskrunner/) (historical reference; see [`docs/modules/jobengine/`](./jobengine/))
- **Type**: Service
- **Database**: PostgreSQL (via infrastructure layer)
- **Endpoints**: Defined in WebService/Worker Program.cs
- **Database**: PostgreSQL (via infrastructure layer, stub DbContext)
- **Endpoints**: Defined in JobEngine Program.cs
Deterministic task pack execution engine with approvals, sealed-mode enforcement, evidence capture, and DSSE attestation for every completed run. Three-phase execution: Plan (build execution graph), optional Simulation (dry-run with gates), and Execution (verify plan hash, execute steps, stream logs). Operates offline/air-gapped.
Deterministic task pack execution engine with approvals, sealed-mode enforcement, evidence capture, and DSSE attestation for every completed run. Three-phase execution: Plan (build execution graph), optional Simulation (dry-run with gates), and Execution (verify plan hash, execute steps, stream logs). Operates offline/air-gapped. Consolidated into JobEngine per Sprint 208.
**Dependencies**: Orchestrator, PacksRegistry, Authority, Signer / Attestor, object storage.
**Dependencies**: JobEngine, Authority, Attestor, object storage.
---
@@ -838,20 +819,7 @@ Observability library providing OpenTelemetry-based metrics, traces, and logs wi
Timeline query service providing export, replay, and timeline browsing endpoints for vulnerability history and event streams. Uses shared libraries from `StellaOps.Eventing` for event envelope schemas and `StellaOps.Timeline.Core` for core logic including critical path view.
**Dependencies**: All services (event sources), TimelineIndexer.
---
### TimelineIndexer
- **Source**: `src/TimelineIndexer/`
- **Docs**: [`docs/modules/timeline-indexer/`](./timeline-indexer/)
- **Type**: Service
- **Database**: PostgreSQL (1 SQL migration)
- **Endpoints**: Defined in WebService Program.cs
Timeline event indexing and query service providing fast indexed access to events across all StellaOps services. Receives events from NATS/Valkey streams, indexes them, and provides efficient time-range queries with filtering. Enables vulnerability history browsing, scan timeline analysis, and policy evaluation trail inspection.
**Dependencies**: NATS / Valkey, Timeline.
**Dependencies**: All services (event sources). TimelineIndexer is now consolidated into the Timeline module (`src/Timeline/`).
---
@@ -881,14 +849,14 @@ Structured registry for tracking unresolved components, symbols, and incomplete
---
### Verifier
- **Source**: `src/Verifier/`
- **Docs**: [`docs/modules/verifier/`](./verifier/)
### Verifier (archived -- absorbed into Tools)
- **Source**: `src/Tools/StellaOps.Verifier/`
- **Docs**: [`docs/modules/tools/`](./tools/)
- **Type**: CLI Tool
- **Database**: None
- **Endpoints**: None
Standalone CLI tool for verifying the integrity and authenticity of signed evidence bundles produced by the platform. Validates DSSE envelope signatures, Merkle inclusion proofs, and bundle manifest checksums. Designed for operators and auditors who need independent verification without a full StellaOps installation.
Standalone CLI tool for verifying the integrity and authenticity of signed evidence bundles produced by the platform. Absorbed into `src/Tools/` as of Sprint 212.
**Dependencies**: None (standalone verification).
@@ -921,8 +889,8 @@ VEX consensus viewer and analysis service providing issuer-aware VEX statement e
---
### VulnExplorer
- **Source**: `src/VulnExplorer/`
- **Docs**: [`docs/modules/vuln-explorer/`](./vuln-explorer/)
- **Source**: `src/Findings/StellaOps.VulnExplorer.*` (consolidated into Findings, Sprint 207)
- **Docs**: [`docs-archived/modules/vuln-explorer/`](../../docs-archived/modules/vuln-explorer/)
- **Type**: Service
- **Database**: None (reads from other modules' databases)
- **Endpoints**: Defined in Program.cs

View File

@@ -96,7 +96,7 @@ All context references include `content_hash` and `source_id` enabling verifiabl
- Registered via `AddAdvisoryDeterministicToolset` for reuse across orchestrator, CLI, and services.
- **Orchestration pipeline** — see `orchestration-pipeline.md` for prerequisites, task breakdown, and cross-guild responsibilities before wiring the execution flows.
- **Planned extensions** — NEVRA/EVR comparators, ecosystem-specific normalisers, dependency chain scorers (AIAI-31-003 scope).
- Exposed via internal interfaces to allow orchestrator/toolchain reuse; all helpers stay side-effect free and deterministic for golden testing.
- Exposed via internal interfaces to allow jobengine/toolchain reuse; all helpers stay side-effect free and deterministic for golden testing.
## 6) Output persistence
@@ -165,3 +165,57 @@ All endpoints accept `profile` parameter (default `fips-local`) and return `outp
- **Offline parity.** Local model profiles are the default; remote inference is opt-in and blocked in sealed mode.
See `docs/modules/advisory-ai/chat-interface.md` and `docs-archived/product/advisories/13-Jan-2026 - Controlled Conversational Interface.md`.
## 15) OpsMemory (Operational Memory and RAG)
> Consolidated from `src/OpsMemory/` into `src/AdvisoryAI/` (Sprint 213, 2026-03-04).
> Archived docs: `docs-archived/modules/opsmemory/`.
### Overview
OpsMemory provides a decision ledger for security operations learning. It captures the complete lifecycle of a security decision -- from situation context through action taken to eventual outcome -- enabling playbook suggestions for future similar situations.
### Source layout (post-consolidation)
- **Library:** `src/AdvisoryAI/__Libraries/StellaOps.OpsMemory/` -- core domain: models, similarity vectors, playbook suggestion engine, storage abstractions.
- **WebService:** `src/AdvisoryAI/StellaOps.OpsMemory.WebService/` -- HTTP API (`/api/v1/opsmemory/*`), auth, Swagger, health checks. Deploys as its own container (`opsmemory-web`).
- **Tests:** `src/AdvisoryAI/__Tests/StellaOps.OpsMemory.Tests/` -- unit (similarity vectors, playbook suggestions, context enrichers, chat provider) and integration (Postgres store with Testcontainers).
### Key components
| Component | Purpose |
|-----------|---------|
| `SimilarityVectorGenerator` | 50-dimensional feature vectors from CVE, severity, reachability, EPSS/CVSS, component type, context tags |
| `PlaybookSuggestionService` | Confidence-ranked suggestions from historical decisions |
| `OutcomeTrackingService` | Records decision outcomes for feedback loop |
| `PostgresOpsMemoryStore` | Postgres storage with array-based cosine similarity (no pgvector dependency) |
| `OpsMemoryChatProvider` | Chat integration for conversational playbook queries |
| `OpsMemoryContextEnricher` | Enriches AdvisoryAI context packs with operational memory |
### API surface
| Method | Path | Description |
|--------|------|-------------|
| POST | `/api/v1/opsmemory/decisions` | Record a new decision |
| GET | `/api/v1/opsmemory/decisions/{id}` | Get decision details |
| POST | `/api/v1/opsmemory/decisions/{id}/outcome` | Record outcome |
| GET | `/api/v1/opsmemory/suggestions` | Get playbook suggestions |
| GET | `/api/v1/opsmemory/decisions` | Query past decisions |
| GET | `/api/v1/opsmemory/stats` | Get statistics |
### Database
OpsMemory uses the shared Postgres instance with an `opsmemory` schema. No EF Core migrations -- schema is managed via raw SQL (`CREATE TABLE opsmemory.decisions ...`). Tenant isolation is enforced at the query level.
Connection contract (Sprint 312 remediation):
- Connection resolution precedence: `ConnectionStrings:OpsMemory` -> `ConnectionStrings:Default`.
- In non-development environments, missing DB configuration is a startup error (fail-fast).
- Localhost fallback is limited to development-only workflows.
### Dependencies
- `StellaOps.Findings.Ledger` (upstream library)
- `StellaOps.Auth.ServerIntegration` (authentication)
- `StellaOps.Determinism.Abstractions` (deterministic time/GUID providers)
- `StellaOps.Localization` (i18n)
- AdvisoryAI core references OpsMemory via ProjectReference for context enrichment

View File

@@ -72,4 +72,4 @@ stella promotion preview-gates --promotion <promotion-id> --offline-rekor
- `docs/modules/airgap/README.md`
- `docs/modules/airgap/guides/proof-chain-verification.md`
- `docs/modules/evidence-locker/promotion-evidence-contract.md`
- `docs/modules/release-orchestrator/promotion-runtime-gap-closure-plan.md`
- `docs/modules/release-jobengine/promotion-runtime-gap-closure-plan.md`

View File

@@ -18,7 +18,7 @@ API contains OpenAPI 3.1 specifications for all StellaOps services and governanc
- `policy/openapi.yaml`
- `graph/openapi.yaml`
- `export-center/openapi.yaml`
- `orchestrator/openapi.yaml`
- `jobengine/openapi.yaml`
**Shared Components:**
- `_shared/schemas/` - Common schema definitions

View File

@@ -2660,4 +2660,92 @@ Meter: `StellaOps.Attestor.ProofChain.Receipts.Sidebar`
null/empty/whitespace throws
- DeriveVerificationStatus: single pass, single fail
- Register: null throws
- RegisterContext: null/empty/whitespace bundleId throws
- RegisterContext: null/empty/whitespace bundleId throws
## Advisory Commitments (2026-02-26 Batch)
- `SPRINT_20260226_225_Attestor_signature_trust_and_verdict_api_hardening` governs:
- DSSE signature verifier trust behavior (including deterministic failure reasons).
- authority roster validation for verdict creation.
- authenticated tenant context enforcement over header-only spoofable inputs.
- deterministic verdict retrieval APIs for hash-based lookup.
- Rekor/tile verification commitments from `Deterministic tile verification with Rekor v2` are coordinated with Symbols sprint `SPRINT_20260226_226_Symbols_dsse_rekor_merkle_and_hash_integrity`.
---
## Trust Domain Model (Sprint 204 -- 2026-03-04)
### Overview
As of Sprint 204, the Attestor module directory (`src/Attestor/`) is the trust domain owner for three runtime services and their supporting libraries:
1. **Attestor** -- transparency log submission, inclusion proof verification, evidence caching
2. **Signer** -- DSSE envelope creation, cryptographic signing (keyless/keyful/HSM), entitlement enforcement
3. **Provenance** -- SLSA/DSSE attestation generation, Merkle tree construction, verification tooling
Source consolidation places all trust-domain code under a single directory for ownership clarity, while preserving runtime service identities and security boundaries.
### Trust Data Classification
| Data Category | Owner Service | Storage | Sensitivity |
|---|---|---|---|
| Attestation evidence (proofchain, inclusion proofs, Rekor entries) | Attestor | `attestor` PostgreSQL schema | High -- tamper-evident, integrity-critical |
| Provenance evidence (SLSA predicates, build attestations, Merkle trees) | Provenance (library) | Consumed by Attestor/EvidenceLocker | High -- deterministic, reproducible |
| Signer metadata (audit events, signing ceremony state, rate limits) | Signer | `signer` PostgreSQL schema | High -- operational security |
| Signer key material (KMS/HSM refs, Fulcio certs, trust anchors, rotation state) | Signer (KeyManagement) | `key_management` PostgreSQL schema | Critical -- cryptographic trust root |
### PostgreSQL Schema Ownership
Each trust-domain service retains its own DbContext and dedicated PostgreSQL schema:
- **`attestor` schema** -- Owned by the Attestor service. Contains `entries`, `dedupe`, `audit` tables for transparency log state.
- **`signer` schema** -- Owned by the Signer service. Contains signing ceremony audit, rate limit state, and operational metadata.
- **`key_management` schema** -- Owned by the Signer KeyManagement library. Contains key rotation records, trust anchor configurations, and HSM/KMS binding metadata.
There is **no cross-schema merge**. Each service connects with its own connection string scoped to its own schema.
### Security Boundary: No-Merge Decision (ADR)
**Decision:** Signer key-material isolation from attestation evidence is a deliberate security boundary. The schemas will NOT be merged into a unified DbContext.
**Rationale:**
- A merged DbContext would require a single connection string with access to both key material (signing keys, HSM/KMS bindings, trust anchors) and evidence stores (proofchain entries, Rekor logs).
- This widens the blast radius of any credential compromise: an attacker gaining the Attestor database credential would also gain access to key rotation state and trust anchor configurations.
- Schema isolation is a defense-in-depth measure. Each service authenticates to PostgreSQL independently, with schema-level `GRANT` restrictions.
- The Signer's KeyManagement database contains material that, if compromised, could allow forging of signatures. This material must be isolated from the higher-volume, lower-privilege evidence store.
**Implications:**
- No shared EF Core DbContext across trust services.
- Each service manages its own migrations independently (`src/Attestor/__Libraries/StellaOps.Attestor.Persistence/` for Attestor; `src/Attestor/__Libraries/StellaOps.Signer.KeyManagement/` for Signer key management).
- Cross-service queries (e.g., "find the signing identity for a given attestation entry") use API calls, not database joins.
### Source Layout (post-Sprint 204)
```
src/Attestor/
StellaOps.Attestation/ # DSSE envelope model library
StellaOps.Attestation.Tests/
StellaOps.Attestor/ # Attestor service (Core, Infrastructure, WebService, Tests)
StellaOps.Attestor.Envelope/ # Envelope serialization
StellaOps.Attestor.TileProxy/ # Rekor tile proxy
StellaOps.Attestor.Types/ # Shared predicate types
StellaOps.Attestor.Verify/ # Verification pipeline
StellaOps.Signer/ # Signer service (Core, Infrastructure, WebService, Tests)
StellaOps.Provenance.Attestation/ # Provenance attestation library
StellaOps.Provenance.Attestation.Tool/ # Forensic verification CLI tool
__Libraries/
StellaOps.Attestor.*/ # Attestor domain libraries
StellaOps.Signer.KeyManagement/ # Key rotation and trust anchor management
StellaOps.Signer.Keyless/ # Keyless (Fulcio/Sigstore) signing support
__Tests/
StellaOps.Attestor.*/ # Attestor test projects
StellaOps.Provenance.Attestation.Tests/ # Provenance test project
```
### What Did NOT Change
- **Namespaces** -- All `StellaOps.Signer.*` and `StellaOps.Provenance.*` namespaces are preserved.
- **Runtime service identities** -- Docker image names (`stellaops/signer`), container names, network aliases, and API base paths (`/api/v1/signer/`) are unchanged.
- **Database schemas** -- No schema changes, no migrations, no data movement.
- **API contracts** -- All endpoints including `/api/v1/signer/sign/dsse` remain stable.

View File

@@ -123,3 +123,15 @@ stella bundle verify --bundle light-bundle/ --replay --blob-source https://regis
- `docs/modules/attestor/guides/timestamp-policy.md`
- `docs/modules/attestor/airgap.md`
- `docs/modules/airgap/guides/staleness-and-time.md`
## 8. Deterministic Error Triage Guidance (Sprint 20260226_225)
Use stable error classes to route remediation:
- `signature_untrusted`: key not present in authority roster; refresh roster snapshot and retry.
- `signature_revoked`: signing key revoked; rotate signer and regenerate attestation.
- `tenant_mismatch`: authenticated tenant differs from verdict owner; re-run with correct principal context.
- `verdict_not_found`: no verdict exists for requested hash; verify hash source and storage replication.
Operator rule:
- Do not treat these as transient network faults unless the error class is explicitly retryable.

View File

@@ -429,6 +429,7 @@ The 13-step verification algorithm:
| 0501.6 | Database Schema Implementation | TODO |
| 0501.7 | CLI Integration & Exit Codes | TODO |
| 0501.8 | Key Rotation & Trust Anchors | TODO |
| 20260226_225 | Signature trust + verdict API hardening | DONE |
## Related Documents

View File

@@ -511,3 +511,61 @@ Signer validates that `hash(JWK)` in the proof matches `cnf.jkt` in the token.
2. **Add**: mTLSâ€bound tokens for Signer/Attestor; device code for CLI; optional introspection.
3. **Hardening**: DPoP nonce support; full audit pipeline; HA tuning.
4. **UX**: Tenant/installation admin UI; roleâ†scope editors; client bootstrap wizards.
---
## 21) Identity domain schema ownership
> **ADR: No-merge decision (Sprint 216, 2026-03-04)**
>
> Authority and IssuerDirectory share the same PostgreSQL instance but use **separate schemas and separate DbContext classes**. This is a deliberate security decision, not a consolidation oversight.
### 21.1 AuthorityDbContext (schema: `authority`)
The most security-critical schema in the system. Owns:
| Table/Entity group | Security classification | Content |
| --- | --- | --- |
| Users | **Critical** | Password hashes, MFA state, lockout counters, email verification |
| Sessions | **Critical** | Active session tokens, refresh tokens, device grants |
| Tokens | **Critical** | Issued OpTok metadata, revocation records, jti replay cache |
| Roles & Permissions | **High** | Role-to-scope mappings, audience bindings |
| Clients | **High** | Client registrations, JWK material references, grant type configs |
| Tenants | **High** | Tenant/installation registry, cross-tenant isolation boundaries |
| MFA | **Critical** | TOTP secrets, recovery codes, WebAuthn credentials |
| Audit | **High** | Authentication event log, admin change trail |
**Compiled models:** AuthorityDbContext uses EF Core compiled models (generated by Sprint 219). The `<Compile Remove>` directive for `EfCore/CompiledModels/AuthorityDbContextAssemblyAttributes.cs` lives in `src/Authority/__Libraries/StellaOps.Authority.Persistence/StellaOps.Authority.Persistence.csproj`.
### 21.2 IssuerDirectoryDbContext (schema: `issuer_directory`)
Manages trusted VEX/CSAF publisher metadata. Owns:
| Table/Entity group | Security classification | Content |
| --- | --- | --- |
| Issuers | **Medium** | Publisher identity, display name, homepage, tenant scope |
| Issuer Keys | **Medium** | Public key material (Ed25519, X.509, DSSE), fingerprints, key lifecycle |
| Issuer Audit | **Medium** | CRUD audit trail for issuer metadata changes |
**Compiled models:** IssuerDirectoryDbContext also uses EF Core compiled models. The `<Compile Remove>` directive for `EfCore/CompiledModels/IssuerDirectoryDbContextAssemblyAttributes.cs` lives in `src/Authority/__Libraries/StellaOps.IssuerDirectory.Persistence/StellaOps.IssuerDirectory.Persistence.csproj` (relocated from `src/IssuerDirectory/` by Sprint 216).
### 21.3 No-merge security rationale
**Decision:** Schemas remain permanently separate. No cross-schema DB merge.
**Rationale:**
- AuthorityDbContext manages the most security-sensitive data in the system: password hashes, MFA state, session tokens, refresh tokens, and tenant isolation boundaries.
- A merged DbContext would mean any code path with access to issuer metadata could also reach authentication internals via the same EF Core connection and change tracker.
- The security principle of **least privilege** demands keeping these schemas separate even though they share the same PostgreSQL instance.
- **Blast radius containment**: a vulnerability in issuer metadata handling (e.g., a malformed CSAF publisher import) cannot escalate to credential compromise when the schemas are isolated.
- Each DbContext has its own migration history, compiled models, and connection pooling, enabling independent security hardening.
### 21.4 IssuerDirectory domain ownership
As of Sprint 216, the IssuerDirectory source tree is owned by the Authority domain:
- Source: `src/Authority/StellaOps.IssuerDirectory/` (service projects)
- Persistence: `src/Authority/__Libraries/StellaOps.IssuerDirectory.Persistence/`
- Tests: `src/Authority/__Tests/StellaOps.IssuerDirectory.Persistence.Tests/`
- Client library: `src/Authority/__Libraries/StellaOps.IssuerDirectory.Client/` (shared with Excititor, DeltaVerdict)
- Solution: included in `src/Authority/StellaOps.Authority.sln`
- Runtime identity: unchanged (separate container, separate endpoints, separate schema)

View File

@@ -1,47 +0,0 @@
# Bench (Performance Benchmarks)
**Status:** Implemented
**Source:** `src/Bench/`
**Owner:** Platform Team
> **Note:** This folder documents **performance benchmarks**. For **competitive benchmarking** (accuracy comparison with other scanners), see [`../benchmark/`](../benchmark/).
## Purpose
Bench provides performance benchmark infrastructure for StellaOps modules. Measures throughput, latency, and resource usage to detect regressions and validate performance targets.
## Components
**Benchmark Projects:**
- `StellaOps.Bench.LinkNotMerge` - Link-Not-Merge correlation performance
- `StellaOps.Bench.LinkNotMerge.Vex` - LNM VEX statement performance
- `StellaOps.Bench.Notify` - Notification delivery throughput
- `StellaOps.Bench.PolicyEngine` - Policy evaluation performance
- `StellaOps.Bench.ScannerAnalyzers` - Language analyzer performance
## Scanner Vendor Parity Tracking
`StellaOps.Bench.ScannerAnalyzers` now supports vendor parity tracking for offline benchmark runs:
- Scenario-level vendor ingestion from JSON or SARIF artifacts (`vendorResults[]` in benchmark config).
- Optional Stella finding ingestion (`stellaFindingsPath`) for exact overlap comparisons.
- Deterministic parity outputs in benchmark JSON and Prometheus exports:
- overlap counts and percentages
- scanner-only / vendor-only counts
- parity score (Jaccard-style overlap over union)
## Usage
```bash
# Run all benchmarks
dotnet run -c Release --project src/Bench/StellaOps.Bench/LinkNotMerge/StellaOps.Bench.LinkNotMerge
# Run with specific runtime
dotnet run -c Release --project src/Bench/StellaOps.Bench/Notify/StellaOps.Bench.Notify
```
## Related Documentation
- Competitive Benchmark: `../benchmark/architecture.md`
- Scanner: `../scanner/architecture.md`
- Policy: `../policy/architecture.md`
- Notify: `../notify/architecture.md`

View File

@@ -1772,6 +1772,49 @@ inside `AddNormalizationPipelines()` in `ServiceCollectionExtensions.cs`.
---
*Document Version: 1.5.0*
*Last Updated: 2026-02-12*
## Symbols (Debug Symbol Resolution)
> Absorbed from `src/Symbols/` into `src/BinaryIndex/` per Sprint 202 (2026-03-04).
> Project names and namespaces remain `StellaOps.Symbols.*` to avoid serialized type name breakage.
### Overview
The Symbols subsystem provides debug symbol storage, resolution, and marketplace functionality. It is the primary data source for BinaryIndex.DeltaSig when resolving function-level identifiers in stripped binaries.
### Project Structure
| Project | Location | Role |
|---------|----------|------|
| `StellaOps.Symbols.Core` | `__Libraries/StellaOps.Symbols.Core/` | Leaf library: models, abstractions (`ISymbolRepository`, `ISymbolResolver`), hashing |
| `StellaOps.Symbols.Client` | `__Libraries/StellaOps.Symbols.Client/` | HTTP client for Symbols.Server API (depends on Core) |
| `StellaOps.Symbols.Infrastructure` | `__Libraries/StellaOps.Symbols.Infrastructure/` | In-memory and persistent storage, Blake3 hashing (depends on Core) |
| `StellaOps.Symbols.Marketplace` | `__Libraries/StellaOps.Symbols.Marketplace/` | Marketplace scoring and catalog (leaf) |
| `StellaOps.Symbols.Bundle` | `__Libraries/StellaOps.Symbols.Bundle/` | Deterministic symbol bundles for air-gapped installs with DSSE manifests (depends on Core) |
| `StellaOps.Symbols.Server` | `StellaOps.Symbols.Server/` | Deployable ASP.NET Core WebService (depends on Core, Infrastructure, Marketplace) |
| `StellaOps.Symbols.Tests` | `__Tests/StellaOps.Symbols.Tests/` | Test project covering all Symbols libraries |
### Symbols.Server API Surface
The server exposes REST endpoints on port 8080 (mapped to `127.1.0.38:80` in compose):
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/health` | Anonymous | Health check |
| POST | `/v1/symbols/manifests` | `symbols:write` | Upload symbol manifest |
| GET | `/v1/symbols/manifests/{manifestId}` | `symbols:read` | Get manifest by ID |
| GET | `/v1/symbols/manifests` | `symbols:read` | Query manifests (filter by debugId, codeId, binaryName, platform) |
| POST | `/v1/symbols/resolve` | `symbols:read` | Batch-resolve symbol addresses |
| GET | `/v1/symbols/by-debug-id/{debugId}` | `symbols:read` | Get manifests by debug ID |
Additional marketplace endpoints are mapped via `app.MapSymbolSourceEndpoints()`.
### Consumers
- **BinaryIndex.DeltaSig** (`__Libraries/StellaOps.BinaryIndex.DeltaSig/`): References `Symbols.Core` for symbol resolution during delta signature generation.
- **Cli.Plugins.Symbols** (`src/Cli/__Libraries/StellaOps.Cli.Plugins.Symbols/`): References `Symbols.Core` and `Symbols.Client` for CLI symbol ingestion commands.
---
*Document Version: 1.6.0*
*Last Updated: 2026-03-04*

View File

@@ -694,7 +694,13 @@ the registry's Referrers API. `--offline` returns simulated data for testing.
### 21.4 Implementation
- `EvidenceReferrerCommands.cs` static command builder class following existing pattern
- Wired into `EvidenceCommandGroup.BuildEvidenceCommand()` alongside existing sub-commands
- Reuses `IOciRegistryClient` and OCI models from `StellaOps.Cli.Services`
- 25 unit tests in `EvidenceReferrerCommandTests.cs`
- `EvidenceReferrerCommands.cs` static command builder class following existing pattern
- Wired into `EvidenceCommandGroup.BuildEvidenceCommand()` alongside existing sub-commands
- Reuses `IOciRegistryClient` and OCI models from `StellaOps.Cli.Services`
- 25 unit tests in `EvidenceReferrerCommandTests.cs`
## 22) Advisory Commitments (2026-02-26 Batch)
- `SPRINT_20260226_222_Cli_proof_chain_verification_and_replay_parity` delivers cryptographic verification-first command behavior for `chain`, `bundle`, `sbom`, `timeline`, and `replay` flows.
- `SPRINT_20260226_223_Platform_score_explain_contract_and_replay_alignment` aligns CLI score replay calls with deterministic Platform explain/history contracts.
- `SPRINT_20260226_229_DOCS_advisory_hygiene_dedup_and_archival_translation` tracks advisory translation and archival state for this batch.

View File

@@ -154,3 +154,16 @@ The script should emit a parity report that feeds into the Downloads workspace (
*Last updated: 2026-01-20 (Sprint 20260120).*
## 12. 2026-02-26 Batch Parity Update
Aligned sprints:
- `SPRINT_20260226_222_Cli_proof_chain_verification_and_replay_parity`
- `SPRINT_20260226_227_FE_triage_risk_score_widget_wiring_and_parity`
Parity outcomes in this batch:
- CLI proof verification flows now align with deterministic verification contracts used by UI evidence and score surfaces.
- UI risk and score widgets are covered by active E2E suites and no longer tracked as skipped test debt.
- Replay/score explain integration paths use the same deterministic error semantics across CLI and UI consumers.

View File

@@ -59,6 +59,11 @@ The command performs the following verification checks:
4. **Tool Version**: Verifies tool version metadata is present and valid.
5. **Timestamp Validity**: Checks generation timestamp is within acceptable window.
### 2026-02-26 parity note
- `stella sbom verify` now follows verification-first behavior and no longer relies on structural placeholder checks.
- Deterministic failure reasons are surfaced for missing trust roots, malformed signatures, and verification mismatch paths.
### Exit Codes
| Code | Meaning |

View File

@@ -146,6 +146,11 @@ stella scan replay \
--policy "sha256:policy321..."
```
## 2026-02-26 parity note
- Replay commands in UI and evidence exports are backend-generated and should be executed without placeholder edits.
- `scan replay`, `timeline query/export`, and score explain/replay flows are aligned with deterministic backend contracts and error taxonomy.
## Related Commands
| Command | Description |

View File

@@ -32,3 +32,8 @@ stella task-runner simulate --output table
## Observability signals
- When tracing headers are present (`traceparent`), CLI propagates them; otherwise it emits new span IDs only in verbose logs.
- Metrics are not emitted by the CLI itself; servers capture request telemetry and can be correlated via the returned correlation/trace IDs printed on errors in verbose mode.
## 2026-02-26 proof/replay contract note
- Proof verification surfaces (`chain verify`, `bundle verify`, `sbom verify`, `witness verify`) emit deterministic error bodies and stable non-zero exit behavior when cryptographic checks fail.
- Score explain/replay and scan replay flows avoid synthetic fallback payloads and return explicit contract errors for missing or malformed backend responses.

View File

@@ -273,7 +273,7 @@ public interface IFeedConnector {
* **Fetch**: windowed (cursor), conditional GET (ETag/LastModified), retry/backoff, rate limiting.
* **Parse**: schema validation (JSON Schema, XSD/CSAF), content type checks; write **DTO** with normalized casing.
* **Map**: build canonical records; all outputs carry **provenance** (doc digest, URI, anchors). KEV references use `reference` provenance anchored to the catalog search URL.
* **Map**: build canonical records; all outputs carry **provenance** (doc digest, URI, anchors). KEV references use `reference` provenance anchored to the catalog search URL.
### 4.2 Version range normalization
@@ -631,3 +631,29 @@ concelier:
- Advisory evidence attestation parameters and path rules are documented in `docs/modules/concelier/attestation.md`.
4. **Scale & diagnostics**: provider dashboards, staleness alerts, export cache reuse.
5. **Offline kit**: endtoend verified bundles for airgap.
---
## ADR: Advisory Domain Source Consolidation (Sprint 203, 2026-03-04)
### Decision
Absorb `src/Feedser/` (4 projects) and `src/Excititor/` (38+ projects) into `src/Concelier/` as a **source-only consolidation**. No namespace renames. No database schema merge. No service identity changes.
### Context
The advisory domain spans three service-level source directories (Concelier, Feedser, Excititor) that all contribute to the same logical pipeline: raw advisory ingestion, proof evidence generation, and VEX observation correlation. Keeping them as separate top-level directories created confusion about domain ownership and complicated cross-module reference tracking for 17+ dependent projects.
### Rationale for no DB merge
All three DbContexts (`ConcelierDbContext`, `ExcititorDbContext`, `ProofServiceDbContext`) connect to the same PostgreSQL database (`stellaops_platform`) but own distinct schemas (`vuln`/`concelier`, `vex`/`excititor`, `vuln`/`feedser`). The 49 entities across 5 schemas have distinct write lifecycles (raw ingestion vs. proof generation vs. VEX processing). Merging DbContexts would couple unrelated write patterns for zero operational benefit. Schema isolation is a feature.
### Consequences
- `src/Concelier/` is now the single domain root for all advisory-related source code.
- Feedser projects live at `src/Concelier/StellaOps.Feedser.*` and `src/Concelier/__Tests/StellaOps.Feedser.*`.
- Excititor projects live at `src/Concelier/StellaOps.Excititor.*`, `src/Concelier/__Libraries/StellaOps.Excititor.*`, and `src/Concelier/__Tests/StellaOps.Excititor.*`.
- Runtime service identities are unchanged: Excititor WebService and Worker deploy as separate containers with the same Docker image names and HTTP paths.
- Deployment boundary is frozen: Concelier and Excititor remain independently deployable services.
- CI path-filters updated: `excititor` section replaced with comment pointing to `concelier` paths.
- `src/Feedser/` and `src/Excititor/` top-level directories have been deleted.

View File

@@ -6,7 +6,7 @@ This prep note was consolidated into the current Concelier and Orchestrator docu
- `docs/modules/concelier/architecture.md`
- `docs/modules/concelier/connectors.md`
- `docs/modules/concelier/operations/authority-audit-runbook.md`
- `docs/modules/orchestrator/architecture.md`
- `docs/modules/jobengine/architecture.md`
## Scope
- Registry/control-plane assumptions for ingestion scheduling.

View File

@@ -1,40 +0,0 @@
# DevPortal
> Developer portal for API documentation and SDK access.
## Purpose
DevPortal provides a unified developer experience for StellaOps API consumers. It hosts API documentation, SDK downloads, and developer guides.
## Quick Links
- [Guides](./guides/) - Developer guides and tutorials
## Status
| Attribute | Value |
|-----------|-------|
| **Maturity** | Beta |
| **Last Reviewed** | 2025-12-29 |
| **Maintainer** | Platform Guild |
## Key Features
- **API Documentation**: Interactive OpenAPI documentation
- **SDK Downloads**: Language-specific SDK packages
- **Developer Guides**: Integration tutorials and examples
- **API Playground**: Interactive API testing environment
## Dependencies
### Upstream (this module depends on)
- **Authority** - Developer authentication
- **Gateway** - API proxy and rate limiting
### Downstream (modules that depend on this)
- None (consumer-facing portal)
## Related Documentation
- [API Overview](../../api/overview.md)
- [CLI Reference](../../cli/command-reference.md)

View File

@@ -1,80 +0,0 @@
# Developer Portal Publishing Guide
Last updated: 2025-11-25
## Goals
- Publish the StellaOps Developer Portal consistently across connected and air-gapped environments.
- Produce deterministic artefacts (checksums, manifests) so releases are auditable and reproducible.
- Keep docs, API specs, and examples in sync with the CI pipelines that build the portal.
## Prerequisites
- Node.js 20.x + pnpm 9.x
- Docker / Podman (for static-site container image)
- Spectral lint baseline from `src/Api/StellaOps.Api.OpenApi` (optional, to embed OAS links)
- Access to `local-nugets/` cache and offline asset bundle (see Offline section)
## Build & Test (connected)
```bash
pnpm install --frozen-lockfile
pnpm lint # markdownlint/prettier/eslint as configured
pnpm build # generates static site into dist/
pnpm test # component/unit tests if configured
```
- Determinism: ensure `pnpm-lock.yaml` is committed; no timestamps in emitted HTML (set `SOURCE_DATE_EPOCH` if needed).
## Publish (connected)
1. Build the static site: `pnpm build` (or reuse CI artifact).
2. Create artefact bundle:
```bash
tar -C dist -czf out/devportal/site.tar.gz .
sha256sum out/devportal/site.tar.gz > out/devportal/site.tar.gz.sha256
```
3. Container image (optional):
```bash
docker build -t registry.example.com/stella/devportal:${VERSION} -f ops/devportal/Dockerfile .
docker push registry.example.com/stella/devportal:${VERSION}
```
4. Record manifest `out/devportal/manifest.json`:
```json
{
"version": "${VERSION}",
"checksum": "$(cat out/devportal/site.tar.gz.sha256 | awk '{print $1}')",
"build": {
"node": "20.x",
"pnpm": "9.x"
},
"timestamp": "${UTC_ISO8601}",
"source_commit": "$(git rev-parse HEAD)"
}
```
## Offline / Air-gap
- Use pre-seeded bundle `offline/devportal/site.tar.gz` with accompanying `.sha256` and `manifest.json`.
- Verify before use:
```bash
sha256sum -c offline/devportal/site.tar.gz.sha256
```
- Serve locally:
```bash
mkdir -p /srv/devportal && tar -C /srv/devportal -xzf offline/devportal/site.tar.gz
# then point nginx/caddy to /srv/devportal
```
- No external CDN references allowed; ensure assets are bundled and CSP is self-contained.
## Deployment targets
- **Kubernetes**: use the static-site container image with a read-only root filesystem; expose via ingress with TLS; set `ETAG`/`Last-Modified` headers from manifest.
- **Docker Compose**: mount `site.tar.gz` into a lightweight nginx container; sample compose snippet lives in `ops/deployment/devportal/docker-compose.devportal.yml` (to be authored alongside this doc).
- **File share**: extract bundle onto shared storage for disconnected viewing; keep manifest + checksum adjacent.
## Checks & Observability
- Lint/OAS links: run `pnpm lint` and optional `pnpm api:check` (if wired) to ensure embedded API links resolve.
- Availability: configure basic `/healthz` (static 200) and enable access logging at the reverse proxy.
- Integrity: serve checksums/manifest from `/meta` path for auditors; include build `source_commit` and `timestamp`.
## Release checklist
- [ ] `pnpm build` succeeds reproducibly.
- [ ] `site.tar.gz` + `.sha256` generated and verified.
- [ ] `manifest.json` populated with version, checksum, UTC timestamp, commit SHA.
- [ ] Offline bundle placed in `offline/devportal/` with checksums.
- [ ] Image (if used) pushed to registry and noted in release notes.
- [ ] Deployment target (K8s/Compose/File share) instructions updated if changed.

View File

@@ -92,4 +92,4 @@ key order and UTC timestamps.
- EvidenceLocker architecture: `docs/modules/evidence-locker/architecture.md`
- EvidenceLocker attestation contract: `docs/modules/evidence-locker/attestation-contract.md`
- Policy ownership contract: `docs/modules/policy/promotion-gate-ownership-contract.md`
- Release Orchestrator runtime gap plan: `docs/modules/release-orchestrator/promotion-runtime-gap-closure-plan.md`
- Release Orchestrator runtime gap plan: `docs/modules/release-jobengine/promotion-runtime-gap-closure-plan.md`

View File

@@ -1,4 +1,6 @@
# StellaOps Excititor
# StellaOps Excititor (Archived -- absorbed into Concelier domain, Sprint 203)
> **Note:** Excititor source code has been moved to `src/Concelier/StellaOps.Excititor.*` as part of the advisory domain consolidation (Sprint 203, 2026-03-04). This documentation is kept as a redirect. Full archive at `docs-archived/modules/excititor/`. The ADR is recorded in `docs/modules/concelier/architecture.md`.
Excititor converts heterogeneous VEX feeds into raw observations and linksets that honour the Aggregation-Only Contract.

View File

@@ -1,40 +0,0 @@
# Extensions (IDE Plugins)
> IDE integration plugins for Stella Ops, enabling release management and configuration validation from within VS Code and JetBrains IDEs.
## Purpose
Provides IDE integration for Stella Ops via VS Code and JetBrains plugins, allowing developers to manage releases, view environments, and validate configurations without leaving their editor. Extensions act as thin clients consuming existing Orchestrator and Router APIs, bringing operational visibility directly into the development workflow.
## Quick Links
- [Architecture](./architecture.md) - Technical design and implementation details
## Status
| Attribute | Value |
|-----------|-------|
| **Maturity** | Beta |
| **Source** | `src/Extensions/` |
## Key Features
- **VS Code extension:** Tree views for releases and environments, CodeLens annotations for `stella.yaml` files, command palette integration, status bar widgets
- **JetBrains plugin:** Tool windows with Releases/Environments/Deployments tabs, YAML annotator for configuration validation, status bar integration, action menus
- **Unified configuration:** Both plugins share the same Orchestrator API surface and authentication flow
- **Real-time updates:** Live status refresh for release pipelines and environment health
## Dependencies
### Upstream (this module depends on)
- **Orchestrator** - Release state, pipeline status, and environment data via HTTP API
- **Authority** - OAuth token-based authentication and scope enforcement
### Downstream (modules that depend on this)
- None (end-user development tools; no other modules consume Extensions)
## Related Documentation
- [Orchestrator](../orchestrator/) - Backend API consumed by extensions
- [Authority](../authority/) - Authentication provider
- [CLI](../cli/) - Command-line alternative for the same operations

View File

@@ -1,117 +0,0 @@
# Extensions (IDE Plugins) Architecture
> Technical architecture for VS Code and JetBrains IDE plugins providing Stella Ops integration.
## Overview
The Extensions module consists of two independent IDE plugins that provide developer-facing integration with the Stella Ops platform. Both plugins are pure HTTP clients that consume the Orchestrator and Router APIs; they do not host any services, expose endpoints, or maintain local databases. Authentication is handled through OAuth tokens obtained from the Authority service.
## Design Principles
1. **Thin client** - Extensions contain no business logic; all state and decisions live in backend services
2. **Consistent experience** - Both plugins expose equivalent functionality despite different technology stacks
3. **Non-blocking** - All API calls are asynchronous; the IDE remains responsive during network operations
4. **Offline-tolerant** - Graceful degradation when the Stella Ops backend is unreachable
## Components
```
Extensions/
├── vscode-stella-ops/ # VS Code extension (TypeScript)
│ ├── src/
│ │ ├── extension.ts # Entry point and activation
│ │ ├── providers/
│ │ │ ├── ReleaseTreeProvider.ts # TreeView: releases
│ │ │ ├── EnvironmentTreeProvider.ts# TreeView: environments
│ │ │ └── CodeLensProvider.ts # CodeLens for stella.yaml
│ │ ├── commands/ # Command palette handlers
│ │ ├── views/
│ │ │ └── webview/ # Webview panels (detail views)
│ │ ├── statusbar/
│ │ │ └── StatusBarManager.ts # Status bar integration
│ │ └── api/
│ │ └── OrchestratorClient.ts # HTTP client for Orchestrator API
│ ├── package.json # Extension manifest
│ └── tsconfig.json
└── jetbrains-stella-ops/ # JetBrains plugin (Kotlin)
├── src/main/kotlin/
│ ├── toolwindow/
│ │ ├── ReleasesToolWindow.kt # Tool window: Releases tab
│ │ ├── EnvironmentsToolWindow.kt # Tool window: Environments tab
│ │ └── DeploymentsToolWindow.kt # Tool window: Deployments tab
│ ├── annotator/
│ │ └── StellaYamlAnnotator.kt # YAML file annotator
│ ├── actions/ # Action menu handlers
│ ├── statusbar/
│ │ └── StellaStatusBarWidget.kt # Status bar widget
│ └── api/
│ └── OrchestratorClient.kt # HTTP client for Orchestrator API
├── src/main/resources/
│ └── META-INF/plugin.xml # Plugin descriptor
└── build.gradle.kts
```
## Data Flow
```
[Developer IDE] --> [Extension/Plugin]
├── GET /api/v1/releases/* ──────> [Orchestrator API]
├── GET /api/v1/environments/* ──> [Orchestrator API]
├── POST /api/v1/promotions/* ──-> [Orchestrator API]
└── POST /oauth/token ──────────-> [Authority]
```
1. **Authentication:** On activation, the extension initiates an OAuth device-code or browser-redirect flow against Authority. The obtained access token is stored in the IDE's secure credential store (VS Code `SecretStorage`, JetBrains `PasswordSafe`).
2. **Data retrieval:** Tree views and tool windows issue HTTP GET requests to the Orchestrator API on initial load and on manual/timed refresh.
3. **Actions:** Approve/reject/promote commands issue HTTP POST requests to the Orchestrator release control endpoints.
4. **Configuration validation:** The CodeLens provider (VS Code) and YAML annotator (JetBrains) parse `stella.yaml` files locally and highlight configuration issues inline.
## VS Code Extension Details
### Tree Views
- **Releases:** Hierarchical view of releases grouped by environment, showing status, version, and promotion eligibility
- **Environments:** Flat list of configured environments with health indicators
### CodeLens
- Inline annotations above `stella.yaml` entries showing the current deployment status of the referenced release
- Click-to-promote actions directly from the YAML file
### Status Bar
- Compact widget showing the number of pending promotions and overall platform health
### Webview Panels
- Detail panels for release timelines, evidence summaries, and deployment logs
## JetBrains Plugin Details
### Tool Windows
- **Releases tab:** Table view of all releases with sortable columns (version, environment, status, timestamp)
- **Environments tab:** Environment cards with health status and current deployments
- **Deployments tab:** Active and recent deployment history with log links
### YAML Annotator
- Real-time validation of `stella.yaml` files with gutter icons and tooltip messages for configuration issues
### Action Menus
- Context-sensitive actions (promote, approve, reject) available from tool window rows and editor context menus
## Security Considerations
- **Token storage:** OAuth tokens are stored exclusively in the IDE's built-in secure credential store; never persisted to disk in plaintext
- **Scope enforcement:** Extensions request only the scopes necessary for read operations and promotions (`release:read`, `release:promote`, `env:read`)
- **TLS enforcement:** All HTTP communication uses HTTPS; certificate validation is not bypassed
- **No secrets in configuration:** The `stella.yaml` file contains no credentials; integration secrets are managed by the Authority and Integrations modules
## Performance Characteristics
- Tree view refresh is debounced to avoid excessive API calls (default: 30-second minimum interval)
- API responses are cached locally with short TTL (60 seconds) to reduce latency on repeated navigation
- Webview panels and tool windows load data lazily on first open
## References
- [Module README](./README.md)
- [Orchestrator Architecture](../orchestrator/architecture.md)
- [Authority Architecture](../authority/architecture.md)

View File

@@ -1,4 +1,6 @@
# Feedser
# Feedser (Archived -- absorbed into Concelier domain, Sprint 203)
> **Note:** Feedser source code has been moved to `src/Concelier/StellaOps.Feedser.*` as part of the advisory domain consolidation (Sprint 203, 2026-03-04). This documentation is kept as a redirect. Full archive at `docs-archived/modules/feedser/`.
> Evidence collection library for backport detection and binary fingerprinting.

View File

@@ -9,6 +9,16 @@ Immutable, append-only event ledger for tracking vulnerability findings, policy
- **Merkle anchoring**: Event chains are Merkle-linked for tamper-evident verification.
- **Tenant isolation**: All events are partitioned by tenant with cross-tenant access forbidden.
## Consolidated modules (Sprint 207)
The `src/Findings/` directory is the unified home for all findings-related services:
- **Findings Ledger** (`StellaOps.Findings.Ledger`, `StellaOps.Findings.Ledger.WebService`): Core append-only event ledger.
- **RiskEngine** (`StellaOps.RiskEngine.Core`, `StellaOps.RiskEngine.WebService`, `StellaOps.RiskEngine.Worker`): Computes risk scores using CVSS, EPSS, KEV, exploit maturity, fix-chain attestation, and VEX gates. Infrastructure lives under `__Libraries/StellaOps.RiskEngine.Infrastructure`.
- **VulnExplorer** (`StellaOps.VulnExplorer.Api`): API surface for browsing findings, evidence subgraphs, triage workflows, and VEX decision management. Shared contracts from `StellaOps.VulnExplorer.WebService`.
Previously archived docs for RiskEngine and VulnExplorer are in `docs-archived/modules/risk-engine/` and `docs-archived/modules/vuln-explorer/`.
## Quick links
- FL1FL10 remediation tracker: `gaps-FL1-FL10.md`
- Implementation plan: `implementation_plan.md`

View File

@@ -1,49 +0,0 @@
# Gateway
**Status:** Implemented
**Source:** `src/Gateway/`
**Owner:** Platform Team
## Purpose
Gateway provides API routing, authentication enforcement, and transport abstraction for StellaOps services. Acts as the single entry point for external clients with support for HTTP/HTTPS and transport-agnostic messaging via Router module.
## Components
**Services:**
- `StellaOps.Gateway.WebService` - API gateway with routing, middleware, and security
**Key Features:**
- Route configuration and service discovery
- Authorization middleware (Authority integration)
- Request/response transformation
- Rate limiting and throttling
- Transport abstraction (HTTP, TCP/TLS, UDP, RabbitMQ, Valkey)
## Configuration
See `etc/policy-gateway.yaml.sample` for gateway configuration examples.
Key settings:
- Service route mappings
- Authority issuer and audience configuration
- Transport protocols and endpoints
- Security policies and CORS settings
- Rate limiting rules
## Dependencies
- Authority (authentication and authorization)
- Router (transport-agnostic messaging)
- All backend services (routing targets)
## Related Documentation
- Architecture: `./architecture.md`
- Router Module: `../router/`
- Authority Module: `../authority/`
- API Reference: `../../API_CLI_REFERENCE.md`
## Current Status
Implemented with HTTP/HTTPS support. Integrated with Authority for token validation and authorization. Supports service routing and middleware composition.

View File

@@ -1,568 +0,0 @@
# component_architecture_gateway.md — **Stella Ops Gateway** (Sprint 3600)
> Derived from Reference Architecture Advisory and Router Architecture Specification
> **Dual-location clarification (updated 2026-02-22).** Both `src/Gateway/` and `src/Router/` contain a project named `StellaOps.Gateway.WebService`. They are **different implementations** serving complementary roles:
> - **`src/Gateway/`** (this module) — the simplified HTTP ingress gateway focused on authentication, routing to microservices via binary protocol, and OpenAPI aggregation.
> - **`src/Router/`** — the evolved "Front Door" gateway with advanced features: configurable route tables (`GatewayRouteCatalog`), reverse proxy, SPA hosting, WebSocket support, Valkey messaging transport, and extended Authority integration.
>
> The Router version (`src/Router/`) appears to be the current canonical deployment target. This Gateway version may represent a simplified or legacy configuration. Operators should verify which is deployed in their environment. See also [Router Architecture](../router/architecture.md).
> **Scope.** The Gateway WebService is the single HTTP ingress point for all external traffic. It authenticates requests via Authority (DPoP/mTLS), routes to microservices via the Router binary protocol, aggregates OpenAPI specifications, and enforces tenant isolation.
> **Ownership:** Platform Guild
---
## 0) Mission & Boundaries
### What Gateway Does
- **HTTP Ingress**: Single entry point for all external HTTP/HTTPS traffic
- **Authentication**: DPoP and mTLS token validation via Authority integration
- **Routing**: Routes HTTP requests to microservices via binary protocol (TCP/TLS)
- **OpenAPI Aggregation**: Combines endpoint specs from all registered microservices
- **Health Aggregation**: Provides unified health status from downstream services
- **Rate Limiting**: Per-tenant and per-identity request throttling
- **Tenant Propagation**: Extracts tenant context and propagates to microservices
### What Gateway Does NOT Do
- **Business Logic**: No domain logic; pure routing and auth
- **Data Storage**: Stateless; no persistent state beyond connection cache
- **Direct Database Access**: Never connects to PostgreSQL directly
- **SBOM/VEX Processing**: Delegates to Scanner, Excititor, etc.
---
## 1) Solution & Project Layout
```
src/Gateway/
├── StellaOps.Gateway.WebService/
│ ├── StellaOps.Gateway.WebService.csproj
│ ├── Program.cs # DI bootstrap, transport init
│ ├── Dockerfile
│ ├── appsettings.json
│ ├── appsettings.Development.json
│ ├── Configuration/
│ │ ├── GatewayOptions.cs # All configuration options
│ │ └── TransportOptions.cs # TCP/TLS transport config
│ ├── Middleware/
│ │ ├── TenantMiddleware.cs # Tenant context extraction
│ │ ├── RequestRoutingMiddleware.cs # HTTP → binary routing
│ │ ├── SenderConstraintMiddleware.cs # DPoP/mTLS validation
│ │ ├── IdentityHeaderPolicyMiddleware.cs # Identity header sanitization
│ │ ├── CorrelationIdMiddleware.cs # Request correlation
│ │ └── HealthCheckMiddleware.cs # Health probe handling
│ ├── Services/
│ │ ├── GatewayHostedService.cs # Transport lifecycle
│ │ ├── OpenApiAggregationService.cs # Spec aggregation
│ │ └── HealthAggregationService.cs # Downstream health
│ └── Endpoints/
│ ├── HealthEndpoints.cs # /health/*, /metrics
│ └── OpenApiEndpoints.cs # /openapi.json, /openapi.yaml
```
### Dependencies
```xml
<ItemGroup>
<ProjectReference Include="..\..\__Libraries\StellaOps.Router.Gateway\..." />
<ProjectReference Include="..\..\__Libraries\StellaOps.Router.Transport.Tcp\..." />
<ProjectReference Include="..\..\__Libraries\StellaOps.Router.Transport.Tls\..." />
<ProjectReference Include="..\..\Auth\StellaOps.Auth.ServerIntegration\..." />
</ItemGroup>
```
---
## 2) External Dependencies
| Dependency | Purpose | Required |
|------------|---------|----------|
| **Authority** | OpTok validation, DPoP/mTLS | Yes |
| **Router.Gateway** | Routing state, endpoint discovery | Yes |
| **Router.Transport.Tcp** | Binary transport (dev) | Yes |
| **Router.Transport.Tls** | Binary transport (prod) | Yes |
| **Valkey/Redis** | Rate limiting state | Optional |
---
## 3) Contracts & Data Model
### Request Flow
```
┌──────────────┐ HTTPS ┌─────────────────┐ Binary ┌─────────────────┐
│ Client │ ─────────────► │ Gateway │ ────────────► │ Microservice │
│ (CLI/UI) │ │ WebService │ Frame │ (Scanner, │
│ │ ◄───────────── │ │ ◄──────────── │ Policy, etc) │
└──────────────┘ HTTPS └─────────────────┘ Binary └─────────────────┘
```
### Binary Frame Protocol
Gateway uses the Router binary protocol for internal communication:
| Frame Type | Purpose |
|------------|---------|
| HELLO | Microservice registration with endpoints |
| HEARTBEAT | Health check and latency measurement |
| REQUEST | HTTP request serialized to binary |
| RESPONSE | HTTP response serialized from binary |
| STREAM_DATA | Streaming response chunks |
| CANCEL | Request cancellation propagation |
### Endpoint Descriptor
```csharp
public sealed class EndpointDescriptor
{
public required string Method { get; init; } // GET, POST, etc.
public required string Path { get; init; } // /api/v1/scans/{id}
public required string ServiceName { get; init; } // scanner
public required string Version { get; init; } // 1.0.0
public TimeSpan DefaultTimeout { get; init; } // 30s
public bool SupportsStreaming { get; init; } // true for large responses
public IReadOnlyList<ClaimRequirement> RequiringClaims { get; init; }
}
```
### Routing State
```csharp
public interface IRoutingStateManager
{
ValueTask RegisterEndpointsAsync(ConnectionState conn, HelloPayload hello);
ValueTask<InstanceSelection?> SelectInstanceAsync(string method, string path);
ValueTask UpdateHealthAsync(ConnectionState conn, HeartbeatPayload heartbeat);
ValueTask DrainConnectionAsync(string connectionId);
}
```
---
## 4) REST API
Gateway exposes minimal management endpoints; all business APIs are routed to microservices.
### Health Endpoints
| Endpoint | Auth | Description |
|----------|------|-------------|
| `GET /health/live` | None | Liveness probe |
| `GET /health/ready` | None | Readiness probe |
| `GET /health/startup` | None | Startup probe |
| `GET /metrics` | None | Prometheus metrics |
### OpenAPI Endpoints
| Endpoint | Auth | Description |
|----------|------|-------------|
| `GET /openapi.json` | None | Aggregated OpenAPI 3.1.0 spec |
| `GET /openapi.yaml` | None | YAML format spec |
---
## 5) Execution Flow
### Request Routing
```mermaid
sequenceDiagram
participant C as Client
participant G as Gateway
participant A as Authority
participant M as Microservice
C->>G: HTTPS Request + DPoP Token
G->>A: Validate Token
A-->>G: Claims (sub, tid, scope)
G->>G: Select Instance (Method, Path)
G->>M: Binary REQUEST Frame
M-->>G: Binary RESPONSE Frame
G-->>C: HTTPS Response
```
### Microservice Registration
```mermaid
sequenceDiagram
participant M as Microservice
participant G as Gateway
M->>G: TCP/TLS Connect
M->>G: HELLO (ServiceName, Version, Endpoints)
G->>G: Register Endpoints
G-->>M: HELLO ACK
loop Every 10s
G->>M: HEARTBEAT
M-->>G: HEARTBEAT (latency, health)
G->>G: Update Health State
end
```
---
## 6) Instance Selection Algorithm
```csharp
public ValueTask<InstanceSelection?> SelectInstanceAsync(string method, string path)
{
// 1. Find all endpoints matching (method, path)
var candidates = _endpoints
.Where(e => e.Method == method && MatchPath(e.Path, path))
.ToList();
// 2. Filter by health
candidates = candidates
.Where(c => c.Health is InstanceHealthStatus.Healthy or InstanceHealthStatus.Degraded)
.ToList();
// 3. Region preference
var localRegion = candidates.Where(c => c.Region == _config.Region).ToList();
var neighborRegions = candidates.Where(c => _config.NeighborRegions.Contains(c.Region)).ToList();
var otherRegions = candidates.Except(localRegion).Except(neighborRegions).ToList();
var preferred = localRegion.Any() ? localRegion
: neighborRegions.Any() ? neighborRegions
: otherRegions;
// 4. Within tier: prefer lower latency, then most recent heartbeat
return preferred
.OrderBy(c => c.AveragePingMs)
.ThenByDescending(c => c.LastHeartbeatUtc)
.FirstOrDefault();
}
```
---
## 7) Configuration
```yaml
gateway:
node:
region: "eu1"
nodeId: "gw-eu1-01"
environment: "prod"
transports:
tcp:
enabled: true
port: 9100
maxConnections: 1000
receiveBufferSize: 65536
sendBufferSize: 65536
tls:
enabled: true
port: 9443
certificatePath: "/certs/gateway.pfx"
certificatePassword: "${GATEWAY_CERT_PASSWORD}"
clientCertificateMode: "RequireCertificate"
allowedClientCertificateThumbprints: []
routing:
defaultTimeout: "30s"
maxRequestBodySize: "100MB"
streamingEnabled: true
streamingBufferSize: 16384
neighborRegions: ["eu2", "us1"]
auth:
dpopEnabled: true
dpopMaxClockSkew: "60s"
mtlsEnabled: true
rateLimiting:
enabled: true
requestsPerMinute: 1000
burstSize: 100
redisConnectionString: "${REDIS_URL}" # Valkey (Redis-compatible)
openapi:
enabled: true
cacheTtlSeconds: 300
title: "Stella Ops API"
version: "1.0.0"
health:
heartbeatIntervalSeconds: 10
heartbeatTimeoutSeconds: 30
unhealthyThreshold: 3
```
---
## 8) Scale & Performance
| Metric | Target | Notes |
|--------|--------|-------|
| Routing latency (P50) | <2ms | Overhead only; excludes downstream |
| Routing latency (P99) | <5ms | Under normal load |
| Concurrent connections | 10,000 | Per gateway instance |
| Requests/second | 50,000 | Per gateway instance |
| Memory footprint | <512MB | Base; scales with connections |
### Scaling Strategy
- Horizontal scaling behind load balancer
- Sticky sessions NOT required (stateless)
- Regional deployment for latency optimization
- Rate limiting via distributed Valkey/Redis
---
## 9) Security Posture
### Authentication
| Method | Description |
|--------|-------------|
| DPoP | Proof-of-possession tokens from Authority |
| mTLS | Certificate-bound tokens for machine clients |
### Authorization
- Claims-based authorization per endpoint
- Required claims defined in endpoint descriptors
- Tenant isolation via `tid` claim
### Transport Security
| Component | Encryption |
|-----------|------------|
| Client Gateway | TLS 1.3 (HTTPS) |
| Gateway Microservices | TLS (prod), TCP (dev only) |
### Rate Limiting
Gateway uses the Router's dual-window rate limiting middleware with circuit breaker:
- **Instance-level** (in-memory): Per-router-instance limits using sliding window counters
- High-precision sub-second buckets for fair rate distribution
- No external dependencies; always available
- **Environment-level** (Valkey-backed): Cross-instance limits for distributed deployments
- Atomic Lua scripts for consistent counting across instances
- Circuit breaker pattern for fail-open behavior when Valkey is unavailable
- **Activation gate**: Environment-level checks only activate above traffic threshold (configurable)
- **Response headers**: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, Retry-After
Configuration via `appsettings.yaml`:
```yaml
rate_limiting:
process_back_pressure_when_more_than_per_5min: 5000
for_instance:
rules:
- max_requests: 100
per_seconds: 1
- max_requests: 1000
per_seconds: 60
for_environment:
valkey_connection: "localhost:6379"
rules:
- max_requests: 10000
per_seconds: 60
circuit_breaker:
failure_threshold: 3
timeout_seconds: 30
half_open_timeout: 10
```
---
## 10) Observability & Audit
### Metrics (Prometheus)
```
gateway_requests_total{service,method,path,status}
gateway_request_duration_seconds{service,method,path,quantile}
gateway_active_connections{service}
gateway_transport_frames_total{type}
gateway_auth_failures_total{reason}
gateway_rate_limit_exceeded_total{tenant}
```
### Traces (OpenTelemetry)
- Span per request: `gateway.route`
- Child span: `gateway.auth.validate`
- Child span: `gateway.transport.send`
### Logs (Structured)
```json
{
"timestamp": "2025-12-21T10:00:00Z",
"level": "info",
"message": "Request routed",
"correlationId": "abc123",
"tenantId": "tenant-1",
"method": "GET",
"path": "/api/v1/scans/xyz",
"service": "scanner",
"durationMs": 45,
"status": 200
}
```
---
## 11) Testing Matrix
| Test Type | Scope | Coverage Target |
|-----------|-------|-----------------|
| Unit | Routing algorithm, auth validation | 90% |
| Integration | Transport + routing flow | 80% |
| E2E | Full request path with mock services | Key flows |
| Performance | Latency, throughput, connection limits | SLO targets |
| Chaos | Connection failures, microservice crashes | Resilience |
### Test Fixtures
- `StellaOps.Router.Transport.InMemory` for transport mocking
- Mock Authority for auth testing
- `WebApplicationFactory` for integration tests
---
## 12) DevOps & Operations
### Deployment
```yaml
# Kubernetes deployment excerpt
apiVersion: apps/v1
kind: Deployment
metadata:
name: gateway
spec:
replicas: 3
template:
spec:
containers:
- name: gateway
image: stellaops/gateway:1.0.0
ports:
- containerPort: 8080 # HTTPS
- containerPort: 9443 # TLS (microservices)
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /health/live
port: 8080
readinessProbe:
httpGet:
path: /health/ready
port: 8080
```
### SLOs
| SLO | Target | Measurement |
|-----|--------|-------------|
| Availability | 99.9% | Uptime over 30 days |
| Latency P99 | <50ms | Includes downstream |
| Error rate | <0.1% | 5xx responses |
---
## 13) Roadmap
| Feature | Sprint | Status |
|---------|--------|--------|
| Core implementation | 3600.0001.0001 | TODO |
| Performance Testing Pipeline | 038 | DONE |
| WebSocket support | Future | Planned |
| gRPC passthrough | Future | Planned |
| GraphQL aggregation | Future | Exploration |
---
## 14) Performance Testing Pipeline (k6 + Prometheus + Correlation IDs)
### Overview
The Gateway includes a comprehensive performance testing pipeline with k6 load tests,
Prometheus metric instrumentation, and Grafana dashboards for performance curve modelling.
### k6 Scenarios (AG)
| Scenario | Purpose | VUs | Duration | Key Metric |
|----------|---------|-----|----------|------------|
| A Health Baseline | Sub-ms health probe overhead | 10 | 1 min | P95 < 10 ms |
| B OpenAPI Aggregation | Spec cache under concurrent readers | 50 | 75 s | P95 < 200 ms |
| C Routing Throughput | Mixed-method routing at target RPS | 200 | 2 min | P50 < 2 ms, P99 < 5 ms |
| D Correlation ID | Propagation overhead measurement | 20 | 1 min | P95 < 5 ms overhead |
| E Rate Limit Boundary | Enforcement correctness at boundary | 100 | 1 min | Retry-After header |
| F Connection Ramp | Transport saturation (ramp to 1000 VUs) | 1000 | 2 min | No 503 responses |
| G Steady-State Soak | Memory leak / resource exhaustion | 50 | 10 min | Stable memory |
Run all scenarios:
```bash
k6 run --env BASE_URL=https://gateway.stella-ops.local src/Gateway/__Tests/load/gateway_performance.k6.js
```
Run a single scenario:
```bash
k6 run --env BASE_URL=https://gateway.stella-ops.local --env SCENARIO=scenario_c_routing_throughput src/Gateway/__Tests/load/gateway_performance.k6.js
```
### Performance Metrics (GatewayPerformanceMetrics)
Meter: `StellaOps.Gateway.Performance`
| Instrument | Type | Unit | Description |
|------------|------|------|-------------|
| `gateway.requests.total` | Counter | | Total requests processed |
| `gateway.errors.total` | Counter | | Errors (4xx/5xx) |
| `gateway.ratelimit.total` | Counter | | Rate-limited requests (429) |
| `gateway.request.duration` | Histogram | ms | Full request duration |
| `gateway.auth.duration` | Histogram | ms | Auth middleware duration |
| `gateway.transport.duration` | Histogram | ms | TCP/TLS transport duration |
| `gateway.routing.duration` | Histogram | ms | Instance selection duration |
### Grafana Dashboard
Dashboard: `devops/telemetry/dashboards/stella-ops-gateway-performance.json`
UID: `stella-ops-gateway-performance`
Panels:
1. **Overview row** P50/P99 gauges, error rate, RPS
2. **Latency Distribution** Percentile time series (overall + per-service)
3. **Throughput & Rate Limiting** RPS by service, rate-limited requests by route
4. **Pipeline Breakdown** Auth/Routing/Transport P95 breakdown, errors by status
5. **Connections & Resources** Active connections, endpoints, memory usage
### C# Models
| Type | Purpose |
|------|---------|
| `GatewayPerformanceObservation` | Single request observation (all pipeline phases) |
| `PerformanceScenarioConfig` | Scenario definition with SLO thresholds |
| `PerformanceCurvePoint` | Aggregated window data with computed RPS/error rate |
| `PerformanceTestSummary` | Complete test run result with threshold violations |
| `GatewayPerformanceMetrics` | OTel service emitting Prometheus-compatible metrics |
---
## 14) References
- Router Architecture: `docs/modules/router/architecture.md`
- Gateway Identity Header Policy: `docs/modules/gateway/identity-header-policy.md`
- OpenAPI Aggregation: `docs/modules/gateway/openapi.md`
- Router ASP.NET Endpoint Bridge: `docs/modules/router/aspnet-endpoint-bridge.md`
- Router Messaging (Valkey) Transport: `docs/modules/router/messaging-valkey-transport.md`
- Authority Integration: `docs/modules/authority/architecture.md`
- Reference Architecture: `docs/product/advisories/archived/2025-12-21-reference-architecture/`
---
**Last Updated**: 2025-12-21 (Sprint 3600)

View File

@@ -1,129 +0,0 @@
# Gateway · Identity Header Policy for Router Dispatch
## Status
- **Implemented** in Sprint 8100.0011.0002.
- Middleware: `src/Gateway/StellaOps.Gateway.WebService/Middleware/IdentityHeaderPolicyMiddleware.cs`
- Last updated: 2025-12-24 (UTC).
## Why This Exists
The Gateway is the single HTTP ingress point and routes requests to internal microservices over Router transports. Many services (and legacy components) still rely on **header-based identity context** (tenant/scopes/actor) rather than (or in addition to) `HttpContext.User` claims.
This creates a hard security requirement:
- **Clients must never be able to inject/override “roles/scopes” headers** that the downstream service trusts.
- The Gateway must derive the effective identity from the validated JWT/JWK token (or explicit anonymous identity) and **overwrite** downstream identity headers accordingly.
## Implementation
The `IdentityHeaderPolicyMiddleware` (introduced in Sprint 8100.0011.0002) replaces the legacy middleware:
- ~~`src/Gateway/StellaOps.Gateway.WebService/Middleware/ClaimsPropagationMiddleware.cs`~~ (retired)
- ~~`src/Gateway/StellaOps.Gateway.WebService/Middleware/TenantMiddleware.cs`~~ (retired)
### Resolved issues
1) **Spoofing risk:** ✅ Fixed. Middleware uses "strip-and-overwrite" semantics—reserved headers are stripped before claims are extracted and downstream headers are written.
2) **Claim type mismatch:** ✅ Fixed. Middleware uses `StellaOpsClaimTypes.Tenant` (`stellaops:tenant`) with fallback to legacy `tid` claim.
3) **Scope claim mismatch:** ✅ Fixed. Middleware extracts scopes from both `scp` (individual claims) and `scope` (space-separated) claims.
4) **Docs alignment:** ✅ Reconciled in this sprint.
## Policy Goals
- **No client-controlled identity headers:** the Gateway rejects or strips identity headers coming from external clients.
- **Gateway-controlled propagation:** the Gateway sets downstream identity headers based on validated token claims or a defined anonymous identity.
- **Compatibility bridge:** support both `X-Stella-*` and `X-StellaOps-*` header naming during migration.
- **Determinism:** header values are canonicalized (whitespace, ordering) and do not vary across equivalent requests.
## Reserved Headers (Draft)
The following headers are considered **reserved identity context** and must not be trusted from external clients:
- Tenant / project:
- `X-StellaOps-Tenant`, `X-Stella-Tenant`
- `X-StellaOps-Project`, `X-Stella-Project`
- Scopes / roles:
- `X-StellaOps-Scopes`, `X-Stella-Scopes`
- Actor / subject (if used for auditing):
- `X-StellaOps-Actor`, `X-Stella-Actor`
- Token proof / confirmation (if propagated):
- `cnf`, `cnf.jkt`
**Internal/legacy pass-through keys to also treat as reserved:**
- `sub`, `scope`, `scp`, `tid` (legacy), `stellaops:tenant` (if ever used as a header key)
## Overwrite Rules (Draft)
For non-system paths (i.e., requests that will be routed to microservices):
1) **Strip** all reserved identity headers from the incoming request.
2) **Compute** effective identity from the authenticated principal:
- `sub` from JWT `sub` (`StellaOpsClaimTypes.Subject`)
- `tenant` from `stellaops:tenant` (`StellaOpsClaimTypes.Tenant`)
- `project` from `stellaops:project` (`StellaOpsClaimTypes.Project`) when present
- `scopes` from:
- `scp` claims (`StellaOpsClaimTypes.ScopeItem`) if present, else
- split `scope` (`StellaOpsClaimTypes.Scope`) by spaces
3) **Write** downstream headers (compat mode):
- Tenant:
- `X-StellaOps-Tenant: <tenant>`
- `X-Stella-Tenant: <tenant>` (optional during migration)
- Project (optional):
- `X-StellaOps-Project: <project>`
- `X-Stella-Project: <project>` (optional during migration)
- Scopes:
- `X-StellaOps-Scopes: <space-delimited scopes>`
- `X-Stella-Scopes: <space-delimited scopes>` (optional during migration)
- Actor:
- `X-StellaOps-Actor: <sub>` (unless another canonical actor claim is defined)
### Anonymous mode
If `Gateway:Auth:AllowAnonymous=true` and the request is unauthenticated:
- Set an explicit anonymous identity so downstream services never interpret “missing header” as privileged:
- `X-StellaOps-Actor: anonymous`
- `X-StellaOps-Scopes: ` (empty) or `anonymous` (choose one and document)
- Tenant behavior must be explicitly defined:
- either reject routed requests without tenant context, or
- require a tenant header even in anonymous mode and treat it as *untrusted input* that only selects a tenancy partition (not privileges).
## Scope Override Header (Offline/Pre-prod)
Some legacy flows allow setting scopes via headers (for offline kits or pre-prod bundles).
Draft enforcement:
- Default: **forbid** client-provided scope headers (`X-StellaOps-Scopes`, `X-Stella-Scopes`) and return 403 (deterministic error code).
- Optional controlled override: allow only when `Gateway:Auth:AllowScopeHeader=true`, and only for explicitly allowed environments (offline/pre-prod).
- Even when allowed, the override must not silently escalate a request beyond what the token allows unless the request is explicitly unauthenticated and the environment is configured for offline operation.
## Implementation Details
### Middleware Registration
The middleware is registered in `Program.cs` after authentication:
```csharp
app.UseAuthentication();
app.UseMiddleware<SenderConstraintMiddleware>();
app.UseMiddleware<IdentityHeaderPolicyMiddleware>();
```
### Configuration
Options are configured via `GatewayOptions.Auth`:
```yaml
Gateway:
Auth:
EnableLegacyHeaders: true # Write X-Stella-* in addition to X-StellaOps-*
AllowScopeHeader: false # Forbid client scope headers (default)
```
### HttpContext.Items Keys
The middleware stores normalized identity in `HttpContext.Items` using `GatewayContextKeys`:
- `Gateway.TenantId` — extracted tenant identifier
- `Gateway.ProjectId` — extracted project identifier (optional)
- `Gateway.Actor` — subject/actor from claims or "anonymous"
- `Gateway.Scopes``HashSet<string>` of scopes
- `Gateway.IsAnonymous``bool` indicating anonymous request
- `Gateway.DpopThumbprint` — JKT from cnf claim (if present)
- `Gateway.CnfJson` — raw cnf claim JSON (if present)
### Tests
Located in `src/Gateway/__Tests/StellaOps.Gateway.WebService.Tests/Middleware/IdentityHeaderPolicyMiddlewareTests.cs`:
- ✅ Spoofed identity headers are stripped and overwritten
- ✅ Claim type mapping uses `StellaOpsClaimTypes.*` correctly
- ✅ Anonymous requests receive explicit `anonymous` identity
- ✅ Legacy headers are written when `EnableLegacyHeaders=true`
- ✅ Scopes are sorted deterministically
## Related Documents
- Gateway architecture: `docs/modules/gateway/architecture.md`
- Tenant auth contract (Web V): `docs/api/gateway/tenant-auth.md`
- Router ASP.NET bridge: `docs/modules/router/aspnet-endpoint-bridge.md`

View File

@@ -1,344 +0,0 @@
# Gateway OpenAPI Implementation
This document describes the implementation architecture of OpenAPI document aggregation in the StellaOps Router Gateway.
## Architecture
The Gateway generates OpenAPI 3.1.0 documentation by aggregating schemas and endpoint metadata from connected microservices.
### Component Overview
```
┌─────────────────────────────────────────────────────────────────────┐
│ Gateway │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────┐ ┌────────────────────┐ │
│ │ ConnectionManager │───►│ InMemoryRoutingState│ │
│ │ │ │ │ │
│ │ - OnHelloReceived │ │ - Connections[] │ │
│ │ - OnConnClosed │ │ - Endpoints │ │
│ └──────────────────┘ │ - Schemas │ │
│ │ └─────────┬──────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────────┐ ┌────────────────────┐ │
│ │ OpenApiDocument │◄───│ GatewayOpenApi │ │
│ │ Cache │ │ DocumentCache │ │
│ │ │ │ │ │
│ │ - Invalidate() │ │ - TTL expiration │ │
│ └──────────────────┘ │ - ETag generation │ │
│ └─────────┬──────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────┐ │
│ │ OpenApiDocument │ │
│ │ Generator │ │
│ │ │ │
│ │ - GenerateInfo() │ │
│ │ - GeneratePaths() │ │
│ │ - GenerateTags() │ │
│ │ - GenerateSchemas()│ │
│ └─────────┬──────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────┐ │
│ │ ClaimSecurity │ │
│ │ Mapper │ │
│ │ │ │
│ │ - SecuritySchemes │ │
│ │ - SecurityRequire │ │
│ └────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
```
### Components
| Component | File | Responsibility |
|-----------|------|----------------|
| `IOpenApiDocumentGenerator` | `OpenApi/IOpenApiDocumentGenerator.cs` | Interface for document generation |
| `OpenApiDocumentGenerator` | `OpenApi/OpenApiDocumentGenerator.cs` | Builds OpenAPI 3.1.0 JSON |
| `IGatewayOpenApiDocumentCache` | `OpenApi/IGatewayOpenApiDocumentCache.cs` | Interface for document caching |
| `GatewayOpenApiDocumentCache` | `OpenApi/GatewayOpenApiDocumentCache.cs` | TTL + invalidation caching |
| `ClaimSecurityMapper` | `OpenApi/ClaimSecurityMapper.cs` | Maps claims to OAuth2 scopes |
| `OpenApiEndpoints` | `OpenApi/OpenApiEndpoints.cs` | HTTP endpoint handlers |
| `OpenApiAggregationOptions` | `OpenApi/OpenApiAggregationOptions.cs` | Configuration options |
---
## OpenApiDocumentGenerator
Generates the complete OpenAPI 3.1.0 document from routing state.
### Process Flow
1. **Collect connections** from `IGlobalRoutingState`
2. **Generate info** section from `OpenApiAggregationOptions`
3. **Generate paths** by iterating all endpoints across connections
4. **Generate components** including schemas and security schemes
5. **Generate tags** from unique service names
### Schema Handling
Schemas are prefixed with service name to avoid naming conflicts:
```csharp
var prefixedId = $"{conn.Instance.ServiceName}_{schemaId}";
// billing_CreateInvoiceRequest
```
### Operation ID Generation
Operation IDs follow a consistent pattern:
```csharp
var operationId = $"{serviceName}_{path}_{method}";
// billing_invoices_POST
```
---
## GatewayOpenApiDocumentCache
Implements caching with TTL expiration and content-based ETags.
### Cache Behavior
| Trigger | Action |
|---------|--------|
| First request | Generate and cache document |
| Subsequent requests (within TTL) | Return cached document |
| TTL expired | Regenerate document |
| Connection added/removed | Invalidate cache |
### ETag Generation
ETags are computed from SHA256 hash of document content:
```csharp
var hash = SHA256.HashData(Encoding.UTF8.GetBytes(documentJson));
var etag = $"\"{Convert.ToHexString(hash)[..16]}\"";
```
### Thread Safety
The cache uses locking to ensure thread-safe regeneration:
```csharp
lock (_lock)
{
if (_cachedDocument is null || IsExpired())
{
RegenerateDocument();
}
}
```
---
## ClaimSecurityMapper
Maps endpoint claim requirements to OpenAPI security schemes.
### Security Scheme Generation
Always generates `BearerAuth` scheme. Generates `OAuth2` scheme only when endpoints have claim requirements:
```csharp
public static JsonObject GenerateSecuritySchemes(
IEnumerable<EndpointDescriptor> endpoints,
string tokenUrl)
{
var schemes = new JsonObject();
// Always add BearerAuth
schemes["BearerAuth"] = new JsonObject { ... };
// Collect scopes from all endpoints
var scopes = CollectScopes(endpoints);
// Add OAuth2 only if scopes exist
if (scopes.Count > 0)
{
schemes["OAuth2"] = GenerateOAuth2Scheme(tokenUrl, scopes);
}
return schemes;
}
```
### Per-Operation Security
Each endpoint with claims gets a security requirement:
```csharp
public static JsonArray GenerateSecurityRequirement(EndpointDescriptor endpoint)
{
if (endpoint.AllowAnonymous)
return new JsonArray(); // Anonymous endpoint
if (!endpoint.RequiresAuthentication && endpoint.RequiringClaims.Count == 0)
return new JsonArray(); // No auth semantics published
return new JsonArray
{
new JsonObject
{
["BearerAuth"] = new JsonArray(),
["OAuth2"] = new JsonArray(scopes.Select(scope => scope))
}
};
}
```
### Router-specific OpenAPI extensions
Gateway now emits Router-specific extensions on each operation:
- `x-stellaops-gateway-auth`: effective authorization semantics projected from endpoint metadata.
- `allowAnonymous`
- `requiresAuthentication`
- `source` (`None`, `AspNetMetadata`, `YamlOverride`, `Hybrid`)
- optional `policies`, `roles`, `claimRequirements`
- `x-stellaops-timeout`: timeout semantics used by gateway dispatch.
- `effectiveSeconds`
- `source` (`endpoint`, `gatewayRouteDefault`, and capped variants)
- `endpointSeconds`, `gatewayRouteDefaultSeconds`, `gatewayGlobalCapSeconds` when available
- precedence list: endpoint override -> service default -> gateway route default -> gateway global cap
- `x-stellaops-timeout-seconds`: backward-compatible scalar alias for `effectiveSeconds`.
---
## Configuration Reference
### OpenApiAggregationOptions
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `Title` | `string` | `"StellaOps Gateway API"` | API title |
| `Description` | `string` | `"Unified API..."` | API description |
| `Version` | `string` | `"1.0.0"` | API version |
| `ServerUrl` | `string` | `"/"` | Base server URL |
| `CacheTtlSeconds` | `int` | `60` | Cache TTL |
| `Enabled` | `bool` | `true` | Enable/disable |
| `LicenseName` | `string` | `"BUSL-1.1"` | License name |
| `ContactName` | `string?` | `null` | Contact name |
| `ContactEmail` | `string?` | `null` | Contact email |
| `TokenUrl` | `string` | `"/auth/token"` | OAuth2 token URL |
### YAML Configuration
```yaml
OpenApi:
Title: "My Gateway API"
Description: "Unified API for all microservices"
Version: "2.0.0"
ServerUrl: "https://api.example.com"
CacheTtlSeconds: 60
Enabled: true
LicenseName: "BUSL-1.1"
ContactName: "API Team"
ContactEmail: "api@example.com"
TokenUrl: "/auth/token"
```
---
## Service Registration
Services are registered via dependency injection in `ServiceCollectionExtensions`:
```csharp
services.Configure<OpenApiAggregationOptions>(
configuration.GetSection("OpenApi"));
services.AddSingleton<IOpenApiDocumentGenerator, OpenApiDocumentGenerator>();
services.AddSingleton<IGatewayOpenApiDocumentCache, GatewayOpenApiDocumentCache>();
```
Endpoints are mapped in `ApplicationBuilderExtensions`:
```csharp
app.MapGatewayOpenApiEndpoints();
```
---
## Cache Invalidation
The `ConnectionManager` invalidates the cache on connection changes:
```csharp
private Task HandleHelloReceivedAsync(ConnectionState state, HelloPayload payload)
{
_routingState.AddConnection(state);
_openApiCache?.Invalidate(); // Invalidate on new connection
return Task.CompletedTask;
}
private Task HandleConnectionClosedAsync(string connectionId)
{
_routingState.RemoveConnection(connectionId);
_openApiCache?.Invalidate(); // Invalidate on disconnect
return Task.CompletedTask;
}
```
---
## Extension Points
### Custom Routing Plugins
The Gateway supports custom routing plugins via `IRoutingPlugin`. While not directly related to OpenAPI, routing decisions can affect which endpoints are exposed.
### Future Enhancements
Potential extension points for future development:
- **Schema Transformers**: Modify schemas before aggregation
- **Tag Customization**: Custom tag generation logic
- **Response Examples**: Include example responses from connected services
- **Webhooks**: Notify external systems on document changes
---
## Testing
Unit tests are located in `src/Gateway/__Tests/StellaOps.Gateway.WebService.Tests/OpenApi/`:
| Test File | Coverage |
|-----------|----------|
| `OpenApiDocumentGeneratorTests.cs` | Document structure, schema merging, tag generation |
| `GatewayOpenApiDocumentCacheTests.cs` | TTL expiry, invalidation, ETag consistency |
| `ClaimSecurityMapperTests.cs` | Security scheme generation from claims |
### Test Patterns
```csharp
[Fact]
public void GenerateDocument_WithConnections_GeneratesPaths()
{
// Arrange
var endpoint = new EndpointDescriptor { ... };
var connection = CreateConnection("inventory", "1.0.0", endpoint);
_routingState.Setup(x => x.GetAllConnections()).Returns([connection]);
// Act
var document = _sut.GenerateDocument();
// Assert
var doc = JsonDocument.Parse(document);
doc.RootElement.GetProperty("paths")
.TryGetProperty("/api/items", out _)
.Should().BeTrue();
}
```
---
## See Also
- [Schema Validation](../router/schema-validation.md) - JSON Schema validation in microservices
- [OpenAPI Aggregation](../router/openapi-aggregation.md) - Configuration and usage guide
- [API Overview](../../api/overview.md) - General API conventions

View File

@@ -45,9 +45,13 @@ Key settings:
- Plugin search paths for connector discovery
- Health check intervals and timeout thresholds
## IDE Extensions (VS Code, JetBrains)
As of Sprint 214, the IDE extension plugins (previously `src/Extensions/`) are housed under `src/Integrations/__Extensions/`. These are non-.NET projects (TypeScript for VS Code, Kotlin for JetBrains) that act as thin API clients for the Orchestrator and Authority services. See the [Architecture doc](./architecture.md#ide-extensions-vs-code-jetbrains) for details.
## Related Documentation
- [Plugin Framework](../plugin/) - Underlying plugin infrastructure
- [Scanner](../scanner/) - Primary consumer of integration configs
- [Orchestrator](../orchestrator/) - Pipeline orchestration using integrations
- [Orchestrator](../jobengine/) - Pipeline orchestration using integrations
- [Signals](../signals/) - SCM webhook processing

View File

@@ -115,6 +115,44 @@ public interface IIntegrationPlugin
- Plugin discovery is triggered on startup and on-demand; results are cached
- Integration queries use indexed tenant_id + type columns for fast filtering
## IDE Extensions (VS Code, JetBrains)
The Integrations module also owns the IDE extension plugins, located under `src/Integrations/__Extensions/`. These are non-.NET projects that provide developer-facing tooling consuming the same Orchestrator/Router APIs as other integrations.
### VS Code Extension (`__Extensions/vscode-stella-ops/`)
- **Technology:** TypeScript, VS Code Extension API
- **Build:** `npm run compile` (TypeScript compilation)
- **Features:** Tree views for releases and environments, CodeLens annotations for `stella.yaml`, command palette integration, status bar widget
- **Manifest:** `package.json` (extension manifest, commands, views, configuration)
### JetBrains Plugin (`__Extensions/jetbrains-stella-ops/`)
- **Technology:** Kotlin, IntelliJ Platform SDK
- **Build:** Gradle (`./gradlew build`)
- **Features:** Tool windows (Releases/Environments/Deployments tabs), YAML annotator, action menus, status bar widget
- **Entry point:** `StellaOpsPlugin.kt`
### Design Principles (Extensions)
1. **Thin client** - Extensions contain no business logic; all state and decisions live in backend services
2. **Consistent experience** - Both plugins expose equivalent functionality despite different technology stacks
3. **Non-blocking** - All API calls are asynchronous; the IDE remains responsive during network operations
4. **Offline-tolerant** - Graceful degradation when the Stella Ops backend is unreachable
### Data Flow (Extensions)
```
[Developer IDE] --> [Extension/Plugin]
|
+-- GET /api/v1/releases/* --------> [Orchestrator API]
+-- GET /api/v1/environments/* ----> [Orchestrator API]
+-- POST /api/v1/promotions/* -----> [Orchestrator API]
+-- POST /oauth/token -------------> [Authority]
```
Authentication uses OAuth tokens obtained from the Authority service, stored in the IDE's secure credential store (VS Code `SecretStorage`, JetBrains `PasswordSafe`).
## References
- [Module README](./README.md)

View File

@@ -1,13 +1,19 @@
# IssuerDirectory
**Status:** Implemented
**Source:** `src/IssuerDirectory/`
**Owner:** VEX Guild
**Status:** Implemented (source relocated by Sprint 216)
**Source:** `src/Authority/StellaOps.IssuerDirectory/` (previously `src/IssuerDirectory/`)
**Owner:** Authority domain (Identity & Trust)
## Purpose
IssuerDirectory maintains a trust registry of CSAF publishers and VEX statement issuers. Provides discovery, validation, and trust scoring for upstream vulnerability advisories and VEX statements.
## Domain ownership
As of Sprint 216, IssuerDirectory source is owned by the Authority domain. The runtime service identity, container, and database schema remain independent. Schema isolation from AuthorityDbContext is a deliberate security feature.
See `docs/modules/authority/architecture.md` (sections 21.1--21.4) for schema ownership and the no-merge ADR.
## Components
**Services:**
@@ -34,11 +40,8 @@ Key settings:
## Related Documentation
- Architecture: `./architecture.md`
- Architecture: `../authority/architecture.md` (sections 21.1--21.4)
- Archived original: `docs-archived/modules/issuer-directory/`
- Concelier: `../concelier/`
- VexHub: `../vexhub/`
- VexLens: `../vex-lens/`
## Current Status
Implemented with CSAF publisher discovery and validation. Supports issuer metadata storage and trust registry queries. Integrated with VEX ingestion pipeline.

View File

@@ -1,105 +1,19 @@
# Issuer Directory Architecture
# Issuer Directory Architecture -- Redirect
> **Status:** Initial service scaffold (Sprint 100 Identity & Signing)
> **Moved by Sprint 216 (2026-03-04).** IssuerDirectory is now owned by the Authority domain.
## 1. Purpose
## Current documentation
Issuer Directory centralises trusted VEX/CSAF publisher metadata so downstream services (VEX Lens, Excititor, Policy Engine) can resolve issuer identity, active keys, and trust weights. The initial milestone delivers tenant-scoped CRUD APIs with audit logging plus bootstrap import for CSAF publishers.
- **Architecture and schema ownership:** `docs/modules/authority/architecture.md` (sections 21.1--21.4)
- **Source code:** `src/Authority/StellaOps.IssuerDirectory/`
- **Client library:** `src/Authority/__Libraries/StellaOps.IssuerDirectory.Client/`
- **Persistence:** `src/Authority/__Libraries/StellaOps.IssuerDirectory.Persistence/`
## 2. Runtime Topology
## Archived original
- **Service name:** `stellaops/issuer-directory`
- **Framework:** ASP.NET Core minimal APIs (`net10.0`)
- **Persistence:** PostgreSQL (`issuer_directory.issuers`, `issuer_directory.issuer_keys`, `issuer_directory.issuer_audit`)
- **AuthZ:** StellaOps resource server scopes (`issuer-directory:read`, `issuer-directory:write`, `issuer-directory:admin`)
- **Audit:** Every create/update/delete emits an audit record with actor, reason, and context.
- **Bootstrap:** On startup, the service imports `data/csaf-publishers.json` into the global tenant (`@global`) and records a `seeded` audit the first time each publisher is added.
- **Key lifecycle:** API validates Ed25519 public keys, X.509 certificates, and DSSE public keys, enforces future expiries, deduplicates fingerprints, and records audit entries for create/rotate/revoke actions.
- `docs-archived/modules/issuer-directory/architecture.md`
```
Clients ──> Authority (DPoP/JWT) ──> IssuerDirectory WebService ──> PostgreSQL
└─> Audit sink (PostgreSQL)
```
## Runtime identity
## 3. Configuration
Configuration is resolved via `IssuerDirectoryWebServiceOptions` (section name `IssuerDirectory`). The default YAML sample lives at `etc/issuer-directory.yaml.sample` and exposes:
```yaml
IssuerDirectory:
telemetry:
minimumLogLevel: Information
authority:
enabled: true
issuer: https://authority.example.com/realms/stellaops
requireHttpsMetadata: true
audiences:
- stellaops-platform
readScope: issuer-directory:read
writeScope: issuer-directory:write
adminScope: issuer-directory:admin
tenantHeader: X-StellaOps-Tenant
seedCsafPublishers: true
csafSeedPath: data/csaf-publishers.json
Postgres:
connectionString: Host=localhost;Port=5432;Database=issuer_directory;Username=stellaops;Password=secret
schema: issuer_directory
issuersTable: issuers
issuerKeysTable: issuer_keys
auditTable: issuer_audit
```
## 4. API Surface (v0)
| Method | Route | Scope | Description |
|--------|-------|-------|-------------|
| `GET` | `/issuer-directory/issuers` | `issuer-directory:read` | List tenant issuers (optionally include global seeds). |
| `GET` | `/issuer-directory/issuers/{id}` | `issuer-directory:read` | Fetch a single issuer by identifier. |
| `POST` | `/issuer-directory/issuers` | `issuer-directory:write` | Create a tenant issuer. Requires `X-StellaOps-Tenant` header and optional `X-StellaOps-Reason`. |
| `PUT` | `/issuer-directory/issuers/{id}` | `issuer-directory:write` | Update issuer metadata/endpoints/tags. |
| `DELETE` | `/issuer-directory/issuers/{id}` | `issuer-directory:admin` | Delete issuer (records audit). |
| `GET` | `/issuer-directory/issuers/{id}/keys` | `issuer-directory:read` | List issuer keys (tenant + optional `@global` seeds). |
| `POST` | `/issuer-directory/issuers/{id}/keys` | `issuer-directory:write` | Add a signing key (validates format, deduplicates fingerprint, audits). |
| `POST` | `/issuer-directory/issuers/{id}/keys/{keyId}/rotate` | `issuer-directory:write` | Retire an active key and create a replacement atomically. |
| `DELETE` | `/issuer-directory/issuers/{id}/keys/{keyId}` | `issuer-directory:admin` | Revoke a key (status → revoked, audit logged). |
| `GET` | `/issuer-directory/issuers/{id}/trust` | `issuer-directory:read` | Retrieve tenant/global trust overrides with effective weight. |
| `PUT` | `/issuer-directory/issuers/{id}/trust` | `issuer-directory:write` | Set or update a tenant trust override; reason may be supplied in body/header. |
| `DELETE` | `/issuer-directory/issuers/{id}/trust` | `issuer-directory:admin` | Remove a tenant trust override (falls back to global/default weight). |
All write/delete operations accept an optional audit reason header (`X-StellaOps-Reason`) which is persisted alongside trust override changes.
Payloads follow the contract in `Contracts/IssuerDtos.cs` and align with domain types (`IssuerRecord`, `IssuerMetadata`, `IssuerEndpoint`).
## 5. Dependencies & Reuse
- `StellaOps.IssuerDirectory.Core` — domain model (`IssuerRecord`, `IssuerKeyRecord`) + application services.
- `StellaOps.IssuerDirectory.Infrastructure` — PostgreSQL persistence, audit sink, seed loader.
- `StellaOps.IssuerDirectory.WebService` — minimal API host, authentication wiring.
- Shared libraries: `StellaOps.Configuration`, `StellaOps.Auth.ServerIntegration`.
## 6. Testing
- Unit coverage for issuer CRUD (`IssuerDirectoryServiceTests`) and key lifecycle (`IssuerKeyServiceTests`) in `StellaOps.IssuerDirectory.Core.Tests`.
- Test infrastructure leverages `FakeTimeProvider` for deterministic timestamps and in-memory fakes for repository + audit sink.
## 7. Observability
- **Metrics.** `issuer_directory_changes_total` (labels: `tenant`, `issuer`, `action`) tracks issuer create/update/delete events; `issuer_directory_key_operations_total` (labels: `tenant`, `issuer`, `operation`, `key_type`) covers key create/rotate/revoke flows; `issuer_directory_key_validation_failures_total` (labels: `tenant`, `issuer`, `reason`) captures validation/verification failures. The WebService exports these via OpenTelemetry (`StellaOps.IssuerDirectory` meter).
- **Logs.** Service-level `ILogger` instrumentation records structured entries for issuer CRUD, key lifecycle operations, and validation failures; audit logs remain the authoritative trail.
## 8. Roadmap (next milestones)
1. **Key management APIs (ISSUER-30-002)** — manage signing keys, enforce expiry, integrate with KMS.
2. **Trust weight overrides (ISSUER-30-003)** — expose policy-friendly trust weighting with audit trails.
3. **SDK integration (ISSUER-30-004)** — supply cached issuer metadata to VEX Lens and Excititor clients.
4. **Observability & Ops (ISSUER-30-005/006)** — metrics, dashboards, deployment automation, offline kit.
## 9. Operations & runbooks
- [Deployment guide](operations/deployment.md)
- [Backup & restore](operations/backup-restore.md)
- [Offline kit notes](operations/offline-kit.md)
---
*Document owner: Issuer Directory Guild*
The IssuerDirectory service retains its own container, hostname, and endpoints.
Schema isolation from AuthorityDbContext is a deliberate security feature (see ADR in Authority architecture).

View File

@@ -21,9 +21,9 @@ The Orchestrator schedules, observes, and recovers ingestion and analysis jobs a
- Document offline/air-gap pathways for any new feature.
- Update telemetry/observability assets alongside feature work.
## Required Reading
- `docs/modules/orchestrator/README.md`
- `docs/modules/orchestrator/architecture.md`
- `docs/modules/orchestrator/implementation_plan.md`
- `docs/modules/jobengine/README.md`
- `docs/modules/jobengine/architecture.md`
- `docs/modules/jobengine/implementation_plan.md`
- `docs/modules/platform/architecture-overview.md`
## Working Agreement

View File

@@ -4,7 +4,7 @@
## 1) Topology
- **Orchestrator API (`StellaOps.Orchestrator`).** Minimal API providing job state, throttling controls, replay endpoints, and dashboard data. Authenticated via Authority scopes (`orchestrator:*`).
- **Orchestrator API (`StellaOps.JobEngine`).** Minimal API providing job state, throttling controls, replay endpoints, and dashboard data. Authenticated via Authority scopes (`orchestrator:*`).
- **Job ledger (PostgreSQL).** Tables `jobs`, `job_history`, `sources`, `quotas`, `throttles`, `incidents` (schema `orchestrator`). Append-only history ensures auditability.
- **Queue abstraction.** Supports Valkey Streams or NATS JetStream (pluggable). Each job carries lease metadata and retry policy.
- **Dashboard feeds.** SSE/GraphQL endpoints supply Console UI with job timelines, throughput, error distributions, and rate-limit status.
@@ -93,7 +93,7 @@ The `CircuitBreakerService` implements the circuit breaker pattern for downstrea
- `POST /api/limits/throttle` — apply throttle (requires elevated scope).
- `GET /api/dashboard/metrics` — aggregated metrics for Console dashboards.
### 4.2) Circuit breaker endpoints (`/api/v1/orchestrator/circuit-breakers`)
### 4.2) Circuit breaker endpoints (`/api/v1/jobengine/circuit-breakers`)
- `GET /` — List all circuit breakers for tenant (optional `?state=` filter).
- `GET /{serviceId}` — Get circuit breaker state for specific downstream service.
- `GET /{serviceId}/check` — Check if requests are allowed; returns `IsAllowed`, `State`, `FailureRate`, `TimeUntilRetry`.
@@ -102,7 +102,7 @@ The `CircuitBreakerService` implements the circuit breaker pattern for downstrea
- `POST /{serviceId}/force-open` — Manually open circuit (body: `reason`; audited).
- `POST /{serviceId}/force-close` — Manually close circuit (audited).
### 4.3) Quota governance endpoints (`/api/v1/orchestrator/quota-governance`)
### 4.3) Quota governance endpoints (`/api/v1/jobengine/quota-governance`)
- `GET /policies` — List quota allocation policies (optional `?enabled=` filter).
- `GET /policies/{policyId}` — Get specific policy.
- `POST /policies` — Create new policy.
@@ -116,14 +116,14 @@ The `CircuitBreakerService` implements the circuit breaker pattern for downstrea
- `GET /can-schedule` — Check if job can be scheduled (optional `?jobType=`).
### 4.4) Discovery and documentation
- Event envelope draft (`docs/modules/orchestrator/event-envelope.md`) defines notifier/webhook/SSE payloads with idempotency keys, provenance, and task runner metadata for job/pack-run events.
- OpenAPI discovery: `/.well-known/openapi` exposes `/openapi/orchestrator.json` (OAS 3.1) with pagination/idempotency/error-envelope examples; legacy job detail/summary endpoints now ship `Deprecation` + `Link` headers that point to their replacements.
- Event envelope draft (`docs/modules/jobengine/event-envelope.md`) defines notifier/webhook/SSE payloads with idempotency keys, provenance, and task runner metadata for job/pack-run events.
- OpenAPI discovery: `/.well-known/openapi` exposes `/openapi/jobengine.json` (OAS 3.1) with pagination/idempotency/error-envelope examples; legacy job detail/summary endpoints now ship `Deprecation` + `Link` headers that point to their replacements.
### 4.5) Release control plane dashboard endpoints
- `GET /api/v1/release-orchestrator/dashboard` — control-plane dashboard payload (pipeline, pending approvals, active deployments, recent releases).
- `POST /api/v1/release-orchestrator/promotions/{id}/approve` — approve a pending promotion from dashboard context.
- `POST /api/v1/release-orchestrator/promotions/{id}/reject` — reject a pending promotion from dashboard context.
- Compatibility aliases are exposed for legacy clients under `/api/release-orchestrator/*`.
- `GET /api/v1/release-jobengine/dashboard` — control-plane dashboard payload (pipeline, pending approvals, active deployments, recent releases).
- `POST /api/v1/release-jobengine/promotions/{id}/approve` — approve a pending promotion from dashboard context.
- `POST /api/v1/release-jobengine/promotions/{id}/reject` — reject a pending promotion from dashboard context.
- Compatibility aliases are exposed for legacy clients under `/api/release-jobengine/*`.
All responses include deterministic timestamps, job digests, and DSSE signature fields for offline reconciliation.
@@ -144,3 +144,85 @@ All responses include deterministic timestamps, job digests, and DSSE signature
- HA deployment with multiple API instances; queue storage determines redundancy strategy.
- Support for `maintenance` mode halting leases while allowing status inspection.
- Runbook includes procedures for expanding quotas, blacklisting misbehaving tenants, and recovering stuck jobs (clearing leases, applying pause/resume).
---
## 8) Orchestration domain subdomains (Sprint 208)
Sprint 208 consolidated Scheduler, TaskRunner, and PacksRegistry source trees under `src/JobEngine/` as subdomains of the orchestration domain. Each subdomain retains its own project names, namespaces, and runtime identities. No namespace renames were performed.
### 8.1) Scheduler subdomain
**Source location:** `src/JobEngine/StellaOps.Scheduler.*`
The Scheduler service re-evaluates already-cataloged images when intelligence changes (Concelier/Excititor/policy), orchestrates nightly and ad-hoc runs, targets only impacted images using the BOM-Index, and emits report-ready events for downstream Notify. Default mode is analysis-only (no image pull); optional content-refresh can be enabled per schedule.
**Deployables:** `StellaOps.Scheduler.WebService` (stateless), `StellaOps.Scheduler.Worker.Host` (scale-out).
**Database:** `SchedulerDbContext` (schema `scheduler`, 11 entities). Owns `schedules`, `runs`, `impact_cursors`, `locks`, `audit` tables. See archived docs: `docs-archived/modules/scheduler/architecture.md`.
### 8.2) TaskRunner subdomain
**Source location:** `src/JobEngine/StellaOps.TaskRunner/`, `src/JobEngine/StellaOps.TaskRunner.__Libraries/`
The TaskRunner provides the execution substrate for Orchestrator jobs. Workers poll lease endpoints, execute tasks, report outcomes, and stream logs/artifacts for pack-runs.
**Deployables:** `StellaOps.TaskRunner.WebService`, `StellaOps.TaskRunner.Worker`.
**Database and storage contract (Sprint 312):**
- `Storage:Driver=postgres` is the production default for run state, logs, and approvals.
- Postgres-backed stores: `PostgresPackRunStateStore`, `PostgresPackRunLogStore`, `PostgresPackRunApprovalStore` via `TaskRunnerDataSource`.
- Artifact payload channel uses object storage path (`seed-fs` driver) configured with `TaskRunner:Storage:ObjectStore:SeedFs:RootPath`.
- Explicit non-production overrides remain available (`filesystem`, `inmemory`) but are no longer implicit defaults.
### 8.3) PacksRegistry subdomain
**Source location:** `src/JobEngine/StellaOps.PacksRegistry/`, `src/JobEngine/StellaOps.PacksRegistry.__Libraries/`
The PacksRegistry manages compliance/automation pack definitions, versions, and distribution for the task execution pipeline.
**Deployables:** `StellaOps.PacksRegistry.WebService`, `StellaOps.PacksRegistry.Worker`.
**Database and storage contract (Sprint 312):**
- `Storage:Driver=postgres` is the production default for metadata/state repositories (`pack`, `parity`, `lifecycle`, `mirror`, `audit`, `attestation metadata`).
- Blob/object payloads (`pack content`, `provenance content`, `attestation content`) are persisted through the seed-fs object-store channel (`SeedFsPacksRegistryBlobStore`).
- PostgreSQL keeps metadata and compatibility placeholders; payload retrieval resolves from object storage first.
- Explicit non-production overrides remain available (`filesystem`, `inmemory`) but are no longer implicit defaults.
---
## 9) Architecture Decision Record: No DB merge (Sprint 208)
**Decision:** OrchestratorDbContext and SchedulerDbContext remain as separate DbContexts with separate PostgreSQL schemas. No cross-schema DB merge.
**Context:** Sprint 208 evaluated merging the Orchestrator (39 entities) and Scheduler (11 entities) DbContexts into a single unified context. Both define `Jobs` and `JobHistory` entities.
**Problem:** The `Jobs` and `JobHistory` entities have fundamentally incompatible semantics:
- **OrchestratorDbContext.Jobs:** Represents pipeline orchestration runs (source ingestion, policy evaluation, release promotion). Fields include `payloadDigest`, `dependencies`, `leaseId`, `retryPolicy`.
- **SchedulerDbContext.Jobs:** Represents cron-scheduled rescan executions (image re-evaluation, impact-index-driven). Fields include `scheduleId`, `trigger` (cron/conselier/excitor/manual), `impactSet`, `runStats`.
Merging would require renaming one set of entities (e.g., `SchedulerJobs`, `SchedulerJobHistory`), propagating through repositories, query code, compiled models, migrations, and external contracts. The schemas already provide clean separation at no operational cost since both live in the same `stellaops_platform` database.
**Decision rationale:**
1. Entity name collision with incompatible models makes merge risky and disruptive.
2. Compiled models from Sprint 219 would need regeneration for both contexts.
3. Schemas provide clean separation at zero cost.
4. Future domain rename (Sprint 221) is a better venue for any schema consolidation.
**Consequences:** TaskRunner and PacksRegistry remain independent subdomains and now implement explicit storage contracts (Postgres state/metadata plus object-store payload channels) without cross-schema DB merge.
---
## 10) Schema continuity remediation (Sprint 311)
Sprint 221 renamed the domain from Orchestrator to JobEngine but intentionally preserved the PostgreSQL schema name `orchestrator` for continuity. Sprint 311 closed the implementation drift so runtime, design-time, and compiled-model paths now align on the same preserved schema default.
Implemented alignment:
- Runtime default schema is centralized in `JobEngineDbContext.DefaultSchemaName` (`orchestrator`) and schema normalization is centralized in `JobEngineDbContext.ResolveSchemaName(...)`.
- Repository runtime context creation (`JobEngineDbContextFactory`) uses that same shared default and normalization logic.
- Design-time context creation now passes `JobEngineDbContext.DefaultSchemaName` explicitly instead of relying on implicit constructor fallback.
- EF compiled model schema annotations were aligned to `orchestrator` so compiled-model and runtime model behavior match.
Out of scope for Sprint 311:
- No schema migration from `orchestrator` to `jobengine` was introduced.
- Any future physical schema rename requires a dedicated migration sprint with data/backfill and rollback planning.

View File

@@ -11,14 +11,14 @@ Provide a living plan for Orchestrator deliverables, dependencies, and evidence.
- TBD (add when sprint is staffed).
## Dependencies
- `docs/modules/orchestrator/architecture.md`
- `docs/modules/orchestrator/README.md`
- `docs/modules/jobengine/architecture.md`
- `docs/modules/jobengine/README.md`
- `docs/modules/platform/architecture-overview.md`
## Evidence of completion
- Code changes under `src/Orchestrator/**`.
- Code changes under `src/JobEngine/**`.
- Tests and fixtures under the module's `__Tests` / `__Libraries`.
- Docs and runbooks under `docs/modules/orchestrator/**`.
- Docs and runbooks under `docs/modules/jobengine/**`.
## Notes
- Keep deterministic and offline-first expectations aligned with module AGENTS.

View File

@@ -28,7 +28,7 @@ Scope: defines the deterministic payload Orchestrator emits for job/run exports
- Timestamps UTC ISO-8601; no clock-skew correction performed by Ledger.
## Transport
- REST: `POST /internal/orchestrator/exports` (Orchestrator) → Findings Ledger ingest queue.
- REST: `POST /internal/jobengine/exports` (Orchestrator) → Findings Ledger ingest queue.
- Events: `orchestrator.export.created` carries the same payload; consumers must verify DSSE before persistence.
## Validation rules (Ledger side)

View File

@@ -12,7 +12,7 @@ Purpose: define the payload emitted by Telemetry SLO evaluators toward Notifier
```
{
"id": "uuid",
"tenant": "string", // required; aligns with orchestrator/telemetry tenant id
"tenant": "string", // required; aligns with jobengine/telemetry tenant id
"service": "string", // logical service name
"host": "string", // optional; k8s node/hostname
"slo": {

View File

@@ -1,327 +0,0 @@
# OpsMemory Module
> **Decision Ledger for Playbook Learning**
OpsMemory is a structured ledger of prior security decisions and their outcomes. It enables playbook learning - understanding which decisions led to good outcomes and surfacing institutional knowledge for similar situations.
## What OpsMemory Is
-**Decision + Outcome pairs**: Every security decision is recorded with its eventual outcome
-**Success/failure classification**: Learn what worked and what didn't
-**Similar situation matching**: Find past decisions in comparable scenarios
-**Playbook suggestions**: Surface recommendations based on historical success
## What OpsMemory Is NOT
- ❌ Chat history (that's conversation storage)
- ❌ Audit logs (that's the Timeline)
- ❌ VEX statements (that's Excititor)
## Architecture
```
┌─────────────────────────────────────────────────────────────┐
│ OpsMemory Service │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌──────────────────┐ ┌───────────────┐ │
│ │ Decision │ │ Playbook │ │ Outcome │ │
│ │ Recording │ │ Suggestion │ │ Tracking │ │
│ └──────┬──────┘ └────────┬─────────┘ └───────┬───────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ IOpsMemoryStore │ │
│ │ (PostgreSQL with similarity vectors) │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
## Core Components
### OpsMemoryRecord
The core data structure capturing a decision and its context:
```json
{
"memoryId": "mem-abc123",
"tenantId": "tenant-xyz",
"recordedAt": "2026-01-07T12:00:00Z",
"situation": {
"cveId": "CVE-2023-44487",
"component": "pkg:npm/http2@1.0.0",
"severity": "high",
"reachability": "reachable",
"epssScore": 0.97,
"isKev": true,
"contextTags": ["production", "external-facing", "payment-service"]
},
"decision": {
"action": "Remediate",
"rationale": "KEV + reachable + payment service = immediate remediation",
"decidedBy": "security-team",
"decidedAt": "2026-01-07T12:00:00Z",
"policyReference": "policy/critical-kev.rego"
},
"outcome": {
"status": "Success",
"resolutionTime": "4:30:00",
"lessonsLearned": "Upgrade was smooth, no breaking changes",
"recordedAt": "2026-01-07T16:30:00Z"
}
}
```
### Decision Actions
| Action | Description |
|--------|-------------|
| `Accept` | Accept the risk (no action) |
| `Remediate` | Upgrade/patch the component |
| `Quarantine` | Isolate the component |
| `Mitigate` | Apply compensating controls (WAF, config) |
| `Defer` | Defer for later review |
| `Escalate` | Escalate to security team |
| `FalsePositive` | Mark as not applicable |
### Outcome Status
| Status | Description |
|--------|-------------|
| `Success` | Decision led to successful resolution |
| `PartialSuccess` | Decision led to partial resolution |
| `Ineffective` | Decision was ineffective |
| `NegativeOutcome` | Decision led to negative consequences |
| `Pending` | Outcome still pending |
## API Reference
### Record a Decision
```http
POST /api/v1/opsmemory/decisions
Content-Type: application/json
{
"tenantId": "tenant-xyz",
"cveId": "CVE-2023-44487",
"componentPurl": "pkg:npm/http2@1.0.0",
"severity": "high",
"reachability": "reachable",
"epssScore": 0.97,
"action": "Remediate",
"rationale": "KEV + reachable + payment service",
"decidedBy": "alice@example.com",
"contextTags": ["production", "payment-service"]
}
```
**Response:**
```json
{
"memoryId": "abc123def456",
"recordedAt": "2026-01-07T12:00:00Z"
}
```
### Record an Outcome
```http
POST /api/v1/opsmemory/decisions/{memoryId}/outcome?tenantId=tenant-xyz
Content-Type: application/json
{
"status": "Success",
"resolutionTimeMinutes": 270,
"lessonsLearned": "Upgrade was smooth, no breaking changes",
"recordedBy": "alice@example.com"
}
```
### Get Playbook Suggestions
```http
GET /api/v1/opsmemory/suggestions?tenantId=tenant-xyz&cveId=CVE-2024-1234&severity=high&reachability=reachable
```
**Response:**
```json
{
"suggestions": [
{
"suggestedAction": "Remediate",
"confidence": 0.87,
"rationale": "87% confidence based on 15 similar past decisions. Remediation succeeded in 93% of high-severity reachable vulnerabilities.",
"successRate": 0.93,
"similarDecisionCount": 15,
"averageResolutionTimeMinutes": 180,
"evidence": [
{
"memoryId": "abc123",
"similarity": 0.92,
"action": "Remediate",
"outcome": "Success",
"cveId": "CVE-2023-44487"
}
],
"matchingFactors": [
"Same severity: high",
"Same reachability: Reachable",
"Both are KEV",
"Shared context: production"
]
}
],
"analyzedRecords": 15,
"topSimilarity": 0.92
}
```
### Query Past Decisions
```http
GET /api/v1/opsmemory/decisions?tenantId=tenant-xyz&action=Remediate&pageSize=20
```
### Get Statistics
```http
GET /api/v1/opsmemory/stats?tenantId=tenant-xyz
```
**Response:**
```json
{
"tenantId": "tenant-xyz",
"totalDecisions": 1250,
"decisionsWithOutcomes": 980,
"successRate": 0.87
}
```
## Similarity Algorithm
OpsMemory uses a 50-dimensional vector to represent each security situation:
| Dimensions | Feature |
|------------|---------|
| 0-9 | CVE category (memory, injection, auth, crypto, dos, etc.) |
| 10-14 | Severity (none, low, medium, high, critical) |
| 15-18 | Reachability (unknown, reachable, not-reachable, potential) |
| 19-23 | EPSS band (0-0.2, 0.2-0.4, 0.4-0.6, 0.6-0.8, 0.8-1.0) |
| 24-28 | CVSS band (0-2, 2-4, 4-6, 6-8, 8-10) |
| 29 | KEV flag |
| 30-39 | Component type (npm, maven, pypi, nuget, go, cargo, etc.) |
| 40-49 | Context tags (production, external-facing, payment, etc.) |
Similarity is computed using **cosine similarity** between normalized vectors.
## Integration Points
### Decision Recording Hook
OpsMemory integrates with the Findings Ledger to automatically capture decisions:
```csharp
public class OpsMemoryHook : IDecisionHook
{
public async Task OnDecisionRecordedAsync(FindingDecision decision)
{
var record = new OpsMemoryRecord
{
TenantId = decision.TenantId,
Situation = ExtractSituation(decision),
Decision = ExtractDecision(decision)
};
// Fire-and-forget to not block the decision flow
_ = _store.RecordDecisionAsync(record);
}
}
```
### Outcome Tracking
The OutcomeTrackingService monitors for resolution events and prompts users:
1. **Auto-detect resolution**: When a finding is marked resolved
2. **Calculate resolution time**: Time from decision to resolution
3. **Prompt for classification**: Ask user about outcome quality
4. **Link to original decision**: Update the OpsMemory record
## Configuration
```yaml
opsmemory:
connectionString: "Host=localhost;Database=stellaops"
similarity:
minThreshold: 0.6 # Minimum similarity for suggestions
maxResults: 10 # Maximum similar records to analyze
suggestions:
maxSuggestions: 3 # Maximum suggestions to return
minConfidence: 0.5 # Minimum confidence threshold
outcomeTracking:
autoPromptDelay: 24h # Delay before prompting for outcome
reminderInterval: 7d # Reminder interval for pending outcomes
```
## Database Schema
```sql
CREATE SCHEMA IF NOT EXISTS opsmemory;
CREATE TABLE opsmemory.decisions (
memory_id TEXT PRIMARY KEY,
tenant_id TEXT NOT NULL,
recorded_at TIMESTAMPTZ NOT NULL,
-- Situation (JSONB for flexibility)
situation JSONB NOT NULL,
-- Decision (JSONB)
decision JSONB NOT NULL,
-- Outcome (nullable, updated later)
outcome JSONB,
-- Similarity vector (array for simple cosine similarity)
similarity_vector REAL[] NOT NULL
);
CREATE INDEX idx_decisions_tenant ON opsmemory.decisions(tenant_id);
CREATE INDEX idx_decisions_recorded ON opsmemory.decisions(recorded_at DESC);
CREATE INDEX idx_decisions_cve ON opsmemory.decisions((situation->>'cveId'));
```
## Best Practices
### Recording Decisions
1. **Include context tags**: The more context, the better similarity matching
2. **Document rationale**: Future users benefit from understanding why
3. **Reference policies**: Link to the policy that guided the decision
### Recording Outcomes
1. **Be timely**: Record outcomes as soon as resolution is confirmed
2. **Be honest**: Failed decisions are valuable learning data
3. **Add lessons learned**: Help future users avoid pitfalls
### Using Suggestions
1. **Review evidence**: Look at the similar past decisions
2. **Check matching factors**: Ensure the situations are truly comparable
3. **Trust but verify**: Suggestions are guidance, not mandates
## Related Modules
- [Findings Ledger](../findings-ledger/README.md) - Source of decision events
- [Timeline](../timeline-indexer/README.md) - Audit trail
- [Excititor](../excititor/README.md) - VEX statement management
- [Risk Engine](../risk-engine/README.md) - Risk scoring

View File

@@ -1,393 +0,0 @@
# OpsMemory Architecture
> **Technical deep-dive into the Decision Ledger**
## Overview
OpsMemory provides a structured approach to organizational learning from security decisions. It captures the complete lifecycle of a decision - from the situation context through the action taken to the eventual outcome.
## Design Principles
### 1. Determinism First
All operations produce deterministic, reproducible results:
- Similarity vectors are computed from stable inputs
- Confidence scores use fixed formulas
- No randomness in suggestion ranking
### 2. Multi-Tenant Isolation
Every operation is scoped to a tenant:
- Records cannot be accessed across tenants
- Similarity search is tenant-isolated
- Statistics are per-tenant
### 3. Fire-and-Forget Integration
Decision recording is async and non-blocking:
- UI decisions complete immediately
- OpsMemory recording happens in background
- Failures don't affect the primary flow
### 4. Offline Capable
All features work without network access:
- Local PostgreSQL storage
- No external API dependencies
- Self-contained similarity computation
## Component Architecture
```
┌────────────────────────────────────────────────────────────────────┐
│ WebService Layer │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ OpsMemoryEndpoints │ │
│ │ POST /decisions GET /decisions GET /suggestions GET /stats│ │
│ └──────────────────────────────────────────────────────────────┘ │
└────────────────────────────────┬───────────────────────────────────┘
┌────────────────────────────────┼───────────────────────────────────┐
│ Service Layer │
│ ┌─────────────────┐ ┌─────────────────┐ ┌────────────────────┐ │
│ │ PlaybookSuggest │ │ OutcomeTracking │ │ SimilarityVector │ │
│ │ Service │ │ Service │ │ Generator │ │
│ └────────┬────────┘ └────────┬────────┘ └─────────┬──────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ IOpsMemoryStore │ │
│ └──────────────────────────────────────────────────────────────┘ │
└────────────────────────────────┬───────────────────────────────────┘
┌────────────────────────────────┼───────────────────────────────────┐
│ Storage Layer │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ PostgresOpsMemoryStore │ │
│ │ - Decision CRUD │ │
│ │ - Outcome updates │ │
│ │ - Similarity search (array-based cosine) │ │
│ │ - Query with pagination │ │
│ │ - Statistics aggregation │ │
│ └──────────────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────────────┘
```
## Data Model
### OpsMemoryRecord
The core aggregate containing all decision information:
```csharp
public sealed record OpsMemoryRecord
{
public required string MemoryId { get; init; }
public required string TenantId { get; init; }
public required DateTimeOffset RecordedAt { get; init; }
public required SituationContext Situation { get; init; }
public required DecisionRecord Decision { get; init; }
public OutcomeRecord? Outcome { get; init; }
public ImmutableArray<float> SimilarityVector { get; init; }
}
```
### SituationContext
Captures the security context at decision time:
```csharp
public sealed record SituationContext
{
public string? CveId { get; init; }
public string? Component { get; init; } // PURL
public string? Severity { get; init; } // low/medium/high/critical
public ReachabilityStatus Reachability { get; init; }
public double? EpssScore { get; init; } // 0-1
public double? CvssScore { get; init; } // 0-10
public bool IsKev { get; init; }
public ImmutableArray<string> ContextTags { get; init; }
}
```
### DecisionRecord
The action taken and why:
```csharp
public sealed record DecisionRecord
{
public required DecisionAction Action { get; init; }
public required string Rationale { get; init; }
public required string DecidedBy { get; init; }
public required DateTimeOffset DecidedAt { get; init; }
public string? PolicyReference { get; init; }
public MitigationDetails? Mitigation { get; init; }
}
```
### OutcomeRecord
The result of the decision:
```csharp
public sealed record OutcomeRecord
{
public required OutcomeStatus Status { get; init; }
public TimeSpan? ResolutionTime { get; init; }
public string? ActualImpact { get; init; }
public string? LessonsLearned { get; init; }
public required string RecordedBy { get; init; }
public required DateTimeOffset RecordedAt { get; init; }
}
```
## Similarity Algorithm
### Vector Generation
The `SimilarityVectorGenerator` creates 50-dimensional feature vectors:
```
Vector Layout:
[0-9] : CVE category one-hot (memory, injection, auth, crypto, dos,
info-disclosure, privilege-escalation, xss, path-traversal, other)
[10-14] : Severity one-hot (none, low, medium, high, critical)
[15-18] : Reachability one-hot (unknown, reachable, not-reachable, potential)
[19-23] : EPSS band one-hot (0-0.2, 0.2-0.4, 0.4-0.6, 0.6-0.8, 0.8-1.0)
[24-28] : CVSS band one-hot (0-2, 2-4, 4-6, 6-8, 8-10)
[29] : KEV flag (0 or 1)
[30-39] : Component type one-hot (npm, maven, pypi, nuget, go, cargo,
deb, rpm, apk, other)
[40-49] : Context tag presence (production, development, staging,
external-facing, internal, payment, auth, data, api, frontend)
```
### Cosine Similarity
Similarity between vectors A and B:
```
similarity = (A · B) / (||A|| × ||B||)
```
Where `A · B` is the dot product and `||A||` is the L2 norm.
### CVE Classification
CVEs are classified by analyzing keywords in the CVE ID and description:
| Category | Keywords |
|----------|----------|
| memory | buffer, overflow, heap, stack, use-after-free |
| injection | sql, command, code injection, ldap |
| auth | authentication, authorization, bypass |
| crypto | cryptographic, encryption, key |
| dos | denial of service, resource exhaustion |
| info-disclosure | information disclosure, leak |
| privilege-escalation | privilege escalation, elevation |
| xss | cross-site scripting, xss |
| path-traversal | path traversal, directory traversal |
## Playbook Suggestion Algorithm
### Confidence Calculation
```csharp
confidence = baseSimilarity
× successRateBonus
× recencyBonus
× evidenceCountBonus
```
Where:
- `baseSimilarity`: Highest similarity score from matching records
- `successRateBonus`: `1 + (successRate - 0.5) * 0.5` (rewards high success rate)
- `recencyBonus`: More recent decisions weighted higher
- `evidenceCountBonus`: More evidence = higher confidence
### Suggestion Ranking
1. Group past decisions by action taken
2. For each action, calculate:
- Average similarity of records with that action
- Success rate for that action
- Number of similar decisions
3. Compute confidence score
4. Rank by confidence descending
5. Return top N suggestions
### Rationale Generation
Rationales are generated programmatically:
```
"{confidence}% confidence based on {count} similar past decisions.
{action} succeeded in {successRate}% of {factors}."
```
## Storage Design
### PostgreSQL Schema
```sql
CREATE TABLE opsmemory.decisions (
memory_id TEXT PRIMARY KEY,
tenant_id TEXT NOT NULL,
recorded_at TIMESTAMPTZ NOT NULL,
-- Denormalized situation fields for indexing
cve_id TEXT,
component TEXT,
severity TEXT,
-- Full data as JSONB
situation JSONB NOT NULL,
decision JSONB NOT NULL,
outcome JSONB,
-- Similarity vector as array (not pgvector)
similarity_vector REAL[] NOT NULL
);
-- Indexes
CREATE INDEX idx_decisions_tenant ON opsmemory.decisions(tenant_id);
CREATE INDEX idx_decisions_recorded ON opsmemory.decisions(recorded_at DESC);
CREATE INDEX idx_decisions_cve ON opsmemory.decisions(cve_id) WHERE cve_id IS NOT NULL;
CREATE INDEX idx_decisions_component ON opsmemory.decisions(component) WHERE component IS NOT NULL;
```
### Why Not pgvector?
The current implementation uses PostgreSQL arrays instead of pgvector:
1. **Simpler deployment**: No extension installation required
2. **Smaller dataset**: OpsMemory is per-org, not global
3. **Adequate performance**: Array operations are fast enough for <100K records
4. **Future option**: Can migrate to pgvector if needed
### Cosine Similarity in SQL
```sql
-- Cosine similarity between query vector and stored vectors
SELECT memory_id,
(
SELECT SUM(a * b)
FROM UNNEST(similarity_vector, @query_vector) AS t(a, b)
) / (
SQRT((SELECT SUM(a * a) FROM UNNEST(similarity_vector) AS t(a))) *
SQRT((SELECT SUM(b * b) FROM UNNEST(@query_vector) AS t(b)))
) AS similarity
FROM opsmemory.decisions
WHERE tenant_id = @tenant_id
ORDER BY similarity DESC
LIMIT @top_k;
```
## API Design
### Endpoint Overview
| Method | Path | Description |
|--------|------|-------------|
| POST | `/api/v1/opsmemory/decisions` | Record a new decision |
| GET | `/api/v1/opsmemory/decisions/{id}` | Get decision details |
| POST | `/api/v1/opsmemory/decisions/{id}/outcome` | Record outcome |
| GET | `/api/v1/opsmemory/suggestions` | Get playbook suggestions |
| GET | `/api/v1/opsmemory/decisions` | Query past decisions |
| GET | `/api/v1/opsmemory/stats` | Get statistics |
### Request/Response DTOs
The API uses string-based DTOs that convert to/from internal enums:
```csharp
// API accepts strings
public record RecordDecisionRequest
{
public required string Action { get; init; } // "Remediate", "Accept", etc.
public string? Reachability { get; init; } // "reachable", "not-reachable"
}
// Internal uses enums
public enum DecisionAction { Accept, Remediate, Quarantine, ... }
public enum ReachabilityStatus { Unknown, Reachable, NotReachable, Potential }
```
## Testing Strategy
### Unit Tests (26 tests)
**SimilarityVectorGeneratorTests:**
- Vector dimension validation
- Feature encoding (severity, reachability, EPSS, CVSS, KEV)
- Component type classification
- Context tag encoding
- Vector normalization
- Cosine similarity computation
- Matching factor detection
**PlaybookSuggestionServiceTests:**
- Empty history handling
- Single record suggestions
- Multiple record ranking
- Confidence calculation
- Rationale generation
- Evidence linking
### Integration Tests (5 tests)
**PostgresOpsMemoryStoreTests:**
- Decision persistence and retrieval
- Outcome updates
- Tenant isolation
- Query filtering
- Statistics calculation
## Performance Considerations
### Indexing Strategy
- Primary key on `memory_id` for direct lookups
- Index on `tenant_id` for isolation
- Index on `recorded_at` for recent-first queries
- Partial indexes on `cve_id` and `component` for filtered queries
### Query Optimization
- Limit similarity search to last N days by default
- Return only top-K similar records
- Use cursor-based pagination for large result sets
### Caching
Currently no caching (records are infrequently accessed). Future options:
- Cache similarity vectors in memory
- Cache recent suggestions per tenant
- Use read replicas for heavy read loads
## Future Enhancements
### pgvector Migration
If dataset grows significantly:
1. Install pgvector extension
2. Add vector column with IVFFlat index
3. Replace array-based similarity with vector operations
4. ~100x speedup for large datasets
### ML-Based Suggestions
Replace rule-based confidence with ML model:
1. Train on historical decision-outcome pairs
2. Include more features (time of day, team, etc.)
3. Use gradient boosting or neural network
4. Continuous learning from new outcomes
### Outcome Prediction
Predict outcome before decision is made:
1. Use past outcomes as training data
2. Predict success probability per action
3. Show predicted outcomes in UI
4. Track prediction accuracy over time

View File

@@ -1,316 +0,0 @@
# OpsMemory Chat Integration
> **Connecting Decision Memory to AI-Assisted Workflows**
## Overview
The OpsMemory Chat Integration connects organizational decision memory to AdvisoryAI Chat, enabling:
1. **Context Enrichment**: Past relevant decisions surface automatically in chat
2. **Decision Recording**: New decisions from chat actions are auto-recorded
3. **Feedback Loop**: Outcomes improve future AI suggestions
4. **Object Linking**: Structured references to decisions, issues, and tactics
## Architecture
```
┌──────────────────────────────────────────────────────────────────────┐
│ Chat Session │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ User: "What should we do about CVE-2023-44487?" │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ OpsMemoryChatProvider.EnrichContextAsync() │ │
│ │ → Query similar past decisions │ │
│ │ → Include known issues and tactics │ │
│ │ → Return top-3 with outcomes │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ Prompt Assembly (via AdvisoryAiPromptContextEnricher) │ │
│ │ System: "Previous similar situations..." │ │
│ │ - CVE-2022-41903 (same category): Accepted, SUCCESS │ │
│ │ - CVE-2023-1234 (similar severity): Quarantined, SUCCESS │ │
│ │ Known Issues: [ops-mem:issue-xyz123] may apply │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ Assistant Response with Object Links: │ │
│ │ "Based on 3 similar past decisions [ops-mem:dec-abc123]..." │ │
│ │ [Accept Risk]{action:approve,cve_id=CVE-2023-44487} │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ (if action executed) │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ OpsMemoryDecisionRecorder.RecordFromActionAsync() │ │
│ │ → Extract situation from chat context │ │
│ │ → Record decision with action, rationale │ │
│ │ → Link to Run attestation │ │
│ └────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
```
## Core Components
### IOpsMemoryChatProvider
The main interface for chat context enrichment:
```csharp
public interface IOpsMemoryChatProvider
{
/// <summary>
/// Enriches chat context with relevant past decisions.
/// </summary>
Task<OpsMemoryChatContext> EnrichContextAsync(
ChatEnrichmentRequest request,
CancellationToken ct = default);
/// <summary>
/// Records a decision made during a chat session.
/// </summary>
Task RecordDecisionAsync(
ChatDecisionRecord record,
CancellationToken ct = default);
}
```
**Location:** `src/AdvisoryAI/StellaOps.AdvisoryAI/Chat/Integration/IOpsMemoryChatProvider.cs`
### OpsMemoryChatProvider
Implementation that queries OpsMemory and formats results for chat:
- **Similarity Search**: Finds past decisions with similar CVE/severity/category
- **Known Issues**: Includes relevant documented issues
- **Tactics**: Surfaces applicable response tactics
- **Fire-and-Forget Recording**: Async decision capture without blocking UX
**Location:** `src/AdvisoryAI/StellaOps.AdvisoryAI/Chat/Integration/OpsMemoryChatProvider.cs`
### AdvisoryAiPromptContextEnricher
Transforms OpsMemory context into AI prompt format:
```csharp
public interface IAdvisoryAiPromptContextEnricher
{
/// <summary>
/// Enriches AI prompt with OpsMemory context.
/// </summary>
Task<PromptEnrichmentResult> EnrichAsync(
PromptEnrichmentRequest request,
CancellationToken ct = default);
}
```
**Location:** `src/AdvisoryAI/StellaOps.AdvisoryAI/Chat/Integration/AdvisoryAiPromptContextEnricher.cs`
## Object Link Format
OpsMemory uses structured object links for cross-referencing:
| Type | Format | Example |
|------|--------|---------|
| Decision | `[ops-mem:dec-{id}]` | `[ops-mem:dec-abc12345]` |
| Known Issue | `[ops-mem:issue-{id}]` | `[ops-mem:issue-xyz98765]` |
| Tactic | `[ops-mem:tactic-{id}]` | `[ops-mem:tactic-respond-001]` |
| Playbook | `[ops-mem:playbook-{id}]` | `[ops-mem:playbook-log4j-response]` |
### Link Resolution
The `OpsMemoryLinkResolver` resolves object links to display text and URLs:
```csharp
public interface IObjectLinkResolver
{
/// <summary>
/// Resolves an object link to display information.
/// </summary>
Task<ObjectLinkResolution?> ResolveAsync(
string objectLink,
CancellationToken ct = default);
}
```
**Example Resolution:**
```
Input: [ops-mem:dec-abc12345]
Output:
- DisplayText: "Accept Risk decision for CVE-2022-41903"
- Url: "/opsmemory/decisions/abc12345"
- Metadata: { outcome: "SUCCESS", actor: "security-team" }
```
## Configuration
```yaml
AdvisoryAI:
Chat:
OpsMemory:
# Enable OpsMemory integration
Enabled: true
# Maximum number of similar decisions to surface
MaxSuggestions: 3
# Minimum similarity score (0.0-1.0)
MinSimilarity: 0.5
# Include known issues in context
IncludeKnownIssues: true
# Include response tactics
IncludeTactics: true
# Automatically record decisions from actions
RecordDecisions: true
OpsMemory:
Integration:
# Link recorded decisions to AI Run attestations
AttestationLinking: true
# Don't block chat flow on recording
FireAndForget: true
```
## Known Issues and Tactics
### Known Issues
Document common false positives or expected behaviors:
```csharp
public interface IKnownIssueStore
{
Task<KnownIssue?> GetByIdAsync(string id, CancellationToken ct);
Task<IReadOnlyList<KnownIssue>> SearchAsync(
KnownIssueSearchRequest request, CancellationToken ct);
}
```
**Example Known Issue:**
```json
{
"id": "issue-log4j-test-code",
"title": "Log4j in test dependencies",
"description": "Log4j detected in test-scope dependencies is not exploitable in production",
"applies_to": {
"cve_pattern": "CVE-2021-44228",
"scope": "test"
},
"recommended_action": "accept_risk",
"status": "active"
}
```
### Response Tactics
Pre-defined response strategies for common situations:
```csharp
public interface ITacticStore
{
Task<Tactic?> GetByIdAsync(string id, CancellationToken ct);
Task<IReadOnlyList<Tactic>> GetMatchingTacticsAsync(
TacticMatchRequest request, CancellationToken ct);
}
```
**Example Tactic:**
```json
{
"id": "tactic-quarantine-critical",
"name": "Quarantine Critical Vulnerabilities",
"trigger": {
"severity": ["CRITICAL"],
"reachability": ["REACHABLE", "UNKNOWN"]
},
"steps": [
"Block deployment to production",
"Notify security team",
"Schedule remediation within 24h"
],
"automation": {
"action": "quarantine",
"notify_channel": "#security-alerts"
}
}
```
## Integration Points
### AdvisoryAI Integration
Register OpsMemory integration during startup:
```csharp
services.AddAdvisoryAIOpsMemoryIntegration(options =>
{
options.Enabled = true;
options.MaxSuggestions = 3;
options.IncludeKnownIssues = true;
options.IncludeTactics = true;
});
```
### Chat Flow Integration
The integration hooks into the chat pipeline:
1. **Pre-Prompt**: `AdvisoryAiPromptContextEnricher` adds OpsMemory context
2. **Response**: AI references past decisions with object links
3. **Post-Action**: `OpsMemoryChatProvider.RecordDecisionAsync` captures the decision
## Best Practices
### Tuning Similarity Threshold
- **0.3-0.5**: Broader matches, may include less relevant decisions
- **0.5-0.7**: Balanced - recommended starting point
- **0.7+**: Strict matching, only very similar situations
### Recording Quality Decisions
For decisions to be useful for future suggestions:
1. **Include Context**: CVE ID, severity, package information
2. **Clear Rationale**: Why this action was chosen
3. **Track Outcomes**: Update with SUCCESS/FAILURE after implementation
### Managing Known Issues
- Review quarterly for relevance
- Archive issues for CVEs that are fully remediated
- Keep issue descriptions actionable
## Testing
### Unit Tests
Located in `src/AdvisoryAI/__Tests/StellaOps.AdvisoryAI.Tests/Chat/Integration/`:
- `OpsMemoryChatProviderTests.cs` - Provider functionality
- `AdvisoryAiPromptContextEnricherTests.cs` - Prompt enrichment
### Integration Tests
Located in `src/OpsMemory/__Tests/StellaOps.OpsMemory.Tests/Integration/`:
- `OpsMemoryChatProviderIntegrationTests.cs` - Full flow with PostgreSQL
## Related Documentation
- [OpsMemory Architecture](architecture.md) - Core OpsMemory design
- [AdvisoryAI Architecture](../advisory-ai/architecture.md) - AI assistant design
- [Decision Recording API](../../api/opsmemory.md) - REST API reference
---
_Last updated: 10-Jan-2026_

View File

@@ -1,49 +0,0 @@
# Packs Registry
> Task packs registry and distribution service.
## Purpose
PacksRegistry provides a centralized registry for distributable task packs, policy packs, and analyzer bundles. It enables versioned pack management with integrity verification and air-gap support.
## Quick Links
- [Architecture](./architecture.md) - Technical design and implementation details
- [Guides](./guides/) - Usage and configuration guides
## Status
| Attribute | Value |
|-----------|-------|
| **Maturity** | Production |
| **Last Reviewed** | 2025-12-29 |
| **Maintainer** | Platform Guild |
## Key Features
- **Centralized Registry**: Store and manage task packs, policy packs, and analyzer bundles
- **Versioned Management**: Semantic versioning with upgrade/downgrade support
- **Content-Addressed**: All packs are content-addressed with integrity verification
- **Offline Distribution**: Bundle export for air-gapped environments
## Dependencies
### Upstream (this module depends on)
- **PostgreSQL** - Pack metadata storage
- **RustFS/S3** - Pack content storage
- **Authority** - Authentication and authorization
### Downstream (modules that depend on this)
- **TaskRunner** - Consumes packs for execution
## Configuration
```yaml
packs_registry:
storage_backend: rustfs # or s3
max_pack_size_mb: 100
```
## Related Documentation
- [TaskRunner Architecture](../task-runner/architecture.md)

View File

@@ -1,100 +0,0 @@
# component_architecture_packsregistry.md - **Stella Ops PacksRegistry** (2025Q4)
> Task packs registry and distribution service.
> **Scope.** Implementation-ready architecture for **PacksRegistry**: the registry for task packs, policy packs, and analyzer packs that can be distributed to TaskRunner instances.
---
## 0) Mission & boundaries
**Mission.** Provide a **centralized registry** for distributable task packs, policy packs, and analyzer bundles. Enable versioned pack management with integrity verification and air-gap support.
**Boundaries.**
* PacksRegistry **stores and distributes** packs; it does not execute them.
* Pack execution is handled by **TaskRunner**.
* All packs are **content-addressed** with integrity verification.
* Supports **offline distribution** via bundle export.
---
## 1) Solution & project layout
```
src/PacksRegistry/StellaOps.PacksRegistry/
├─ StellaOps.PacksRegistry.Core/ # Pack models, validation
├─ StellaOps.PacksRegistry.Infrastructure/ # Storage, distribution
├─ StellaOps.PacksRegistry.Persistence.EfCore/ # EF Core persistence
├─ StellaOps.PacksRegistry.WebService/ # REST API
├─ StellaOps.PacksRegistry.Worker/ # Background processing
└─ StellaOps.PacksRegistry.Tests/
src/PacksRegistry/__Libraries/
└─ StellaOps.PacksRegistry.Persistence/ # Persistence abstractions
```
---
## 2) External dependencies
* **PostgreSQL** - Pack metadata storage
* **RustFS/S3** - Pack content storage
* **Authority** - Authentication and authorization
* **TaskRunner** - Pack consumer
---
## 3) Contracts & data model
### 3.1 Pack
```json
{
"packId": "policy-baseline-v2",
"version": "2.1.0",
"type": "policy",
"name": "Baseline Security Policy",
"description": "Standard security policy pack",
"digest": "sha256:abc123...",
"size": 45678,
"publishedAt": "2025-01-15T10:30:00Z",
"author": "stellaops",
"dependencies": [],
"metadata": {
"minRunnerVersion": "1.5.0"
}
}
```
### 3.2 Pack Types
| Type | Description |
|------|-------------|
| `policy` | Policy rule packs |
| `analyzer` | Scanner analyzer packs |
| `task` | TaskRunner task definitions |
| `bundle` | Composite packs |
---
## 4) REST API
```
GET /packs → { packs: PackSummary[] }
GET /packs/{id} → { pack: Pack }
GET /packs/{id}/versions → { versions: Version[] }
GET /packs/{id}/{version} → binary content
POST /packs { manifest, content } → { packId }
DELETE /packs/{id}/{version} → { deleted: bool }
GET /healthz | /readyz | /metrics
```
---
## Related Documentation
* TaskRunner: `../taskrunner/architecture.md`
* Policy: `../policy/architecture.md`

View File

@@ -1,208 +0,0 @@
> **Imposed rule:** Work of this type or tasks of this type on this component must also be applied everywhere else it should be applied.
# Task Pack Authoring Guide
This guide teaches engineers how to design, validate, and publish Task Packs that align with the Sprint43 specification. Follow these steps to ensure deterministic behaviour, secure approvals, and smooth hand-off to operators.
---
## 1·Prerequisites
- StellaOps CLI `>= 2025.10.0` with pack commands enabled.
- Authority client configured with `packs.write` (publish) and `packs.run` (local testing) scopes.
- Access to Task Runner staging environment for validation runs.
- Familiarity with the [Task Pack Specification](spec.md) and [Packs Registry](registry.md).
- Optional: connection to DevOps staging registry or Offline Kit mirror for publishing.
---
## 2·Design Checklist
1. **Define objective.** Document the operational need, inputs, expected outputs, and rollback strategy.
2. **Identify approvals.** Determine which scopes/roles must sign off (`packs.approve` assignments).
3. **Plan security posture.** Limit secrets usage, set tenant visibility, and note network constraints (sealed mode).
4. **Model observability.** Decide which metrics, logs, and evidence artifacts are critical for post-run audits.
5. **Reuse libraries.** Prefer built-in modules or shared pack fragments to reduce drift.
Capture the above in `docs/summary.md` (optional but recommended) for future maintainers.
---
## 3·Authoring Workflow
### 3.1 Scaffold project
```bash
mkdir my-pack
cd my-pack
stella pack init --name sbom-remediation
```
`stella pack init` creates baseline files:
- `pack.yaml` with metadata placeholders.
- `schemas/inputs.schema.json` (sample).
- `docs/usage.md` (template for human instructions).
- `.packignore` to exclude build artifacts.
### 3.2 Define inputs & schemas
- Use JSON Schema (`draft-2020-12`) for input validation.
- Avoid optional inputs unless there is a deterministic default.
- Store schemas under `schemas/` and reference via relative paths.
### 3.3 Compose steps
- Break workflow into small deterministic steps.
- Name each step with stable `id`.
- Wrap scripts/tools using built-in modules; copy scripts to `assets/` if necessary.
- Use `when` expressions for branch logic; ensure expressions rely solely on inputs or previous outputs.
- For loops, adopt `map` with capped iteration count; avoid data-dependent randomness.
### 3.4 Configure approvals
- Add `spec.approvals` entries for each required review.
- Capture the metadata Authority enforces: `runId`, `gateId`, and `planHash` should be documented so approvers can pass them through `stella pack approve --pack-run-id/--pack-gate-id/--pack-plan-hash` (see `docs/modules/packs-registry/guides/runbook.md#4-approvals-workflow`).
- Provide informative `reasonTemplate` with placeholders.
- Set `expiresAfter` to match operational policy (e.g., 4h for security reviews).
- Document fallback contacts in `docs/runbook.md`.
### 3.5 Manage secrets
- Declare secrets under `spec.secrets`.
- Reference secrets via expressions (e.g., `{{ secrets.jiraToken.value }}`) inside modules that support secure injection.
- Never bake secrets or tokens into pack assets.
- If secret optional, set `optional: true` and handle absence in step logic.
### 3.6 Document outputs
- List expected artifacts under `spec.outputs`.
- Include human-friendly docs (Markdown) describing each output and how to access it through CLI or Console.
---
## 4·Validation
### 4.1 Static validation
```bash
stella pack validate
```
Checks performed:
- Schema compliance (YAML, JSON Schema).
- Determinism guard (forbidden functions, clock usage, network allowlist).
- Reference integrity (assets, schemas, documentation).
- Approval/secret scope availability.
### 4.2 Simulation & plan hash
```bash
stella pack plan --inputs samples/inputs.json --output .artifacts/plan.json
stella pack simulate --inputs samples/inputs.json --output .artifacts/sim.json
```
- Review plan graph to ensure step ordering and gating align with expectations.
- Store simulation output with pack metadata for future audits.
### 4.3 Local rehearsal
```bash
stella pack run \
--inputs samples/inputs.json \
--secrets jiraToken=@secrets/jira.txt \
--dry-run
```
- Use `--dry-run` to verify approvals and outputs without side effects.
- Real runs require `packs.run` and all approval gates satisfied (e.g., via CLI prompts or Console).
### 4.4 Unit tests (optional but encouraged)
- Create a `tests/` folder with CLI-driven regression scripts (e.g., using `stella pack plan` + `jq` assertions).
- Integrate into CI pipelines; ensure tests run offline using cached assets.
---
## 5·Publishing
### 5.1 Build bundle
```bash
stella pack build \
--output dist/sbom-remediation-1.3.0.stella-pack.tgz \
--manifest pack.yaml
```
### 5.2 Sign bundle
```bash
cosign sign-blob \
--yes \
--output-signature dist/sbom-remediation-1.3.0.sig \
dist/sbom-remediation-1.3.0.stella-pack.tgz
```
Store signature alongside bundle; DSSE optional but recommended (see [security guidance](../security/pack-signing-and-rbac.md)).
### 5.3 Publish to registry
```bash
stella pack push \
registry.stella-ops.org/packs/sbom-remediation:1.3.0 \
--bundle dist/sbom-remediation-1.3.0.stella-pack.tgz \
--signature dist/sbom-remediation-1.3.0.sig
```
Registry verifies signature, stores provenance, and updates index.
### 5.4 Offline distribution
- Export bundle + signature + provenance into Offline Kit using `stella pack bundle export`.
- Update mirror manifest (`manifest/offline-manifest.json`) with new pack entries.
---
## 6·Versioning & Compatibility
- Follow SemVer (increment major when breaking schema/behaviour).
- Document compatibility in `docs/compatibility.md` (recommended).
- Registry retains immutable history; use `metadata.deprecated: true` to indicate retirement.
---
## 7·Best Practices
- **Keep steps idempotent.** Support manual retries without side effects.
- **Surface evidence early.** Export intermediate artifacts (plans, logs) for operators.
- **Localize messages.** Provide `locales/en-US.json` for CLI/Console strings (Sprint43 requirement).
- **Avoid long-running commands.** Split heavy tasks into smaller steps with progress telemetry.
- **Guard network usage.** Use `when: "{{ env.isSealed }}"` to block disallowed network operations or provide offline instructions.
- **Document fallbacks.** Include manual recovery instructions in `docs/runbook.md`.
---
## 8·Hand-off & Review
- Submit PR including pack bundle metadata, docs, and validation evidence.
- Request review from Task Runner + Security + DevOps stakeholders.
- Attach `stella pack plan` output and signature digest to review notes.
- After approval, update change log (`docs/CHANGELOG.md`) and notify Task Runner operations.
---
## 9·Compliance Checklist
- [ ] Metadata, inputs, steps, approvals, secrets, and outputs defined per spec.
- [ ] Schemas provided for all object inputs and outputs.
- [ ] Determinism validation (`stella pack validate`) executed with evidence stored.
- [ ] Plan + simulation artifacts committed in `.artifacts/` or CI evidence store.
- [ ] Bundle signed (cosign/DSSE) and signature recorded.
- [ ] Runbook and troubleshooting notes documented.
- [ ] Offline distribution steps prepared (bundle export + manifest update).
- [ ] Imposed rule reminder retained at top.
---
*Last updated: 2025-10-27 (Sprint43).*

View File

@@ -1,184 +0,0 @@
> **Imposed rule:** Work of this type or tasks of this type on this component must also be applied everywhere else it should be applied.
# Packs Registry Architecture & Operations
The Packs Registry stores, verifies, and serves Task Pack bundles across environments. It integrates with Authority for RBAC, Task Runner for execution, DevOps for release automation, and Offline Kit for air-gapped distribution.
---
## 1·Service Overview
- **Service name:** `StellaOps.PacksRegistry`
- **Interfaces:** REST/GraphQL API, OCI-compatible registry endpoints, event streams for mirroring.
- **Data stores:** PostgreSQL (`packs`, `pack_versions`, `pack_provenance` tables), object storage (bundle blobs, signatures), timeline events.
- **Dependencies:** Authority scopes (`packs.*`), Export Center (manifests), DevOps signing service, Notifications (optional).
---
## 2·Core Concepts
| Concept | Description |
|---------|-------------|
| **Pack record** | Immutable entry representing a pack version; includes metadata, digest, signatures, tenant visibility. |
| **Channel** | Logical distribution channel (`stable`, `edge`, `beta`, custom). Controls mirroring/promotion flows. |
| **Provenance** | DSSE statements + SBOM linking pack bundle to source repo, CLI build, and Task Runner compatibility. |
| **Mirroring policy** | Rules specifying which packs replicate to downstream registries or Offline Kit bundles. |
| **Audit trail** | Append-only log capturing publish/update/delete actions, approvals, and policy evaluations. |
---
## 3·API Surface
### 3.1 REST Endpoints
| Method | Path | Description | Scopes |
|--------|------|-------------|--------|
| `GET` | `/api/packs` | List packs with filters (`name`, `channel`, `tenant`, `tag`). | `packs.read` |
| `GET` | `/api/packs/{packId}/versions` | List versions with metadata, provenance. | `packs.read` |
| `GET` | `/api/packs/{packId}/versions/{version}` | Retrieve manifest, signatures, compatibility matrix. | `packs.read` |
| `POST` | `/api/packs/{packId}/versions` | Publish new version (bundle upload or OCI reference). | `packs.write` |
| `POST` | `/api/packs/{packId}/promote` | Promote version between channels (edge→stable). | `packs.write` + approval policy |
| `DELETE` | `/api/packs/{packId}/versions/{version}` | Deprecate version (soft delete, immutability preserved). | `packs.write` |
| `GET` | `/api/packs/{packId}/events` | Stream audit events (SSE). | `packs.read` |
### 3.2 OCI Endpoints
The registry exposes OCI-compatible endpoints (`/v2/<namespace>/<pack>/...`) supporting:
- `PUT`/`PATCH`/`GET` for manifests and blobs.
- Content-addressed digests using SHA-256.
- Annotations for pack metadata (`org.opencontainers.image.title`, `io.stellaops.pack.metadata`).
### 3.3 GraphQL (Optional)
GraphQL endpoint (`/api/graphql`) enables advanced queries (filter by approvals, tags, compatibility). Under active development; reference API schema once published.
---
## 4·Publishing Workflow
1. CLI/CI calls `POST /api/packs/{id}/versions` with signed bundle.
2. Registry verifies:
- Manifest schema compliance.
- Signature (cosign/DSSE) validity.
- Authority scopes (`packs.write`).
- Tenant visibility constraints.
3. On success, registry stores bundle, provenance, and emits event (`pack.version.published`).
4. Optional promotion requires additional approvals or integration with DevOps release boards.
All actions recorded in audit log:
```json
{
"id": "evt_01HF...",
"type": "pack.version.published",
"packId": "sbom-remediation",
"version": "1.3.0",
"actor": "user:alice",
"tenant": "west-prod",
"source": "cli/2025.10.0",
"signatures": ["sha256:..."],
"metadataHash": "sha256:..."
}
```
---
## 5·Mirroring & Offline Support
- **Automatic mirroring:** Configure policies to push packs to secondary registries (edge clusters, regional mirrors) or object stores.
- **Offline Kit integration:** `ops/offline-kit` pipeline pulls packs matching specified channels and writes them to `offline/packs/manifest.json` with signatures.
- **Checksum manifest:** Registry maintains `digestmap.json` listing pack digests + signatures; offline installers verify before import.
- **Sealed mode:** Registry can operate in read-only mode for sealed environments; publishing disabled except via offline import command (`stella pack mirror import`).
---
## 6·Security & Compliance
- Enforce Authority scopes; tokens without tenant or required scope are rejected (`ERR_PACK_SCOPE`).
- Signatures verified using trusted Fulcio/KMS roots; optional mirror trust bundles configured via `registry.trustBundle`.
- RBAC mapping:
| Role | Scopes | Capabilities |
|------|--------|--------------|
| `PackViewer` | `packs.read` | Browse, fetch manifests/bundles. |
| `PackPublisher` | `packs.read`, `packs.write` | Publish/promote, manage channels (subject to policy). |
| `PackApprover` | `packs.read`, `packs.approve` | Approve promotions, override tenant visibility (with audit logging). |
| `PackOperator` | `packs.read`, `packs.run` | Execute packs (via CLI/Task Runner). |
- Audit events forwarded to Authority + Evidence Locker.
- Built-in malware/secret scanning runs on bundle upload (configurable via DevOps pipeline).
See [pack signing & RBAC guidance](../security/pack-signing-and-rbac.md) for deeper controls.
---
## 7·Observability
- Metrics (`registry` namespace):
- `pack_publish_total{result}` success/failure counts.
- `pack_signature_verify_seconds` verification latency.
- `pack_channel_promotions_total` promotions per channel.
- `pack_mirror_queue_depth` pending mirror jobs.
- Logs (structured JSON with `packId`, `version`, `actor`, `tenant`, `digest`).
- Traces instrument bundle verification, storage writes, and mirror pushes.
- Alerting suggestions:
- Publish failure rate >5% (5m window) triggers DevOps escalation.
- Mirror lag >15m surfaces to Ops dashboard.
---
## 8·Schema & Metadata Extensions
- Default metadata stored under `metadata.*` from manifest.
- Registry supplements with:
- `compatibility.cli` (supported CLI versions).
- `compatibility.runner` (Task Runner build requirements).
- `provenance.attestations[]` (URIs).
- `channels[]` (current channel assignments).
- `tenantVisibility[]`.
- `deprecated` flag + replacement hints.
Extensions must be deterministic and derived from signed bundle data.
---
## 9·Operations
- **Backups:** Daily snapshots of PostgreSQL tables + object storage, retained for 30days.
- **Retention:** Old versions retained indefinitely; mark as `deprecated` instead of deleting.
- **Maintenance:**
- Run `registry vacuum` weekly to prune orphaned blobs.
- Rotate signing keys per security policy (document in `pack-signing-and-rbac`).
- Validate trust bundles quarterly.
- **Disaster recovery:**
- Restore database + object storage.
- Rebuild OCI indexes (`registry rebuild-index`).
- Replay audit events for downstream systems.
---
## 10·Compliance Checklist
- [ ] REST + OCI endpoints documented with required scopes.
- [ ] Publishing flow covers signature verification, audit logging, and promotion policies.
- [ ] Mirroring/offline strategy recorded (policies, manifests, sealed mode notes).
- [ ] RBAC roles and scope mapping defined.
- [ ] Observability metrics, logs, and alerts described.
- [ ] Operations guidance covers backups, rotation, disaster recovery.
- [ ] Imposed rule reminder included at top of document.
## 11·TP Gap Remediation (2025-12)
- **Signed registry record (TP7):** Every pack version stores DSSE envelopes for bundle + attestation, SBOM path, and revocation list reference. Imports fail-closed when signatures or revocation proofs are missing.
- **Offline bundle schema (TP8):** Registry exports offline artefacts that must satisfy `docs/modules/packs-registry/guides/packs-offline-bundle.schema.json`; publish pipeline invokes `scripts/packs/verify_offline_bundle.py --require-dsse` before promotion.
- **Hash ledger (TP1/TP2):** Publish step writes `hashes[]` (sha256) for manifest, canonical plan, `inputs.lock`, approvals ledger, SBOM, and revocations; digests surface in audit events and `digestmap.json`.
- **Sandbox + quotas (TP6):** Registry metadata carries `sandbox.mode`, explicit egress allowlists, CPU/memory limits, and quota seconds; Task Runner refuses packs missing these fields.
- **SLO + alerting (TP9):** Pack metadata includes SLOs (`runP95Seconds`, `approvalP95Seconds`, `maxQueueDepth`); registry emits metrics/alerts when declared SLOs are exceeded during publish/import flows.
- **Fail-closed imports (TP10):** Import/mirror paths abort when DSSE, hash entries, or revocation files are absent or stale, returning actionable error codes for CLI/Task Runner.
- **Approval ledger schema:** Registry exposes `docs/modules/packs-registry/guides/approvals-ledger.schema.json` for DSSE approval records (planHash must be `sha256:<64-hex>`); import validation rejects non-conforming ledgers.
---
*Last updated: 2025-12-05 (Sprint0157-0001-0001 TaskRunner I).*

View File

@@ -1,163 +0,0 @@
> **Imposed rule:** Work of this type or tasks of this type on this component must also be applied everywhere else it should be applied.
# Task Pack Operations Runbook
This runbook guides SREs and on-call engineers through executing, monitoring, and troubleshooting Task Packs using the Task Runner service, Packs Registry, and StellaOps CLI. It aligns with Sprint43 deliverables (approvals workflow, notifications, chaos resilience).
---
## 1·Quick Reference
| Action | Command / UI | Notes |
|--------|--------------|-------|
| Validate pack | `stella pack validate --bundle <file>` | Run before publishing or importing. |
| Plan pack run | `stella pack plan --inputs inputs.json` | Outputs plan hash, required approvals, secret summary. |
| Execute pack | `stella pack run --pack <id>:<version>` | Streams logs; prompts for secrets/approvals if allowed. |
| Approve gate | Console notifications or `stella pack approve --run <id> --gate <gate>` | Requires `packs.approve`. |
| View run | Console `/console/packs/runs/:id` or `stella pack runs show <id>` | SSE stream available for live status. |
| Export evidence | `stella pack runs export --run <id>` | Produces bundle with plan, logs, artifacts, attestations. |
---
## 2·Run Lifecycle
1. **Submission**
- CLI/Orchestrator submits run with inputs, pack version, tenant context.
- Task Runner validates pack hash, scopes, sealed-mode constraints.
2. **Plan & Simulation**
- Runner caches plan graph; optional simulation diff recorded.
3. **Approvals**
- Gates emit notifications (`NOTIFY-SVC-40-001`).
- Approvers can approve/resume via CLI, Console, or API.
4. **Execution**
- Steps executed per plan (sequential/parallel).
- Logs streamed via SSE (`/task-runner/runs/{id}/logs`).
5. **Evidence & Attestation**
- On completion, DSSE attestation + evidence bundle stored.
- Exports available via Export Center.
6. **Cleanup**
- Artifacts retained per retention policy (default 30d).
- Mirror pack run manifest to Offline Kit if configured.
---
## 3·Monitoring & Telemetry
- **Metrics dashboards:** `task-runner` Grafana board.
- `pack_run_active` active runs per tenant.
- `pack_step_duration_seconds` histograms per step type.
- `pack_gate_wait_seconds` approval wait time (alert >30m).
- `pack_run_success_ratio` success vs failure rate.
- **Logs:** Search by `runId`, `packId`, `tenant`, `stepId`.
- **Traces:** Query `taskrunner.run` span in Tempo/Jaeger.
- **Notifications:** Subscribe to `pack.run.*` topics via Notifier for Slack/email/PagerDuty hooks.
Observability configuration referenced in Task Runner tasks (OBS-50-001..55-001).
---
## 4·Approvals Workflow
- Approvals may be requested via Console banner, CLI prompt, or email/Slack.
- Approver roles: `packs.approve` + tenant membership.
- CLI command:
```bash
stella pack approve \
--run run:tenant:timestamp \
--gate security-review \
--comment "Validated remediation scope; proceeding."
```
- Metadata parameters are mandatory: `--pack-run-id`, `--pack-gate-id`, and `--pack-plan-hash` map 1:1 to the Authority token parameters (`pack_run_id`, `pack_gate_id`, `pack_plan_hash`). The CLI resolves sensible defaults from `stella pack plan`, but operators can override them explicitly for out-of-band runs. Authority `/token` rejects `packs.approve` requests missing any of these fields and records the failure in `authority.pack_scope_violation`. Keep this section (and `docs/security/pack-signing-and-rbac.md`) handy—the Authority team references it as the canonical procedure.
- Auto-expiry triggers run cancellation (configurable per gate).
- Approval events logged and included in evidence bundle.
---
## 5·Secrets Handling
- Secrets retrieved via Authority secure channel or CLI profile.
- Task Runner injects secrets into isolated environment variables or temp files (auto-shredded).
- Logs redact secrets; evidence bundles include only secret metadata (name, scope, last four characters).
- For sealed mode, secrets must originate from sealed vault (configured via `TASKRUNNER_SEALED_VAULT_URL`).
---
## 6·Failure Recovery
| Scenario | Symptom | Resolution |
|----------|---------|------------|
| **Plan hash mismatch** | Run aborted with `ERR_PACK_HASH_MISMATCH`. | Re-run `stella pack plan`; ensure pack not modified post-plan. |
| **Approval timeout** | `ERR_PACK_APPROVAL_TIMEOUT`. | Requeue run with extended TTL or escalate to approver; verify notifications delivered. |
| **Secret missing** | Run fails at injection step. | Provide secret via CLI (`--secrets`) or configure profile; check Authority scope. |
| **Network blocked (sealed)** | `ERR_PACK_NETWORK_BLOCKED`. | Update pack to avoid external calls or whitelist domain via AirGap policy. |
| **Artifact upload failure** | Evidence missing, logs show storage errors. | Retry run with `--resume` (if supported); verify object storage health. |
| **Runner chaos trigger** | Run paused with chaos event note. | Review chaos test plan; resume if acceptable or cancel run. |
`stella pack runs resume --run <id>` resumes paused runs post-remediation (approvals or transient failures).
---
## 7·Chaos & Resilience
- Chaos hooks pause runs, drop network, or delay approvals to test resilience.
- Track chaos events via `pack.chaos.injected` timeline entries.
- Post-chaos, ensure metrics return to baseline; record findings in Ops log.
---
## 8·Offline & Air-Gapped Execution
- Use `stella pack mirror pull` to import packs into sealed registry.
- CLI caches bundles under `~/.stella/packs/` for offline runs.
- Approvals require offline process:
- Generate approval request bundle (`stella pack approve --offline-request`).
- Approver signs bundle using offline CLI.
- Import approval via `stella pack approve --offline-response`.
- Evidence bundles exported to removable media; verify checksums before upload to online systems.
---
## 9·Runbooks for Common Packs
Maintain per-pack playbooks in `docs/modules/packs-registry/guides/runbook/<pack-name>.md`. Include:
- Purpose and scope.
- Required inputs and secrets.
- Approval stakeholders.
- Pre-checks and post-checks.
- Rollback procedures.
The Docs Guild can use this root runbook as a template.
---
## 10·Escalation Matrix
| Issue | Primary | Secondary | Notes |
|-------|---------|-----------|-------|
| Pack validation errors | DevEx/CLI Guild | Task Runner Guild | Provide pack bundle + validation output. |
| Approval pipeline failure | Task Runner Guild | Authority Core | Confirm scope/role mapping. |
| Registry outage | Packs Registry Guild | DevOps Guild | Use mirror fallback if possible. |
| Evidence integrity issues | Evidence Locker Guild | Security Guild | Validate DSSE attestations, escalate if tampered. |
Escalations must include run ID, tenant, pack version, plan hash, and timestamps.
---
## 11·Compliance Checklist
- [ ] Run lifecycle documented (submission → evidence).
- [ ] Monitoring metrics, logs, traces, and notifications captured.
- [ ] Approvals workflow instructions provided (CLI + Console).
- [ ] Secret handling, sealed-mode constraints, and offline process described.
- [ ] Failure scenarios + recovery steps listed.
- [ ] Chaos/resilience guidance included.
- [ ] Escalation matrix defined.
- [ ] Imposed rule reminder included at top.
---
*Last updated: 2025-10-27 (Sprint43).*

View File

@@ -1,261 +0,0 @@
> **Imposed rule:** Work of this type or tasks of this type on this component must also be applied everywhere else it should be applied.
# Task Pack Specification (Sprint43 Draft)
The Task Pack specification defines a deterministic, auditable format that enables operators to encode multi-step maintenance, validation, and deployment workflows. Packs are executed by the Task Runner service, distributed through the Packs Registry, and invoked via the StellaOps CLI (`stella pack ...`) or Orchestrator integrations.
---
## 1·Goals & Scope
- **Deterministic execution.** Identical inputs yield identical run graphs, output manifests, and evidence bundles across environments (online, sealed, or offline).
- **Secure-by-default.** Pack metadata must capture provenance, signatures, RBAC requirements, and secret usage; execution enforces tenant scopes and approvals.
- **Portable.** Packs are distributed as signed OCI artifacts or tarballs that work in connected and air-gapped deployments, including Offline Kit mirrors.
- **Composable.** Packs can reference reusable steps, expressions, and shared libraries without sacrificing determinism or auditability.
Non-goals: full-blown workflow orchestration, unbounded scripting, or remote code injection. All logic is declarative and constrained to Task Runner capabilities.
---
## 2·Terminology
| Term | Definition |
|------|------------|
| **Pack manifest** | Primary YAML document (`pack.yaml`) describing metadata, inputs, steps, policies, and evidence expectations. |
| **Step** | Atomic unit of work executed by Task Runner (e.g., command, API call, policy gate, approval). Steps can be sequential or parallel. |
| **Expression** | Deterministic evaluation (JMESPath-like) used for branching, templating, and conditionals. |
| **Policy gate** | Declarative rule that blocks execution until conditions are met (e.g., approval recorded, external signal received). |
| **Artifact** | File, JSON blob, or OCI object produced by a step, referenced in manifests and evidence bundles. |
| **Pack bundle** | Distribution archive (`.stella-pack.tgz` or OCI ref) containing manifest, assets, schemas, and provenance metadata. |
---
## 3·Pack Layout
```
my-pack/
├─ pack.yaml # Required manifest
├─ assets/ # Optional static assets (scripts, templates)
├─ schemas/ # JSON schemas for inputs/outputs
├─ docs/ # Markdown docs rendered in Console/CLI help
├─ provenance/ # DSSE statements, SBOM, attestations
└─ README.md # Author-facing summary (optional)
```
Publishing via Packs Registry or OCI ensures the directory is canonical and hashed.
---
## 4·Manifest Schema (v1.0)
```yaml
apiVersion: stellaops.io/pack.v1
kind: TaskPack
metadata:
name: sbom-remediation
version: 1.3.0
description: >
Audit SBOM drift, quiet high-risk findings, and export mitigation evidence.
tags: [sbom, remediation, policy]
tenantVisibility: ["west-prod", "east-stage"] # optional allowlist
maintainers:
- name: Jane Doe
email: jane@example.com
license: BUSL-1.1
annotations:
imposedRuleReminder: true
spec:
inputs:
- name: sbomBundle
type: object
schema: schemas/sbom-bundle.schema.json
required: true
- name: dryRun
type: boolean
default: false
secrets:
- name: jiraToken
scope: packs.run # Authority scope required
description: Optional token for ticket automation
approvals:
- id: security-review
grants: ["packs.approve"]
expiresAfter: PT4H
reasonTemplate: "Approve remediation for SBOM {{ inputs.sbomBundle.metadata.image }}"
steps:
- id: validate-input
run:
uses: builtin:validate-schema
with:
target: "{{ inputs.sbomBundle }}"
schema: schemas/sbom-bundle.schema.json
- id: plan-remediation
when: "{{ not inputs.dryRun }}"
run:
uses: builtin:policy-simulate
with:
sbom: "{{ inputs.sbomBundle }}"
policy: "policies/remediation.yaml"
- id: approval-gate
gate:
approval: security-review
message: "Security must approve remediation before changes apply."
- id: apply-remediation
run:
uses: builtin:cli-command
with:
command: ["stella", "policy", "promote", "--from-pack"]
- id: export-evidence
run:
uses: builtin:evidence-export
with:
includeArtifacts: ["{{ steps.plan-remediation.outputs.planPath }}"]
outputs:
- name: evidenceBundle
type: file
path: "{{ steps.export-evidence.outputs.bundlePath }}"
success:
message: "Remediation applied; evidence bundle ready."
failure:
retries:
maxAttempts: 1
backoffSeconds: 0
message: "Remediation failed; see evidence bundle for context."
```
### 4.1 Field Summary
| Field | Description | Requirements |
|-------|-------------|--------------|
| `metadata` | Human-facing metadata; used for registry listings and RBAC hints. | `name` (DNS-1123), `version` (SemVer), `description`2048 chars. |
| `spec.inputs` | Declarative inputs validated at plan time. | Must include type; custom schema optional but recommended. |
| `spec.secrets` | Secrets requested at runtime; never stored in pack bundle. | Each secret references Authority scope; CLI prompts or injects from profiles. |
| `spec.approvals` | Named approval gates with required grants and TTL. | ID unique per pack; `grants` map to Authority roles. Approval metadata (`runId`, `gateId`, `planHash`) feeds Authoritys `pack_run_id`/`pack_gate_id`/`pack_plan_hash` parameters (see `docs/modules/packs-registry/guides/runbook.md#4-approvals-workflow`). |
| `spec.steps` | Execution graph; each step is `run`, `gate`, `parallel`, or `map`. | Steps must declare deterministic `uses` module and `id`. |
| `spec.outputs` | Declared artifacts for downstream automation. | `type` can be `file`, `object`, or `url`; path/expression required. |
| `success` / `failure` | Messages + retry policy. | `failure.retries.maxAttempts` + `backoffSeconds` default to 0. |
---
## 5·Step Types
| Type | Schema | Notes |
|------|--------|-------|
| `run` | Executes a built-in module (`builtin:*`) or registry-provided module. | Modules must be deterministic, side-effect constrained, and versioned. |
| `parallel` | Executes sub-steps concurrently; `maxParallel` optional. | Results aggregated; failures trigger abort unless `continueOnError`. |
| `map` | Iterates over deterministic list; each iteration spawns sub-step. | Sequence derived from expression result; ordering stable. |
| `gate.approval` | Blocks until approval recorded with required grants. | Supports `autoExpire` to cancel run on timeout. |
| `gate.policy` | Calls Policy Engine to ensure criteria met (e.g., no critical findings). | Fails run if gate not satisfied. |
`when` expressions must be pure (no side effects) and rely only on declared inputs or prior outputs.
---
## 6·Determinism & Validation
1. **Plan phase** (`stella pack plan`, `TaskRunner.Plan` API) parses manifest, resolves expressions, validates schemas, and emits canonical graph with hash.
2. **Simulation** compares plan vs dry-run results, capturing differences in `planDiff`. Required for approvals in sealed environments.
3. **Execution** uses plan hash to ensure runtime graph matches simulation. Divergence aborts run.
4. **Evidence**: Task Runner emits DSSE attestation referencing plan hash, input digests, and output artifacts.
Validation pipeline:
```text
pack.yaml ──▶ schema validation ──▶ expression audit ──▶ determinism guard ──▶ signing
```
Packs must pass CLI validation before publishing.
### 6.1·TP Gap Remediation (2025-12)
- **Canonical plan hash (TP1):** Compute `plan.hash` as `sha256:<64-hex>` over canonical JSON (`plan.canonicalPlanPath`) with sorted keys and normalized numbers/booleans. The canonical plan file ships in offline bundles.
- **Inputs lock (TP2):** CLI emits `inputs.lock` capturing resolved inputs and redacted secret placeholders; hashed via `hashes[]` and included in evidence bundles.
- **Approval ledger DSSE (TP3):** Approval responses are DSSE-signed ledgers embedding `runId`, `gateId`, `planHash`, and tenant context; Task Runner rejects approvals without matching plan hash.
- **Secret redaction (TP4):** `security.secretsRedactionPolicy` defines hashing/redaction for secrets and PII; transcripts/evidence must reference this policy.
- **Deterministic RNG/time (TP5):** RNG seed is derived from `plan.hash`; timestamps use UTC ISO-8601; log ordering is monotonic.
- **Sandbox + egress quotas (TP6):** Packs declare `sandbox.mode`, explicit `egressAllowlist`, CPU/memory limits, and optional `quotaSeconds`; missing fields cause fail-closed refusal.
- **Registry signing + revocation (TP7):** Bundles carry SBOM + DSSE envelopes and reference a revocation list enforced during registry import.
- **Offline bundle schema + verifier (TP8):** Offline exports must satisfy `docs/modules/packs-registry/guides/packs-offline-bundle.schema.json` and pass `scripts/packs/verify_offline_bundle.py --require-dsse`.
- **SLO + alerting (TP9):** Manifests declare `slo.runP95Seconds`, `slo.approvalP95Seconds`, `slo.maxQueueDepth`, and optional `slo.alertRules`; telemetry enforces and alerts on breaches.
- **Fail-closed gates (TP10):** Approval/policy/timeline gates fail closed when DSSE, hash entries, or quotas are missing/expired; CLI surfaces remediation hints.
- **Approval ledger schema:** Approval decisions must conform to `docs/modules/packs-registry/guides/approvals-ledger.schema.json`; planHash is `sha256:<64-hex>` and DSSE envelopes must reference ledger digest.
---
## 7·Signatures & Provenance
- Pack bundles are signed with **cosign** (keyless Fulcio/KMS supported) and optionally DSSE envelopes.
- `provenance/` directory stores signed statements (SLSA Build L1+) linking source repo, CI run, and manifest hash.
- Registry verifies signatures on push/pull; Task Runner refuses unsigned packs unless in development mode.
- Attestations include:
- Pack manifest digest (`sha256`)
- Pack bundle digest
- Build metadata (`git.ref`, `ci.workflow`, `cli.version`)
---
## 8·RBAC & Scopes
Authority scopes introduced by `AUTH-PACKS-41-001`:
| Scope | Purpose |
|-------|---------|
| `packs.read` | Discover packs, download manifests. |
| `packs.write` | Publish/update packs in registry (requires signature). |
| `packs.run` | Execute packs via CLI/Task Runner. |
| `packs.approve` | Fulfil approval gates defined in packs. |
Task Runner enforces scopes per tenant; pack metadata may further restrict tenant visibility (`metadata.tenantVisibility`).
---
## 9·Observability & Evidence
- Metrics: `pack_run_duration_seconds`, `pack_step_retry_total`, `pack_gate_wait_seconds`.
- Logs: Structured JSON per step with scrubbed inputs (`secretMask` applied).
- Timeline events: `pack.started`, `pack.approval.requested`, `pack.approval.granted`, `pack.completed`.
- Evidence bundle includes:
- Plan manifest (canonical JSON)
- Step transcripts (redacted)
- Artifacts manifest (sha256, size)
- Attestations references
---
## 10·Compatibility Matrix
| CLI Version | Pack API | Task Runner | Notes |
|-------------|----------|-------------|-------|
| 2025.10.x | `pack.v1` | Runner build `>=2025.10.0` | Approvals optional, loops disabled. |
| 2025.12.x | `pack.v1` | Runner build `>=2025.12.0` | Approvals resume, secrets injection, localization strings. |
| Future | `pack.v2` | TBD | Will introduce typed outputs & partial replay (track in Epic 13). |
CLI enforces compatibility: running pack with unsupported features yields `ERR_PACK_UNSUPPORTED`.
---
## 11·Publishing Workflow
1. Author pack (`pack.yaml`, assets, docs).
2. Run `stella pack validate` (schema + determinism).
3. Generate bundle: `stella pack build --output my-pack.stella-pack.tgz`.
4. Sign: `cosign sign-blob my-pack.stella-pack.tgz`.
5. Publish: `stella pack push registry.example.com/org/my-pack:1.3.0`.
6. Registry verifies signature, records provenance, and exposes pack via API.
---
## 12·Compliance Checklist
- [ ] Manifest schema documented for all fields, including approvals, secrets, and outputs.
- [ ] Determinism requirements outlined with plan/simulate semantics and CLI validation steps.
- [ ] Signing + provenance expectations spelled out with cosign/DSSE references.
- [ ] RBAC scopes (`packs.*`) and tenant visibility rules captured.
- [ ] Observability (metrics, logs, evidence) described for Task Runner integrations.
- [ ] Compatibility matrix enumerates CLI/Runner requirements.
- [ ] Publishing workflow documented with CLI commands.
- [ ] Imposed rule reminder included at top of document.
---
*Last updated: 2025-12-05 (Sprint0157-0001-0001 TaskRunner I).*

View File

@@ -19,3 +19,35 @@ This module aggregates cross-cutting contracts and guardrails that every StellaO
## Coordination
Platform docs are the starting point for new contributors; keep this summary in sync with module-specific dossiers and sprint references.
## Shared Storage Driver Contract (Sprint 312)
This contract is the default for all stateful StellaOps webservices unless a module ADR explicitly overrides it.
- `Storage:Driver`
- Accepted values: `postgres`, `inmemory`, `filesystem`.
- Production default: `postgres`.
- `inmemory` and `filesystem` are non-production/testing-only and must be explicitly configured.
- `Storage:ObjectStore:Driver`
- Accepted values: `rustfs`, `seed-fs`.
- Use only for blob/object payload channels (artifacts, snapshots, package blobs).
- `ConnectionStrings:Default`
- Required when `Storage:Driver=postgres` unless a service-specific connection key is provided.
- Service-specific key, when present, takes precedence over `ConnectionStrings:Default`.
Fail-fast policy:
- Non-development runtime must fail startup when required storage configuration is missing (no silent localhost/file fallback).
- Development runtime may use localhost/file defaults only when explicitly intended for local workflows.
Current implementation status (2026-03-05):
- `PacksRegistry`: Postgres metadata/state + seed-fs payload channel for pack/provenance/attestation blobs.
- `TaskRunner`: Postgres run state/log/approval + seed-fs artifact payload channel.
- `RiskEngine`: Postgres-backed result store (`riskengine.risk_score_results`) with explicit in-memory test fallback.
- `Replay`: Postgres snapshot index + seed-fs snapshot blob store.
- `OpsMemory`: connection precedence aligned to `ConnectionStrings:OpsMemory -> ConnectionStrings:Default`, with non-development fail-fast.
## Advisory Commitments (2026-02-26 Batch)
- `SPRINT_20260226_223_Platform_score_explain_contract_and_replay_alignment` defines deterministic score/explain/replay contract behavior for CLI and Web consumers.
- `SPRINT_20260226_230_Platform_locale_label_translation_corrections` completes locale label correction baseline for cross-language operator UI consistency.
- Cross-module advisory translation tracking is maintained in `docs/product/advisory-translation-20260226.md`.

View File

@@ -889,6 +889,8 @@ stella exception status <request-id>
- **Integration tests:** Joiners with sample SBOM/advisory/VEX data; materialisation with deterministic ordering; API contract tests generated from OpenAPI.
- **Property tests:** Ensure rule evaluation deterministic across permutations.
- **Golden tests:** Replay recorded runs, compare determinism hash.
- **Snapshot contract (Policy Engine tests):** Snapshot assertions resolve to source-controlled `src/Policy/__Tests/StellaOps.Policy.Engine.Tests/Snapshots/` via caller-file path. Regenerate with `UPDATE_SNAPSHOTS=1` only when intentional fixture changes are reviewed.
- **API auth fixture contract (PolicyEngineApiHostTests):** Test auth overrides run in fixture scope only, with deterministic in-memory resource-server settings (`Authority`, `RequireHttpsMetadata=false`) and canonical tenant claim `stellaops:tenant` so tenancy middleware and scope policies both evaluate in tests.
- **Performance tests:** Evaluate 100k component / 1M advisory dataset under warmed caches (<30s full run).
- **Chaos hooks:** Optional toggles to simulate upstream latency/failures; used in staging.
@@ -1288,3 +1290,25 @@ services.AddVerdictExplainability();
- `ProofGraphBuilderTests.cs` 18 tests (graph construction, determinism, depth hierarchy, critical paths, counterfactual overlay, edge cases)
- `ProofStudioServiceTests.cs` 10 tests (compose, score breakdown, guardrails, counterfactual, DI resolution)
## 15 · Advisory Gap Status (2026-03-04 Batch)
Status: implementation delivered in Sprint 306.
- `ScorePolicy` runtime contract now includes required `PolicyId`; `ScorePolicy.Default` emits deterministic ID `score-policy.default.v1`.
- Loader and validator behavior is aligned:
- `ScorePolicyLoader` enforces `policyVersion`, required `policyId`, schema validation, and deterministic load failures.
- Missing `policyId` now fails predictably with explicit error text.
- Schema ownership is canonicalized:
- runtime validator loads one canonical schema resource (`Schemas/score-policy.v1.schema.json`) embedded in `StellaOps.Policy`.
- source schema and embedded resource parity are guarded by tests.
- Section naming drift was removed; schema keys align with runtime serialization (`reachability`, `evidence`, `provenance`, `scoringProfile`).
- Existing policy tests and fixtures that build `ScorePolicy` were updated to include deterministic `policyId`.
Legacy fixture note:
- Older YAML fixtures without `policyId` are no longer valid and must be migrated by adding deterministic `policyId` values.
Closure sprint:
- `docs/implplan/SPRINT_20260304_306_Policy_score_policy_contract_consistency.md`

View File

@@ -62,5 +62,5 @@ Policy side:
## Integration References
- Evidence contract: `docs/modules/evidence-locker/promotion-evidence-contract.md`
- Promotion APIs: `docs/modules/release-orchestrator/api/promotions.md`
- Runtime closure plan: `docs/modules/release-orchestrator/promotion-runtime-gap-closure-plan.md`
- Promotion APIs: `docs/modules/release-jobengine/api/promotions.md`
- Runtime closure plan: `docs/modules/release-jobengine/promotion-runtime-gap-closure-plan.md`

View File

@@ -1,51 +1,20 @@
# Provenance
# StellaOps Provenance (Relocated)
> Provenance attestation library for SLSA/DSSE compliance.
> **Sprint 204 (2026-03-04):** The Provenance module source has been consolidated under the Attestor trust domain.
> Source code is now at `src/Attestor/StellaOps.Provenance.Attestation/` and `src/Attestor/StellaOps.Provenance.Attestation.Tool/`.
> Architecture documentation is now in the [Attestor architecture dossier](../attestor/architecture.md#trust-domain-model-sprint-204----2026-03-04).
> Archived standalone docs are in `docs-archived/modules/provenance/`.
## Purpose
## Purpose (unchanged)
Provenance provides deterministic, verifiable provenance attestations for all StellaOps artifacts. It enables SLSA compliance through DSSE statement generation, Merkle tree construction, and cryptographic verification.
Provenance is a **library** (not a standalone service) that provides deterministic, verifiable provenance attestations for all StellaOps artifacts. It enables SLSA compliance through DSSE statement generation, Merkle tree construction, and cryptographic verification.
## Quick Links
## Note on StellaOps.Provenance (shared library)
- [Architecture](./architecture.md) - Technical design and implementation details
- [Guides](./guides/) - Attestation generation guides
The `src/__Libraries/StellaOps.Provenance/` library is a separate, lower-level provenance data model used by Concelier and other consumers. It was NOT moved by Sprint 204 and remains at its original location.
## Status
## Why the move
| Attribute | Value |
|-----------|-------|
| **Maturity** | Production |
| **Last Reviewed** | 2025-12-29 |
| **Maintainer** | Security Guild |
Provenance attestation libraries are consumed primarily by the Attestor trust domain (proofchain, evidence packs, verification). Consolidating source ownership under `src/Attestor/` clarifies trust-boundary responsibilities.
## Key Features
- **DSSE Statement Generation**: Build provenance attestations per DSSE spec
- **SLSA Compliance**: Support for SLSA build predicates
- **Merkle Tree Construction**: Content-addressed integrity verification
- **Promotion Attestations**: Track artifact promotions across environments
- **Verification Harness**: Validate attestation chains
## Dependencies
### Upstream (this module depends on)
- **Signer/KMS** - Key management for signing (delegated)
### Downstream (modules that depend on this)
- **Attestor** - Stores generated attestations
- **EvidenceLocker** - Evidence bundle attestations
- **ExportCenter** - Export attestations
## Notes
Provenance is a **library**, not a standalone service. It does not:
- Store attestations (handled by Attestor and EvidenceLocker)
- Hold signing keys (delegated to Signer/KMS)
All attestation outputs are deterministic with canonical JSON serialization.
## Related Documentation
- [Attestor Architecture](../attestor/architecture.md)
- [DSSE Specification](../../security/trust-and-signing.md)
See the [Trust Domain Model](../attestor/architecture.md#trust-domain-model-sprint-204----2026-03-04) for details.

View File

@@ -1,316 +0,0 @@
# component_architecture_provenance.md - **Stella Ops Provenance** (2025Q4)
> Provenance attestation library for SLSA/DSSE compliance.
> **Scope.** Library architecture for **Provenance**: shared libraries and tooling for generating, signing, and verifying provenance attestations (DSSE/SLSA). Used by evidence bundles, exports, and timeline verification flows.
---
## 0) Mission & boundaries
**Mission.** Provide **deterministic, verifiable provenance attestations** for all StellaOps artifacts. Enable SLSA compliance through DSSE statement generation, Merkle tree construction, and cryptographic verification.
**Boundaries.**
* Provenance is a **library**, not a standalone service.
* Provenance **does not** store attestations. Storage is handled by Attestor and EvidenceLocker.
* Provenance **does not** hold signing keys. Key management is delegated to Signer/KMS.
* All attestation outputs are **deterministic** with canonical JSON serialization.
---
## 1) Solution & project layout
```
src/Provenance/
├─ StellaOps.Provenance.Attestation/ # Core attestation library
│ ├─ AGENTS.md # Guild charter
│ ├─ PromotionAttestation.cs # Promotion statement builder
│ ├─ BuildModels.cs # SLSA build predicate models
│ ├─ Signers.cs # Signer abstractions
│ ├─ Verification.cs # Verification harness
│ └─ Hex.cs # Hex encoding utilities
├─ StellaOps.Provenance.Attestation.Tool/ # CLI tool for attestation
│ ├─ Program.cs
│ └─ README.md
└─ __Tests/
└─ StellaOps.Provenance.Attestation.Tests/
├─ PromotionAttestationBuilderTests.cs
├─ VerificationTests.cs
├─ SignersTests.cs
├─ MerkleTreeTests.cs
└─ RotatingSignerTests.cs
```
---
## 2) External dependencies
* **Signer** - Cryptographic signing operations
* **Cryptography** - Hash computation, signature algorithms
* **EvidenceLocker** - Consumes attestations for storage
* **ExportCenter** - Attaches attestations to export bundles
* **.NET 10** - Runtime target
---
## 3) Contracts & data model
### 3.1 DSSE Statement
Dead Simple Signing Envelope (DSSE) format:
```json
{
"payloadType": "application/vnd.in-toto+json",
"payload": "<base64-encoded-statement>",
"signatures": [
{
"keyid": "sha256:abc123...",
"sig": "<base64-signature>"
}
]
}
```
### 3.2 SLSA Provenance Predicate
```json
{
"_type": "https://in-toto.io/Statement/v1",
"subject": [
{
"name": "pkg:oci/scanner@sha256:abc123",
"digest": { "sha256": "abc123..." }
}
],
"predicateType": "https://slsa.dev/provenance/v1",
"predicate": {
"buildDefinition": {
"buildType": "https://stellaops.dev/build/v1",
"externalParameters": {},
"internalParameters": {},
"resolvedDependencies": []
},
"runDetails": {
"builder": {
"id": "https://stellaops.dev/builders/scanner"
},
"metadata": {
"invocationId": "build-2025-01-15-abc123",
"startedOn": "2025-01-15T10:30:00Z",
"finishedOn": "2025-01-15T10:35:00Z"
}
}
}
}
```
### 3.3 Promotion Attestation
For artifact promotion across environments:
```csharp
public sealed class PromotionAttestation
{
public required string ArtifactDigest { get; init; }
public required string SourceEnvironment { get; init; }
public required string TargetEnvironment { get; init; }
public required DateTimeOffset PromotedAt { get; init; }
public required string ApprovedBy { get; init; }
public required IReadOnlyList<string> PolicyDigests { get; init; }
public string? MerkleRoot { get; init; }
}
```
---
## 4) Core Components
### 4.1 Signer Abstractions
```csharp
public interface IAttestationSigner
{
string KeyId { get; }
string Algorithm { get; }
Task<byte[]> SignAsync(
ReadOnlyMemory<byte> payload,
CancellationToken ct);
}
public interface IRotatingSigner : IAttestationSigner
{
DateTimeOffset KeyNotBefore { get; }
DateTimeOffset KeyNotAfter { get; }
Task<IAttestationSigner> GetCurrentSignerAsync(CancellationToken ct);
}
```
### 4.2 Verification Harness
```csharp
public interface IAttestationVerifier
{
Task<VerificationResult> VerifyAsync(
DsseEnvelope envelope,
VerificationOptions options,
CancellationToken ct);
}
public sealed record VerificationResult
{
public required bool IsValid { get; init; }
public required string KeyId { get; init; }
public DateTimeOffset? SignedAt { get; init; }
public IReadOnlyList<string>? Warnings { get; init; }
public string? ErrorMessage { get; init; }
}
```
### 4.3 Merkle Tree Utilities
For evidence chain verification:
```csharp
public static class MerkleTree
{
public static string ComputeRoot(IEnumerable<string> leaves);
public static IReadOnlyList<string> ComputePath(
IReadOnlyList<string> leaves,
int leafIndex);
public static bool VerifyPath(
string leaf,
IReadOnlyList<string> path,
string root);
}
```
---
## 5) CLI Tool
`StellaOps.Provenance.Attestation.Tool` provides CLI commands:
```bash
# Generate provenance attestation
provenance-tool generate \
--subject "pkg:oci/scanner@sha256:abc123" \
--builder "stellaops/ci" \
--output attestation.json
# Sign attestation
provenance-tool sign \
--input attestation.json \
--key-id "kms://keys/signing-key" \
--output attestation.dsse.json
# Verify attestation
provenance-tool verify \
--input attestation.dsse.json \
--trust-root trust-bundle.json
# Generate promotion attestation
provenance-tool promote \
--artifact "sha256:abc123" \
--from staging \
--to production \
--approver "user@example.com"
```
---
## 6) Security & compliance
* **SLSA L3 compliance**: Build provenance with hermetic builds
* **Key rotation**: RotatingSigner supports key rotation with overlap
* **Determinism**: Canonical JSON ensures reproducible digests
* **Offline verification**: Trust bundles for air-gapped verification
* **Threat model**: Reviewed before each release
---
## 7) Performance targets
* **Statement generation**: < 10ms for typical attestation
* **Signing**: Depends on KMS (target < 100ms for HSM)
* **Verification**: < 50ms for single signature
* **Merkle root**: < 100ms for 10,000 leaves
---
## 8) Testing matrix
* **Serialization tests**: Deterministic JSON output across runs
* **Signing tests**: Round-trip sign/verify
* **Merkle tests**: Path generation and verification
* **Rotation tests**: Key rotation with overlap handling
* **Integration tests**: Full attestation flow with mock KMS
---
## 9) Sample Artifacts
Samples committed under `samples/provenance/`:
```
samples/provenance/
├─ slsa-provenance-v1.json # Sample SLSA statement
├─ promotion-attestation.json # Sample promotion
├─ trust-bundle.json # Sample trust root
└─ verify-example.sh # Verification script
```
---
## 10) Integration Points
### 10.1 EvidenceLocker
Evidence bundles include attestations:
```json
{
"bundleId": "eb-2025-01-15-abc123",
"attestations": [
{
"type": "slsa-provenance",
"dsse": { /* DSSE envelope */ }
}
]
}
```
### 10.2 ExportCenter
Exports attach attestations to manifests:
```json
{
"exportId": "export-abc123",
"manifest": { /* export manifest */ },
"attestation": { /* DSSE envelope */ }
}
```
### 10.3 CLI
Scanner and other tools generate attestations:
```bash
stella scan image:tag --attest --output sbom.cdx.json
# Produces sbom.cdx.json + sbom.cdx.json.dsse
```
---
## Related Documentation
* Attestor architecture: `../attestor/architecture.md`
* Signer architecture: `../signer/architecture.md`
* EvidenceLocker: `../evidence-locker/architecture.md`
* SLSA specification: https://slsa.dev/provenance/v1
* DSSE specification: https://github.com/secure-systems-lab/dsse

View File

@@ -1,355 +0,0 @@
# Inline DSSE Provenance
> **Status:** Draft aligns with the November2025 advisory “store DSSE attestation refs inline on every SBOM/VEX event node.”
> **Owners:** Authority Guild · Feedser Guild · Platform Guild · Docs Guild.
This document defines how Stella Ops records provenance for SBOM, VEX, scan, and derived events: every event node in the PostgreSQL event store includes DSSE + Rekor references and verification metadata so audits and replay become first-class queries.
---
## 1. Event patch (PostgreSQL schema)
```jsonc
{
"_id": "evt_...",
"kind": "SBOM|VEX|SCAN|INGEST|DERIVED",
"subject": {
"purl": "pkg:nuget/example@1.2.3",
"digest": { "sha256": "..." },
"version": "1.2.3"
},
"provenance": {
"dsse": {
"envelopeDigest": "sha256:...",
"payloadType": "application/vnd.in-toto+json",
"key": {
"keyId": "cosign:SHA256-PKIX:ABC...",
"issuer": "fulcio",
"algo": "ECDSA"
},
"rekor": {
"logIndex": 1234567,
"uuid": "b3f0...",
"integratedTime": 1731081600,
"mirrorSeq": 987654 // optional
},
"chain": [
{ "type": "build", "id": "att:build#...", "digest": "sha256:..." },
{ "type": "sbom", "id": "att:sbom#...", "digest": "sha256:..." }
]
}
},
"trust": {
"verified": true,
"verifier": "Authority@stella",
"witnesses": 1,
"policyScore": 0.92
},
"ts": "2025-11-11T12:00:00Z"
}
```
### Key fields
| Field | Description |
|-------|-------------|
| `provenance.dsse.envelopeDigest` | SHA-256 of the DSSE envelope (not payload). |
| `provenance.dsse.payloadType` | Usually `application/vnd.in-toto+json`. |
| `provenance.dsse.key` | Key fingerprint / issuer / algorithm. |
| `provenance.dsse.rekor` | Rekor transparency log metadata (index, UUID, integrated time). |
| `provenance.dsse.chain` | Optional chain of dependent attestations (build → sbom → scan). |
| `trust.*` | Result of local verification (DSSE signature, Rekor proof, policy). |
---
## 2. Write path (ingest flow)
1. **Obtain provenance metadata** for each attested artifact (build, SBOM, VEX, scan). The CI script (`scripts/publish_attestation_with_provenance.sh`) captures `envelopeDigest`, Rekor `logIndex`/`uuid`, and key info.
2. **Authority/Feedser** verify the DSSE + Rekor proof (local cosign/rekor libs or the Signer service) and set `trust.verified = true`, `trust.verifier = "Authority@stella"`, `trust.witnesses = 1`.
3. **Attach** the provenance block before appending the event to PostgreSQL, using `StellaOps.Provenance.Postgres` helpers.
4. **Backfill** historical events by resolving known subjects → attestation digests and running an update script.
### 2.1 Supplying metadata from Concelier statements
Concelier ingestion jobs can now inline provenance when they create advisory statements. Add an `AdvisoryProvenance` entry with `kind = "dsse"` (or `dsse-metadata` / `attestation-dsse`) and set `value` to the same JSON emitted by the CI snippet. `AdvisoryEventLog` and `AdvisoryMergeService` automatically parse that entry, hydrate `AdvisoryStatementInput.Provenance/Trust`, and persist the metadata alongside the statement.
```json
{
"source": "attestor",
"kind": "dsse",
"value": "{ \"dsse\": { \"envelopeDigest\": \"sha256:…\", \"payloadType\": \"application/vnd.in-toto+json\" }, \"trust\": { \"verified\": true, \"verifier\": \"Authority@stella\" } }",
"recordedAt": "2025-11-10T00:00:00Z"
}
```
Providing the metadata during ingestion keeps new statements self-contained and reduces the surface that the `/events/statements/{statementId}/provenance` endpoint needs to backfill later.
Reference helper: `src/__Libraries/StellaOps.Provenance.Postgres/ProvenancePostgresExtensions.cs`.
---
### 2.2 Advisory AI structured chunk schema (GHSA/Cisco parity)
Advisory AI consumes the canonical `Advisory` aggregate and emits structured chunks that mirror GHSA GraphQL and Cisco PSIRT provenance anchors. The response contract is:
```jsonc
{
"advisoryKey": "CVE-2025-0001",
"fingerprint": "<sha256 of canonical advisory>",
"total": 3,
"truncated": false,
"entries": [
{
"type": "workaround", // sorted by (type, observationPath, documentId)
"chunkId": "c0ffee12", // sha256(advisory.observationId + observationPath)[:16]
"content": { /* structured field */ },
"provenance": {
"documentId": "tenant-a:chunk:newest", // PostgreSQL id of backing observation
"observationPath": "/references/0", // JSON Pointer into the observation
"source": "nvd",
"kind": "workaround",
"value": "tenant-a:chunk:newest",
"recordedAt": "2025-01-07T00:00:00Z",
"fieldMask": ["/references/0"]
}
}
]
}
```
Determinism requirements:
- Order entries by `(type, observationPath, documentId)` to keep cache keys stable across nodes.
- Always include the advisory `fingerprint` in cache keys and responses.
- Preserve observation-level provenance by emitting both `documentId` and `observationPath` under `provenance`.
These anchors let Attestor/Console deep-link evidence and allow offline mirrors to prove origin without merging transforms.
---
## 3. CI/CD snippet
See `scripts/publish_attestation_with_provenance.sh`:
```bash
rekor-cli upload --rekor_server "$REKOR_URL" \
--artifact "$ATTEST_PATH" --type dsse --format json > rekor-upload.json
LOG_INDEX=$(jq '.LogIndex' rekor-upload.json)
UUID=$(jq -r '.UUID' rekor-upload.json)
ENVELOPE_SHA256=$(sha256sum "$ATTEST_PATH" | awk '{print $1}')
cat > provenance-meta.json <<EOF
{
"subject": { "imageRef": "$IMAGE_REF", "digest": { "sha256": "$IMAGE_DIGEST" } },
"dsse": {
"envelopeDigest": "sha256:$ENVELOPE_SHA256",
"payloadType": "application/vnd.in-toto+json",
"key": { "keyId": "$KEY_ID", "issuer": "$KEY_ISSUER", "algo": "$KEY_ALGO" },
"rekor": { "logIndex": $LOG_INDEX, "uuid": "$UUID", "integratedTime": $(jq '.IntegratedTime' rekor-upload.json) }
}
}
EOF
```
Feedser ingests this JSON and maps it to `DsseProvenance` + `TrustInfo`.
---
## 4. PostgreSQL indexes
Create indexes to keep provenance queries fast (PostgreSQL DDL):
```sql
-- events_by_subject_kind_provenance
CREATE INDEX events_by_subject_kind_provenance
ON events (subject_digest_sha256, kind, provenance_dsse_rekor_log_index);
-- events_unproven_by_kind
CREATE INDEX events_unproven_by_kind
ON events (kind, trust_verified, provenance_dsse_rekor_log_index);
-- events_by_rekor_logindex
CREATE INDEX events_by_rekor_logindex
ON events (provenance_dsse_rekor_log_index);
-- events_by_envelope_digest (partial index for non-null values)
CREATE INDEX events_by_envelope_digest
ON events (provenance_dsse_envelope_digest)
WHERE provenance_dsse_envelope_digest IS NOT NULL;
-- events_by_ts_kind_verified
CREATE INDEX events_by_ts_kind_verified
ON events (ts DESC, kind, trust_verified);
```
Deployment options:
- **Ops script:** `psql -d stellaops_db -f ops/postgres/indices/events_provenance_indices.sql`
- **C# helper:** `PostgresIndexes.EnsureEventIndexesAsync(connection, ct)`
This section was updated as part of `PROV-INDEX-401-030` (completed 2025-11-27).
---
## 5. Query recipes
* **All proven VEX for an image digest:**
```sql
SELECT * FROM events
WHERE kind = 'VEX'
AND subject_digest_sha256 = '<digest>'
AND provenance_dsse_rekor_log_index IS NOT NULL
AND trust_verified = true;
```
* **Compliance gap (unverified data used for decisions):**
```sql
SELECT kind, COUNT(*) as count
FROM events
WHERE kind IN ('VEX', 'SBOM', 'SCAN')
AND (trust_verified IS NOT TRUE
OR provenance_dsse_rekor_log_index IS NULL)
GROUP BY kind;
```
* **Replay slice:** filter for events where `provenance.dsse.chain` covers build → sbom → scan and export referenced attestation digests.
---
## 6. Policy gates
Examples:
```yaml
rules:
- id: GATE-PROVEN-VEX
when:
all:
- kind: "VEX"
- trust.verified: true
- key.keyId in VendorAllowlist
- rekor.integratedTime <= releaseFreeze
then:
decision: ALLOW
- id: BLOCK-UNPROVEN
when:
any:
- trust.verified != true
- provenance.dsse.rekor.logIndex missing
then:
decision: FAIL
reason: "Unproven evidence influences decision; require Rekor-backed attestation."
```
---
## 7. UI nudges
* **Provenance chip** on findings/events: `Verified • Rekor#1234567 • KeyID:cosign:...` (click → inclusion proof & DSSE preview).
* Facet filter: `Provenance = Verified / Missing / Key-Policy-Mismatch`.
---
## 8. Implementation tasks
| Task ID | Scope |
|---------|-------|
| `PROV-INLINE-401-028` | Extend Authority/Feedser write-paths to attach `provenance.dsse` + `trust` blocks using `StellaOps.Provenance.Postgres`. |
| `PROV-BACKFILL-401-029` | Backfill historical events with DSSE/Rekor refs based on existing attestation digests. |
| `PROV-INDEX-401-030` | Create PostgreSQL indexes and expose helper queries for audits. |
Keep this document updated when new attestation types or mirror/witness policies land.
---
## 9. Feedser API for provenance updates
Feedser exposes a lightweight endpoint for attaching provenance after an event is recorded:
```
POST /events/statements/{statementId}/provenance
Headers: X-Stella-Tenant, Authorization (if Authority is enabled)
Body: { "dsse": { ... }, "trust": { ... } }
```
The body matches the JSON emitted by `publish_attestation_with_provenance.sh`. Feedser validates the payload, ensures `trust.verified = true`, and then calls `AttachStatementProvenanceAsync` so the DSSE metadata lands inline on the target statement. Clients receive HTTP 202 on success, 400 on malformed input, and 404 if the statement id is unknown.
---
## 10. Backfill service
`EventProvenanceBackfillService` (`src/StellaOps.Events.Postgres/EventProvenanceBackfillService.cs`) orchestrates backfilling historical events with DSSE provenance metadata.
### 10.1 Components
| Class | Purpose |
|-------|---------|
| `IAttestationResolver` | Interface for resolving attestation metadata by subject digest. |
| `EventProvenanceBackfillService` | Queries unproven events, resolves attestations, updates events. |
| `StubAttestationResolver` | Test/development stub implementation. |
### 10.2 Usage
```csharp
var resolver = new MyAttestationResolver(rekorClient, attestationRepo);
var backfillService = new EventProvenanceBackfillService(postgresConnection, resolver);
// Count unproven events
var count = await backfillService.CountUnprovenEventsAsync(
new[] { "SBOM", "VEX", "SCAN" });
// Backfill with progress reporting
var progress = new Progress<BackfillResult>(r =>
Console.WriteLine($"{r.EventId}: {r.Status}"));
var summary = await backfillService.BackfillAllAsync(
kinds: new[] { "SBOM", "VEX", "SCAN" },
limit: 1000,
progress: progress);
Console.WriteLine($"Processed: {summary.TotalProcessed}");
Console.WriteLine($"Success: {summary.SuccessCount}");
Console.WriteLine($"Not found: {summary.NotFoundCount}");
Console.WriteLine($"Errors: {summary.ErrorCount}");
```
### 10.3 Implementing IAttestationResolver
Implementations should query the attestation store (Rekor, CAS, or local PostgreSQL) by subject digest:
```csharp
public class RekorAttestationResolver : IAttestationResolver
{
private readonly IRekorClient _rekor;
private readonly IAttestationRepository _attestations;
public async Task<AttestationResolution?> ResolveAsync(
string subjectDigestSha256,
string eventKind,
CancellationToken cancellationToken)
{
// Look up attestation by subject digest
var record = await _attestations.GetAsync(subjectDigestSha256, eventKind, cancellationToken);
if (record is null) return null;
// Fetch Rekor proof if available
var proof = await _rekor.GetProofAsync(record.RekorUuid, RekorBackend.Sigstore, cancellationToken);
return new AttestationResolution
{
Dsse = new DsseProvenance { /* ... */ },
Trust = new TrustInfo { Verified = true, Verifier = "Authority@stella" },
AttestationId = record.Id
};
}
}
```
### 10.4 Reference files
- `src/StellaOps.Events.Postgres/IAttestationResolver.cs`
- `src/StellaOps.Events.Postgres/EventProvenanceBackfillService.cs`
- `src/StellaOps.Events.Postgres/StubAttestationResolver.cs`
This section was added as part of `PROV-BACKFILL-401-029` (completed 2025-11-27).

View File

@@ -1,16 +0,0 @@
# Provenance Backfill Plan (Sprint 401)
Artifacts available
- Attestation inventory: `docs/modules/provenance/guides/attestation-inventory-2025-11-18.ndjson`
- Subject→Rekor map: `docs/modules/provenance/guides/subject-rekor-map-2025-11-18.json`
Procedure (deterministic)
1) Load inventory NDJSON; validate UUID/ULID and digest formats.
2) For each record, resolve Rekor entry via the subject→Rekor map; if missing, record gap and skip write.
3) Emit backfilled events to the provenance store using `scripts/publish_attestation_with_provenance.sh --mode backfill` (add `--subject` and `--rekor` arguments) with sorted input to guarantee stable ordering.
4) Log every backfilled subject + Rekor digest pair to `logs/provenance-backfill-2025-11-18.ndjson` (UTC timestamps, ISO-8601).
5) Rerun until gaps are zero; then mark PROV-BACKFILL-401-029 DONE.
Determinism
- Sort by subject, then rekorEntry before processing.
- Use canonical JSON writer for outputs; timestamps in UTC `O` format.

View File

@@ -1,76 +0,0 @@
# Provenance & Attestation Reference
> **Imposed rule:** All exported evidence must ship with DSSE + transparency proof bundles; unsigned or proof-less artifacts are rejected at ingress and may not be stored in the Evidence Locker.
This guide explains how StellaOps generates, signs, verifies, and distributes DSSE attestations for SBOMs, policy evaluations, and runtime evidence.
## 1. Attestation Workflow (online and offline)
1. **Producer** (Scanner, Policy Engine, runtime probes) emits a payload and a request to sign.
2. **Signer** authenticates the caller, validates supply-chain policy (release integrity, image pinning), then signs using keyless or tenant KMS keys.
3. **Attestor** wraps the payload in DSSE, records it in Rekor v2 (when online), persists the bundle plus inclusion proof, and exposes a verification package API.
4. **Export Center** and **Evidence Locker** embed the bundle and proof into export artifacts for offline replay; CLI retrieves the same package via `stella attest fetch`.
5. **Verifiers** (CLI, Policy Engine, auditors) validate signature roots, Rekor proof, and optional transparency witness endorsements.
## 2. DSSE Payload Types & Schemas
Supported payload types (all versioned and protobuf/JSON dual-encoded):
- `StellaOps.BuildProvenance@1`
- `StellaOps.SBOMAttestation@1`
- `StellaOps.ScanResults@1`
- `StellaOps.PolicyEvaluation@1`
- `StellaOps.VEXAttestation@1`
- `StellaOps.RiskProfileEvidence@1`
- `StellaOps.PromotionAttestation@1` (predicate `stella.ops/promotion@v1`, see `docs/release/promotion-attestations.md`)
Schema sources: `src/Attestor/StellaOps.Attestor.Types` and module dossiers. All payloads include:
- `subject` (digest + PURL/NEVRA coordinates)
- `timestamp` (UTC, ISO-8601)
- `producer` (service + version)
- `critical` block (policy version, scanner defs, reachability context)
- `materials` (SBOM/VEX references) and optional `auxiliary_proofs`
## 3. Signing & storage controls
- **Key policy:** Short-lived OIDC keyless by default; tenant KMS allowed; Ed25519 and ECDSA P-256 supported.
- **Inclusion:** Rekor v2 UUID + log index cached; when offline, the Attestor stamps a `transparency_pending` marker to be replayed later.
- **WORM:** Evidence Locker keeps immutable copies; retention and legal hold are enforced per tenant and surfaced in `docs/modules/evidence-locker/guides/evidence-locker.md`.
- **Redaction:** Sensitive fields (secrets, PII) must be excluded at payload creation; the signer refuses payloads marked `pii=true` without a redaction ticket.
## 4. Verification workflow
Command-line (online or offline bundle):
```sh
stella attest verify \
--bundle path/to/bundle.dsse.json \
--rekor-root pubkeys/rekor.pub \
--fulcio-root pubkeys/fulcio.pub \
--certificate-chain pubkeys/issuer-chain.pem
```
Verification steps performed by services and CLI:
- Validate DSSE signature against Fulcio/tenant roots and certificate policies.
- Confirm subject digest matches expected container/image/SBOM digest.
- Check Rekor inclusion proof and (if present) transparency witness signatures.
- Enforce freshness: reject bundles older than `attestation.max_age_days` (tenant policy).
- Record verification result into Timeline events for auditability.
## 5. Offline / air-gap posture
- Export Center emits self-contained bundles (`*.dsse.json`, `rekor-proof.json`, `cert-chain.pem`) plus a verification manifest for deterministic replay.
- CLI `stella attest verify --bundle bundle.dsse.json --offline` skips Rekor lookups and relies on embedded proofs.
- When connectivity returns, the Attestor replays pending `transparency_pending` entries and updates Evidence Locker indexes; Timeline events capture the replay.
## 6. References
- `docs/modules/signer/architecture.md`
- `docs/modules/attestor/architecture.md`
- `docs/modules/export-center/architecture.md`
- `docs/modules/policy/architecture.md`
- `docs/modules/telemetry/architecture.md`
- `docs/modules/evidence-locker/guides/evidence-locker.md`
- `src/Provenance/StellaOps.Provenance.Attestation`

View File

@@ -53,4 +53,4 @@ Required fields:
- `src/Policy/StellaOps.Policy.Gateway/Endpoints/ExceptionApprovalEndpoints.cs`
- `src/Policy/StellaOps.Policy.Gateway/Services/ApprovalWorkflowService.cs`
- `docs/product/decision-capsules.md`
- `docs/modules/release-orchestrator/workflow/promotion.md`
- `docs/modules/release-jobengine/workflow/promotion.md`

View File

@@ -39,7 +39,7 @@ HTTP controllers are not yet present in these API hosts.
## Acceptance Criteria
- Endpoint group implementation is tracked by API group with owning project path.
- Promotion state transitions match `docs/modules/release-orchestrator/workflow/promotion.md`.
- Promotion state transitions match `docs/modules/release-jobengine/workflow/promotion.md`.
- Decision records include policy digest and evidence references.
- Fail-closed behavior is enforced when gate providers error.
- Replay-oriented deterministic assertions are present in tests.
@@ -59,4 +59,4 @@ Minimum acceptance test mapping:
- Policy ownership: `docs/modules/policy/promotion-gate-ownership-contract.md`
- Evidence contract: `docs/modules/evidence-locker/promotion-evidence-contract.md`
- Optional capsule profile: `docs/modules/release-orchestrator/appendices/promotion-capsule-optional.md`
- Optional capsule profile: `docs/modules/release-jobengine/appendices/promotion-capsule-optional.md`

View File

@@ -1,7 +1,7 @@
# Evidence-Based Release Gates Contract
**Status:** Implemented baseline in promotion runtime (2026-02-10)
**Related:** `docs/modules/release-orchestrator/workflow/promotion.md`, `docs/modules/attestor/repro-bundle-profile.md`, `docs/modules/evidence-locker/architecture.md`
**Related:** `docs/modules/release-jobengine/workflow/promotion.md`, `docs/modules/attestor/repro-bundle-profile.md`, `docs/modules/evidence-locker/architecture.md`
## Purpose

View File

@@ -1,6 +1,6 @@
# Remediation Module Architecture
# Remediation Module Architecture
> **Status: Planned.** The Remediation marketplace is a planned feature for developer-facing fix templates, PR generation, and contributor trust scoring. Source code at `src/Remediation/` contains initial scaffolding. This architecture document is a design specification pending full implementation.
> **Status: Partially implemented.** Core remediation APIs exist, and marketplace source endpoints now run with persistence-backed list/get/upsert behavior. Remaining areas are still incremental (template lifecycle depth, contributor workflow hardening, and broader policy integration).
## Overview
@@ -18,47 +18,48 @@ Tracks the lifecycle of a remediation pull request from submission through scann
Community members or vendors who submit fix templates. Each contributor has a trust score computed from their verification history (verified fixes, rejections).
### Marketplace Sources
Curated collections of fix templates from community, partner, or vendor origins. Sources are rated independently and can be enabled/disabled per tenant.
Curated collections of fix templates from community, partner, or vendor origins. Sources are rated independently and can be enabled or disabled per tenant.
## Domain Model
```
```text
FixTemplate (remediation.fix_templates)
├── CveId (text, indexed)
├── Purl (text, indexed pkg:type/name)
├── VersionRange (semver range)
├── PatchContent (unified diff)
├── Status (pending/verified/rejected)
├── TrustScore (0.01.0)
├── DsseDigest (nullable signed envelope hash)
└── ContributorId / SourceId (foreign keys)
|- CveId (text, indexed)
|- Purl (text, indexed - pkg:type/name)
|- VersionRange (semver range)
|- PatchContent (unified diff)
|- Status (pending/verified/rejected)
|- TrustScore (0.0-1.0)
|- DsseDigest (nullable - signed envelope hash)
`- ContributorId / SourceId (foreign keys)
PrSubmission (remediation.pr_submissions)
├── FixTemplateId (nullable FK)
├── PrUrl, RepositoryUrl, SourceBranch, TargetBranch
├── CveId (text, indexed)
├── Status (opened/scanning/merged/verified/failed/inconclusive)
├── PreScanDigest, PostScanDigest
├── ReachabilityDeltaDigest, FixChainDsseDigest
├── Verdict (fixed/partial/not_fixed/inconclusive)
└── ContributorId
|- FixTemplateId (nullable FK)
|- PrUrl, RepositoryUrl, SourceBranch, TargetBranch
|- CveId (text, indexed)
|- Status (opened/scanning/merged/verified/failed/inconclusive)
|- PreScanDigest, PostScanDigest
|- ReachabilityDeltaDigest, FixChainDsseDigest
|- Verdict (fixed/partial/not_fixed/inconclusive)
`- ContributorId
Contributor (remediation.contributors)
├── Username (unique)
├── VerifiedFixes, TotalSubmissions, RejectedSubmissions
└── TrustScore (computed)
|- Username (unique)
|- VerifiedFixes, TotalSubmissions, RejectedSubmissions
`- TrustScore (computed)
MarketplaceSource (remediation.marketplace_sources)
├── Key (unique)
├── SourceType (community/partner/vendor)
├── Enabled, TrustScore
└── LastSyncAt
|- Key (tenant-scoped unique key)
|- SourceType (community/partner/vendor)
|- Enabled, TrustScore
`- LastSyncAt
```
## Trust Scoring
Contributor trust score formula:
```
```text
score = clamp((verified * 1.0 - rejected * 0.5) / max(total, 1), 0, 1)
```
@@ -70,30 +71,45 @@ Trust tiers:
## API Surface
All endpoints under `/api/v1/remediation/`.
All endpoints are under `/api/v1/remediation/`.
### Templates
- `GET /templates` List fix templates (filter by CVE, PURL)
- `GET /templates/{id}` Get template detail
- `POST /templates` Create template (requires `remediation.submit`)
- `GET /templates` - List fix templates (filter by CVE, PURL)
- `GET /templates/{id}` - Get template detail
- `POST /templates` - Create template (requires `remediation.submit`)
### Submissions
- `GET /submissions` List PR submissions
- `GET /submissions/{id}` Get submission with attestation chain
- `POST /submissions` Submit PR for verification
- `GET /submissions/{id}/status` Pipeline status
- `GET /submissions` - List PR submissions
- `GET /submissions/{id}` - Get submission with attestation chain
- `POST /submissions` - Submit PR for verification
- `GET /submissions/{id}/status` - Pipeline status
### Matching
- `GET /match?cve=...&purl=...&version=...` Find applicable fix templates
- `GET /match?cve=...&purl=...&version=...` - Find applicable fix templates
### Contributors
- `GET /contributors` List contributors
- `GET /contributors/{username}` Profile with trust score
- `GET /contributors` - List contributors
- `GET /contributors/{username}` - Profile with trust score
### Sources
- `GET /sources` List marketplace sources
- `GET /sources/{key}` Source detail
- `POST /sources` Create/update source (requires `remediation.manage`)
- `GET /sources` - List marketplace sources
- `GET /sources/{key}` - Source detail
- `POST /sources` - Create/update source (requires `remediation.manage`)
Implemented source API contract (2026-03-04):
- Request model for upsert (`POST /sources`): `key`, `name`, `url`, `sourceType`, `enabled`, `trustScore`, `lastSyncAt`.
- Deterministic behavior:
- key normalization uses lowercase invariant
- list ordering is key-based ordinal ordering
- upsert is idempotent by tenant + source key
- Validation:
- key pattern: `^[a-z0-9][a-z0-9._-]{0,63}$`
- sourceType allowed values: `community`, `partner`, `vendor`
- trustScore range: `0..1`
- url must be an absolute `http`/`https` URL when provided
- Tenant isolation:
- all source endpoints require tenant context (`RequireTenant`)
- repository operations are tenant-scoped for list/get/upsert behavior
## Authorization Policies
@@ -103,13 +119,41 @@ All endpoints under `/api/v1/remediation/`.
| `remediation.submit` | Create templates and submit PRs |
| `remediation.manage` | Manage marketplace sources, verify/reject templates |
## Runtime Storage Contract (2026-03-05)
Remediation runtime storage is now selected through `Remediation:Storage:Driver` (or `Storage:Driver`) with explicit startup validation:
- `postgres` (default):
- Required settings: `ConnectionStrings:Default` or `Remediation:Storage:Postgres:ConnectionString`.
- Optional schema override: `Remediation:Storage:Postgres:SchemaName` (defaults to `remediation`).
- Behavior: repositories are wired with `RemediationDataSource` and Postgres-backed constructors.
- Startup: fails fast when required connection configuration is missing.
- `inmemory`:
- Allowed only in `Test`/`Testing` environment profiles.
- Intended for deterministic automated tests only.
- Startup: fails fast outside test profiles.
This removes implicit production-like in-memory behavior and makes storage mode explicit and auditable.
Migration notes:
- Legacy webservice wiring that instantiated parameterless repository constructors has been removed.
- Existing deployments must provide a Postgres connection string (or explicitly run with `inmemory` in `Test`/`Testing` profiles).
- Integration tests should pin `REMEDIATION__STORAGE__DRIVER=inmemory` under a testing environment profile for deterministic non-network execution.
## Service Integration Baseline (2026-03-05)
- Router integration enabled (`serviceName: remediation`) with endpoint refresh on startup.
- Local alias binding/logging enabled via `remediation.stella-ops.local`.
- CORS and tenant middleware are part of the default request pipeline before endpoint execution.
## Verification Pipeline
1. PR submitted (status: `opened`)
2. Pre-merge scan captures baseline SBOM digest
3. PR merged (status: `merged`)
4. Post-merge scan captures updated SBOM digest
5. Reachability delta computed between pre/post digests
5. Reachability delta computed between pre and post digests
6. Fix-chain DSSE envelope signed
7. Verdict determined: `fixed`, `partial`, `not_fixed`, or `inconclusive`
@@ -121,12 +165,13 @@ The `RemediationPrWebhookHandler` in the Signals module detects remediation PRs
## Module Location
```
```text
src/Remediation/
├── StellaOps.Remediation.Core/ Domain models, interfaces, services
├── StellaOps.Remediation.WebService/ API endpoints, Program.cs
├── StellaOps.Remediation.Persistence/ SQL migrations, repositories
└── __Tests/StellaOps.Remediation.Tests/ — Unit tests
|- StellaOps.Remediation.Core/ - Domain models, interfaces, services
|- StellaOps.Remediation.WebService/ - API endpoints, Program.cs
|- StellaOps.Remediation.Persistence/ - SQL migrations, repositories
|- __Tests/StellaOps.Remediation.Tests/ - Repository/domain unit tests
`- __Tests/StellaOps.Remediation.WebService.Tests/ - Source endpoint integration tests
```
## Related Sprints
@@ -141,3 +186,23 @@ src/Remediation/
## Related Contracts
- `docs/contracts/remediation-pr-v1.md`
## Advisory Gap Status (2026-03-04 Batch)
Status:
- Advisory gap `REM-001` is closed for marketplace sources.
Closed behaviors:
- `GET /api/v1/remediation/sources` returns persisted tenant-scoped sources with deterministic ordering.
- `GET /api/v1/remediation/sources/{key}` resolves persisted records (no unconditional stub `source_not_found` path).
- `POST /api/v1/remediation/sources` performs validated upsert and no longer returns `501`.
- Marketplace source repository abstraction and implementation are wired through DI:
- `IMarketplaceSourceRepository`
- `PostgresMarketplaceSourceRepository`
Verification evidence:
- `dotnet test src/Remediation/__Tests/StellaOps.Remediation.Tests/StellaOps.Remediation.Tests.csproj -m:1 -v minimal` - `28` passed.
- `dotnet test src/Remediation/__Tests/StellaOps.Remediation.WebService.Tests/StellaOps.Remediation.WebService.Tests.csproj -m:1 -v minimal` - `4` passed.
Tracking sprint:
- `docs/implplan/SPRINT_20260304_308_Remediation_marketplace_sources_api_completion.md`

View File

@@ -255,6 +255,20 @@ All inputs that affect verdict output are captured:
* **Hash stability**: Canonical JSON hashing is stable across serialization
* **Integration tests**: Full token lifecycle with Policy Engine
## 11) Storage contract (Sprint 312)
Replay now follows the platform storage split used by Scanner:
* `Storage:Driver=postgres` (default) for snapshot index/state (`replay.feed_snapshot_index`).
* `Storage:ObjectStore:Driver=seed-fs` for snapshot blob payloads (`SeedFsFeedSnapshotBlobStore`).
* `inmemory` remains available only for explicit non-production/testing profiles.
* `Storage:ObjectStore:Driver=rustfs` is explicitly rejected at startup; current runtime contract supports `seed-fs` only for blob storage.
Verification evidence:
* `PostgresFeedSnapshotIndexStoreTests` validates index insert/find/list behavior.
* `SeedFsFeedSnapshotBlobStoreTests` validates blob store roundtrip/exists/delete behavior.
---
## Related Documentation

View File

@@ -1,60 +0,0 @@
# Risk Engine
> Risk scoring runtime with pluggable providers and explainability.
## Purpose
RiskEngine computes deterministic, explainable risk scores for vulnerabilities by aggregating signals from multiple data sources (EPSS, CVSS, KEV, VEX, reachability). It produces audit trails and explainability payloads for every scoring decision.
## Quick Links
- [Architecture](./architecture.md) - Technical design and implementation details
- [Guides](./guides/) - Scoring configuration guides
- [Samples](./samples/) - Risk profile examples
## Status
| Attribute | Value |
|-----------|-------|
| **Maturity** | Production |
| **Last Reviewed** | 2025-12-29 |
| **Maintainer** | Policy Guild |
## Key Features
- **Pluggable Providers**: EPSS, CVSS+KEV, VEX status, fix availability providers
- **Deterministic Scoring**: Same inputs produce identical scores
- **Explainability**: Audit trails for every scoring decision
- **Offline Support**: Air-gapped operation via factor bundles
## Dependencies
### Upstream (this module depends on)
- **Concelier** - CVSS, KEV data
- **Excititor** - VEX status data
- **Signals** - Reachability data
- **Authority** - Authentication
### Downstream (modules that depend on this)
- **Policy Engine** - Consumes risk scores for policy evaluation
## Configuration
```yaml
risk_engine:
providers:
- epss
- cvss_kev
- vex_gate
- fix_exposure
cache_ttl_minutes: 60
```
## Notes
RiskEngine does not make PASS/FAIL decisions. It provides scores to the Policy Engine which makes enforcement decisions.
## Related Documentation
- [Policy Architecture](../policy/architecture.md)
- [Risk Scoring Contract](../../contracts/risk-scoring.md)

View File

@@ -1,376 +0,0 @@
# component_architecture_riskengine.md - **Stella Ops RiskEngine** (2025Q4)
> Risk scoring runtime with pluggable providers and explainability.
> **Scope.** Implementation-ready architecture for **RiskEngine**: the scoring runtime that computes Risk Scoring Profiles across deployments while preserving provenance and explainability. Covers scoring workers, providers, caching, and integration with Policy Engine.
---
## 0) Mission & boundaries
**Mission.** Compute **deterministic, explainable risk scores** for vulnerabilities by aggregating signals from multiple data sources (EPSS, CVSS, KEV, VEX, reachability). Produce audit trails and explainability payloads for every scoring decision.
**Boundaries.**
* RiskEngine **does not** make PASS/FAIL decisions. It provides scores to the Policy Engine.
* RiskEngine **does not** own vulnerability data. It consumes from Concelier, Excititor, and Signals.
* Scoring is **deterministic**: same inputs produce identical scores.
* Supports **offline/air-gapped** operation via factor bundles.
---
## 1) Solution & project layout
```
src/RiskEngine/StellaOps.RiskEngine/
├─ StellaOps.RiskEngine.Core/ # Scoring orchestrators, provider contracts
│ ├─ Providers/
│ │ ├─ IRiskScoreProvider.cs # Provider interface
│ │ ├─ EpssProvider.cs # EPSS score provider
│ │ ├─ CvssKevProvider.cs # CVSS + KEV provider
│ │ ├─ VexGateProvider.cs # VEX status provider
│ │ ├─ FixExposureProvider.cs # Fix availability provider
│ │ └─ DefaultTransformsProvider.cs # Score transformations
│ ├─ Contracts/
│ │ ├─ ScoreRequest.cs # Scoring request DTO
│ │ └─ RiskScoreResult.cs # Scoring result with explanation
│ └─ Services/
│ ├─ RiskScoreWorker.cs # Scoring job executor
│ └─ RiskScoreQueue.cs # Job queue management
├─ StellaOps.RiskEngine.Infrastructure/ # Persistence, caching, connectors
│ └─ Stores/
│ └─ InMemoryRiskScoreResultStore.cs
├─ StellaOps.RiskEngine.WebService/ # REST API for jobs and results
│ └─ Program.cs
├─ StellaOps.RiskEngine.Worker/ # Background scoring workers
│ ├─ Program.cs
│ └─ Worker.cs
└─ StellaOps.RiskEngine.Tests/ # Unit and integration tests
```
---
## 2) External dependencies
* **PostgreSQL** - Score persistence and job state
* **Concelier** - Vulnerability advisory data, EPSS scores
* **Excititor** - VEX statements
* **Signals** - Reachability and runtime signals
* **Policy Engine** - Consumes risk scores for decision-making
* **Authority** - Authentication and authorization
* **Valkey/Redis** - Score caching (optional)
---
## 3) Contracts & data model
### 3.1 ScoreRequest
```csharp
public sealed record ScoreRequest
{
public required string VulnerabilityId { get; init; } // CVE or vuln ID
public required string ArtifactId { get; init; } // PURL or component ID
public string? TenantId { get; init; }
public string? ContextId { get; init; } // Scan or assessment ID
public IReadOnlyList<string>? EnabledProviders { get; init; }
}
```
### 3.2 RiskScoreResult
```csharp
public sealed record RiskScoreResult
{
public required string RequestId { get; init; }
public required decimal FinalScore { get; init; } // 0.0-10.0
public required string Tier { get; init; } // Critical/High/Medium/Low/Info
public required DateTimeOffset ComputedAt { get; init; }
public required IReadOnlyList<ProviderContribution> Contributions { get; init; }
public required ExplainabilityPayload Explanation { get; init; }
}
public sealed record ProviderContribution
{
public required string ProviderId { get; init; }
public required decimal RawScore { get; init; }
public required decimal Weight { get; init; }
public required decimal WeightedScore { get; init; }
public string? FactorSource { get; init; } // Where data came from
public DateTimeOffset? FactorTimestamp { get; init; } // When factor was computed
}
```
### 3.3 Provider Interface
```csharp
public interface IRiskScoreProvider
{
string ProviderId { get; }
decimal DefaultWeight { get; }
TimeSpan CacheTtl { get; }
Task<ProviderResult> ComputeAsync(
ScoreRequest request,
CancellationToken ct);
Task<bool> IsHealthyAsync(CancellationToken ct);
}
```
---
## 4) Score Providers
### 4.1 Built-in Providers
| Provider | Data Source | Weight | Description |
|----------|-------------|--------|-------------|
| `epss` | Concelier/EPSS | 0.25 | EPSS probability score (0-1 → 0-10) |
| `cvss-kev` | Concelier | 0.30 | CVSS base + KEV boost |
| `vex-gate` | Excititor | 0.20 | VEX status (affected/not_affected) |
| `fix-exposure` | Concelier | 0.15 | Fix availability window |
| `reachability` | Signals | 0.10 | Code path reachability |
### 4.2 Score Computation
```
FinalScore = Σ(provider.weight × provider.score) / Σ(provider.weight)
Tier mapping:
9.0-10.0 → Critical
7.0-8.9 → High
4.0-6.9 → Medium
1.0-3.9 → Low
0.0-0.9 → Info
```
### 4.3 Provider Data Sources
```csharp
public interface IEpssSources
{
Task<EpssScore?> GetScoreAsync(string cveId, CancellationToken ct);
}
public interface ICvssKevSources
{
Task<CvssData?> GetCvssAsync(string cveId, CancellationToken ct);
Task<bool> IsKevAsync(string cveId, CancellationToken ct);
}
```
---
## 4.4 Exploit Maturity Service
The **ExploitMaturityService** consolidates multiple exploitation signals into a unified maturity level for risk prioritization.
### Maturity Taxonomy
| Level | Description | Evidence |
|-------|-------------|----------|
| `Unknown` | No exploitation intelligence available | No signals or below thresholds |
| `Theoretical` | Exploit theoretically possible | Low EPSS (<10%) |
| `ProofOfConcept` | PoC exploit exists | Moderate EPSS (10-40%) |
| `Active` | Active exploitation observed | High EPSS (40-80%), in-the-wild reports |
| `Weaponized` | Weaponized exploit in campaigns | Very high EPSS (>80%), KEV listing |
### Signal Sources
```csharp
public interface IExploitMaturityService
{
Task<ExploitMaturityResult> AssessMaturityAsync(string cveId, CancellationToken ct);
Task<ExploitMaturityLevel?> GetMaturityLevelAsync(string cveId, CancellationToken ct);
Task<IReadOnlyList<MaturityHistoryEntry>> GetMaturityHistoryAsync(string cveId, CancellationToken ct);
}
```
**Signal aggregation:**
1. **EPSS** - Maps probability score to maturity level via thresholds
2. **KEV** - CISA Known Exploited Vulnerabilities → `Weaponized`
3. **InTheWild** - Threat intel feeds → `Active`
### EPSS Threshold Mapping
| EPSS Score | Maturity Level |
|------------|----------------|
| ≥ 0.80 | Weaponized |
| ≥ 0.40 | Active |
| ≥ 0.10 | ProofOfConcept |
| ≥ 0.01 | Theoretical |
| < 0.01 | Unknown |
### Exploit Maturity API Endpoints
```
GET /exploit-maturity/{cveId} → ExploitMaturityResult
GET /exploit-maturity/{cveId}/level → { level: "Active" }
GET /exploit-maturity/{cveId}/history → { entries: [...] }
POST /exploit-maturity/batch { cveIds: [...] } → { results: [...] }
```
---
## 5) REST API (RiskEngine.WebService)
All under `/api/v1/risk`. Auth: **OpTok**.
```
POST /scores { request: ScoreRequest } → { jobId }
GET /scores/{jobId} → { result: RiskScoreResult, status }
GET /scores/{jobId}/explain → { explanation: ExplainabilityPayload }
POST /batch { requests: ScoreRequest[] } → { batchId }
GET /batch/{batchId} → { results: RiskScoreResult[], status }
GET /providers → { providers: ProviderInfo[] }
GET /providers/{id}/health → { healthy: bool, lastCheck }
GET /healthz | /readyz | /metrics
```
---
## 6) Configuration (YAML)
```yaml
RiskEngine:
Postgres:
ConnectionString: "Host=postgres;Database=risk;..."
Cache:
Enabled: true
Provider: "valkey"
ConnectionString: "redis://valkey:6379" # Valkey (Redis-compatible)
DefaultTtl: "00:15:00"
Providers:
Epss:
Enabled: true
Weight: 0.25
CacheTtl: "01:00:00"
Source: "concelier"
CvssKev:
Enabled: true
Weight: 0.30
KevBoost: 2.0
VexGate:
Enabled: true
Weight: 0.20
NotAffectedScore: 0.0
AffectedScore: 10.0
FixExposure:
Enabled: true
Weight: 0.15
NoFixPenalty: 1.5
Reachability:
Enabled: true
Weight: 0.10
UnreachableDiscount: 0.5
Worker:
Concurrency: 4
BatchSize: 100
PollInterval: "00:00:05"
Offline:
FactorBundlePath: "/data/risk-factors"
AllowStaleData: true
MaxStalenessHours: 168
```
---
## 7) Security & compliance
* **AuthN/Z**: Authority-issued OpToks with `risk.score` scope
* **Tenant isolation**: Scores scoped by tenant ID
* **Audit trail**: All scoring decisions logged with inputs and factors
* **No PII**: Only vulnerability and artifact identifiers processed
---
## 8) Performance targets
* **Single score**: < 100ms P95 (cached factors)
* **Batch scoring**: < 500ms P95 for 100 items
* **Provider health check**: < 1s timeout
* **Cache hit rate**: > 80% for repeated CVEs
---
## 9) Observability
**Metrics:**
* `risk.scores.computed_total{tier,provider}`
* `risk.scores.duration_seconds`
* `risk.providers.health{provider,status}`
* `risk.cache.hits_total` / `risk.cache.misses_total`
* `risk.batch.size_histogram`
**Tracing:** Spans for each provider contribution, cache operations, and aggregation.
**Logs:** Structured logs with `cve_id`, `artifact_id`, `tenant`, `final_score`.
---
## 10) Testing matrix
* **Provider tests**: Each provider returns expected scores for fixture data
* **Aggregation tests**: Weighted combination produces correct final score
* **Determinism tests**: Same inputs produce identical scores
* **Cache tests**: Cache hit/miss behavior correct
* **Offline tests**: Factor bundles load and score correctly
* **Integration tests**: Full scoring pipeline with mocked data sources
---
## 11) Offline/Air-Gap Support
### Factor Bundles
Pre-computed factor data for offline operation:
```
/data/risk-factors/
├─ epss/
│ └─ epss-2025-01-15.json.gz
├─ cvss/
│ └─ cvss-2025-01-15.json.gz
├─ kev/
│ └─ kev-2025-01-15.json
└─ manifest.json
```
### Staleness Handling
When operating offline, scores include staleness indicators:
```json
{
"finalScore": 7.2,
"dataFreshness": {
"epss": { "age": "48h", "stale": false },
"kev": { "age": "24h", "stale": false }
}
}
```
---
## Related Documentation
* Policy scoring: `../policy/architecture.md`
* Concelier feeds: `../concelier/architecture.md`
* Excititor VEX: `../excititor/architecture.md`
* Signals reachability: `../signals/architecture.md`

View File

@@ -1,296 +0,0 @@
# Risk Engine FixChain Integration
> **Sprint:** SPRINT_20260110_012_007_RISK
> **Last Updated:** 10-Jan-2026
## Overview
The Risk Engine FixChain integration enables automatic risk score adjustment based on verified fix status from FixChain attestations. When a vulnerability has a verified fix, the risk score is reduced proportionally to the verification confidence level.
## Why This Matters
| Current State | With FixChain Integration |
|---------------|---------------------------|
| Risk scores ignore fix verification | Fix confidence reduces risk |
| Binary matches = always vulnerable | Verified fixes lower severity |
| No credit for patched backports | Backport fixes recognized |
| Manual risk exceptions needed | Automatic risk adjustment |
## Risk Adjustment Model
### Verdict to Risk Modifier Mapping
| Verdict | Confidence | Risk Modifier | Rationale |
|---------|------------|---------------|-----------|
| `fixed` | >= 95% | -80% to -90% | High-confidence verified fix |
| `fixed` | 85-95% | -60% to -80% | Verified fix, some uncertainty |
| `fixed` | 70-85% | -40% to -60% | Likely fixed, needs confirmation |
| `fixed` | 60-70% | -20% to -40% | Possible fix, low confidence |
| `fixed` | < 60% | 0% | Below threshold, no adjustment |
| `partial` | >= 60% | -25% to -50% | Partial fix applied |
| `inconclusive` | any | 0% | Cannot determine, conservative |
| `still_vulnerable` | any | 0% | No fix detected |
| No attestation | N/A | 0% | No verification performed |
### Modifier Formula
```
AdjustedRisk = BaseRisk * (1 - (Modifier * ConfidenceWeight))
Where:
Modifier = verdict-based modifier from table above
ConfidenceWeight = min(1.0, (Confidence - MinThreshold) / (1.0 - MinThreshold))
```
### Example Calculation
```
CVE-2024-0727 on pkg:deb/debian/openssl@3.0.11-1~deb12u2:
BaseRisk = 8.5 (HIGH)
FixChain Verdict = "fixed"
FixChain Confidence = 0.97
Modifier = 0.90 (high confidence tier)
ConfidenceWeight = (0.97 - 0.60) / (1.0 - 0.60) = 0.925
AdjustedRisk = 8.5 * (1 - 0.90 * 0.925) = 8.5 * 0.1675 = 1.42 (LOW)
```
## Components
### IFixChainRiskProvider
Main interface for FixChain risk integration:
```csharp
public interface IFixChainRiskProvider
{
Task<FixVerificationStatus?> GetFixStatusAsync(
string cveId,
string binarySha256,
string? componentPurl = null,
CancellationToken ct = default);
double ComputeRiskAdjustment(FixVerificationStatus status);
FixChainRiskFactor CreateRiskFactor(FixVerificationStatus status);
}
```
### FixChainRiskProvider
Implementation that:
1. Queries the attestation store for FixChain predicates
2. Computes risk adjustment based on verdict and confidence
3. Creates structured risk factors for UI display
### IFixChainAttestationClient
Client for querying attestations:
```csharp
public interface IFixChainAttestationClient
{
Task<FixChainAttestationData?> GetFixChainAsync(
string cveId,
string binarySha256,
string? componentPurl = null,
CancellationToken ct = default);
Task<ImmutableArray<FixChainAttestationData>> GetForComponentAsync(
string componentPurl,
CancellationToken ct = default);
}
```
## Configuration
### YAML Configuration
```yaml
RiskEngine:
Providers:
FixChain:
Enabled: true
HighConfidenceThreshold: 0.95
MediumConfidenceThreshold: 0.85
LowConfidenceThreshold: 0.70
MinConfidenceThreshold: 0.60
FixedReduction: 0.90
PartialReduction: 0.50
MaxRiskReduction: 0.90
CacheMaxAgeHours: 24
```
### Service Registration
```csharp
services.AddOptions<FixChainRiskOptions>()
.Bind(config.GetSection("RiskEngine:Providers:FixChain"))
.ValidateDataAnnotations()
.ValidateOnStart();
services.AddSingleton<IFixChainRiskProvider, FixChainRiskProvider>();
services.AddHttpClient<IFixChainAttestationClient, FixChainAttestationClient>();
```
## Usage
### Getting Fix Status
```csharp
var provider = services.GetRequiredService<IFixChainRiskProvider>();
var status = await provider.GetFixStatusAsync(
"CVE-2024-0727",
binarySha256,
componentPurl);
if (status is not null)
{
var adjustment = provider.ComputeRiskAdjustment(status);
var adjustedRisk = baseRisk * adjustment;
}
```
### Creating Risk Factors
```csharp
var status = await provider.GetFixStatusAsync(cveId, binarySha256);
if (status is not null)
{
var factor = provider.CreateRiskFactor(status);
// For UI display
var display = factor.ToDisplay();
var badge = factor.ToBadge();
var summary = factor.ToSummary();
}
```
### Signal-Based Scoring
For batch processing via signals:
```csharp
var signals = new Dictionary<string, double>
{
[FixChainRiskProvider.SignalFixConfidence] = 0.95,
[FixChainRiskProvider.SignalFixStatus] = FixChainRiskProvider.EncodeStatus("fixed")
};
var request = new ScoreRequest("fixchain", subject, signals);
var adjustment = await provider.ScoreAsync(request, ct);
```
## Metrics
The integration exposes the following OpenTelemetry metrics:
| Metric | Type | Description |
|--------|------|-------------|
| `risk_fixchain_lookups_total` | Counter | Total attestation lookups |
| `risk_fixchain_hits_total` | Counter | Attestations found |
| `risk_fixchain_misses_total` | Counter | Lookups with no attestation |
| `risk_fixchain_cache_hits_total` | Counter | Lookups served from cache |
| `risk_fixchain_lookup_duration_seconds` | Histogram | Lookup duration |
| `risk_fixchain_adjustments_total` | Counter | Risk adjustments applied |
| `risk_fixchain_reduction_percent` | Histogram | Reduction percentage distribution |
| `risk_fixchain_errors_total` | Counter | Lookup errors |
### Recording Metrics
```csharp
// Automatically recorded by the provider, or manually:
FixChainRiskMetrics.RecordLookup(
found: true,
fromCache: false,
durationSeconds: 0.05,
verdict: "fixed");
FixChainRiskMetrics.RecordAdjustment(
verdict: FixChainVerdictStatus.Fixed,
confidence: 0.95m,
reductionPercent: 0.80);
```
## UI Integration
### Display Model
```csharp
var display = factor.ToDisplay();
// display.Label = "Fix Verification"
// display.Value = "Fixed (95% confidence)"
// display.Impact = -0.80
// display.ImpactDirection = "decrease"
// display.EvidenceRef = "fixchain://sha256:..."
// display.Details = { verdict, confidence, verified_at, ... }
```
### Badge Component
```csharp
var badge = factor.ToBadge();
// badge.Status = "Fixed"
// badge.Color = "green"
// badge.Icon = "check-circle"
// badge.Confidence = 0.95m
// badge.Tooltip = "Verified fix (95% confidence)"
```
## Testing
### Unit Tests
```csharp
[Fact]
public async Task FixedVerdict_HighConfidence_ReturnsLowRisk()
{
var provider = new FixChainRiskProvider(options);
var status = new FixVerificationStatus
{
Verdict = "fixed",
Confidence = 0.97m,
VerifiedAt = DateTimeOffset.UtcNow,
AttestationDigest = "sha256:test"
};
var adjustment = provider.ComputeRiskAdjustment(status);
adjustment.Should().BeLessThan(0.3);
}
```
### Integration Tests
```csharp
[Fact]
public async Task FullWorkflow_FixedVerdict_ReducesRisk()
{
var attestationClient = new InMemoryFixChainAttestationClient();
attestationClient.AddAttestation(cveId, binarySha256, attestation);
var provider = new FixChainRiskProvider(options, attestationClient, logger);
var status = await provider.GetFixStatusAsync(cveId, binarySha256);
status.Should().NotBeNull();
status!.Verdict.Should().Be("fixed");
}
```
## Decisions and Trade-offs
| Decision | Rationale |
|----------|-----------|
| Conservative thresholds | Start high, can lower based on accuracy data |
| No automatic upgrade | Inconclusive doesn't increase risk |
| Cache TTL 30 minutes | Balances freshness vs. performance |
| Attestation required | No reduction without verifiable evidence |
| Minimum confidence 60% | Below this, evidence is too weak for adjustment |
## Related Documentation
- [FixChain Attestation Predicate](../attestor/fix-chain-predicate.md)
- [Golden Set Schema](../binary-index/golden-set-schema.md)
- [Risk Engine Architecture](./architecture.md)

View File

@@ -1,45 +0,0 @@
# Risk API
> Based on `CONTRACT-RISK-SCORING-002` (2025-12-05). Examples are frozen in `docs/modules/risk-engine/samples/api/risk-api-samples.json` with hashes in `SHA256SUMS`. Keep ETags and error payloads deterministic.
## Purpose
- Document risk-related endpoints for profile management, simulation, scoring results, explainability retrieval, and export.
## Scope & Audience
- Audience: API consumers, SDK authors, platform integrators.
- In scope: endpoint list, methods, request/response schemas, auth/tenancy headers, rate limits, feature flags, error model.
- Out of scope: console/UI workflow details (see `explainability.md`).
## Endpoints (v1)
- `POST /api/v1/risk/jobs` — submit scoring job (body: job request); returns `202` with `job_id` and `status` (`queued`). Sample: `risk-api-samples.json#submit_job_request`.
- `GET /api/v1/risk/jobs/{job_id}` — job status + results array (sample: `get_job_status`).
- `GET /api/v1/risk/explain/{job_id}` — explainability payload (sample references `../explain/explain-trace.json`). Optional `If-None-Match` for caching.
- `GET /api/v1/risk/profiles` — list profiles (tenant-filtered); includes `profile_hash`, `version`, `etag` (see error-catalog headers).
- `POST /api/v1/risk/profiles` — create/update profile with DSSE/attestation metadata; returns `201` with `etag`.
- `POST /api/v1/risk/simulations` — dry-run scoring with fixtures; returns explain + contributions without persisting results.
- `GET /api/v1/risk/export/{job_id}` — export bundle (JSON + CSV + manifest) for auditors.
- Feature flags: `risk.jobs`, `risk.explain`, `risk.simulations`, `risk.export` (toggle exposure per tenant).
## Auth & Tenancy
- Required headers: `X-Stella-Tenant`, `Authorization: Bearer <token>`, optional `X-Stella-Scope` for imposed rule reminders.
- Imposed rule reminder must be present in responses where tenant-bound resources are returned.
## Error Model
- Envelope: `code`, `message`, `correlation_id`, `severity`, `remediation`; sample catalog in `docs/modules/risk-engine/samples/api/error-catalog.json`.
- Rate-limit headers: `Retry-After`, `X-RateLimit-Remaining`; caching headers include `ETag` for explain/results/profile GETs.
## Determinism & Offline Posture
- Samples: `docs/modules/risk-engine/samples/api/risk-api-samples.json` (hashes in `SHA256SUMS`); explain sample reused via relative reference.
- No live dependencies; use frozen fixtures. Keep ordering of fields stable in docs and samples.
## Open Items
- Add ETag examples for profile list/create once generators emit them.
- Populate error/code catalog and SDK targets once available.
- Align feature flag names with deployment config.
## References
- `docs/modules/risk-engine/guides/overview.md`
- `docs/modules/risk-engine/guides/profiles.md`
- `docs/modules/risk-engine/guides/factors.md`
- `docs/modules/risk-engine/guides/formulas.md`
- `docs/modules/risk-engine/guides/explainability.md`

View File

@@ -1,817 +0,0 @@
# EPSS v4 Integration Guide
## Overview
EPSS (Exploit Prediction Scoring System) v4 is a machine learning-based vulnerability scoring system developed by FIRST.org that predicts the probability a CVE will be exploited in the wild within the next 30 days. StellaOps integrates EPSS as a **probabilistic threat signal** alongside CVSS v4's **deterministic severity assessment**, enabling more accurate vulnerability prioritization.
**Key Concepts**:
- **EPSS Score**: Probability (0.0-1.0) that a CVE will be exploited in next 30 days
- **EPSS Percentile**: Ranking (0.0-1.0) of this CVE relative to all scored CVEs
- **Model Date**: Date for which EPSS scores were computed
- **Immutable at-scan**: EPSS evidence captured at scan time never changes (deterministic replay)
- **Current EPSS**: Live projection for triage (updated daily)
---
## EPSS Versioning Clarification
> **Note on "EPSS v4" Terminology**
>
> The term "EPSS v4" used in this document is a conceptual identifier aligning with CVSS v4 integration, **not** an official FIRST.org version number. FIRST.org's EPSS does not use explicit version numbers like "v1", "v2", etc.
>
> **How EPSS Versioning Actually Works:**
> - EPSS models are identified by **model_date** (e.g., `2025-12-16`)
> - Each daily CSV release represents a new model trained on updated threat data
> - The EPSS specification itself evolves without formal version increments
>
> **StellaOps Implementation:**
> - Tracks `model_date` for each EPSS score ingested
> - Does not assume a formal EPSS version number
> - Evidence replay uses the `model_date` from scan time
>
> For authoritative EPSS methodology, see: [FIRST.org EPSS Documentation](https://www.first.org/epss/)
---
## How EPSS Works
EPSS uses machine learning to predict exploitation probability based on:
1. **Vulnerability Characteristics**: CVSS metrics, CWE, affected products
2. **Social Signals**: Twitter/GitHub mentions, security blog posts
3. **Exploit Database Entries**: Exploit-DB, Metasploit, etc.
4. **Historical Exploitation**: Past exploitation patterns
EPSS is updated **daily** by FIRST.org based on fresh threat intelligence.
### EPSS vs CVSS
| Dimension | CVSS v4 | EPSS v4 |
|-----------|---------|---------|
| **Nature** | Deterministic severity | Probabilistic threat |
| **Scale** | 0.0-10.0 (severity) | 0.0-1.0 (probability) |
| **Update Frequency** | Static (per CVE version) | Daily (live threat data) |
| **Purpose** | Impact assessment | Likelihood assessment |
| **Source** | Vendor/NVD | FIRST.org ML model |
**Example**:
- **CVE-2024-1234**: CVSS 9.8 (Critical) + EPSS 0.01 (1st percentile)
- Interpretation: Severe impact if exploited, but very unlikely to be exploited
- Priority: **Medium** (deprioritize despite high CVSS)
- **CVE-2024-5678**: CVSS 6.5 (Medium) + EPSS 0.95 (98th percentile)
- Interpretation: Moderate impact, but actively being exploited
- Priority: **High** (escalate despite moderate CVSS)
---
## Architecture Overview
### Data Flow
```
┌────────────────────────────────────────────────────────────────┐
│ EPSS Data Lifecycle in StellaOps │
└────────────────────────────────────────────────────────────────┘
1. INGESTION (Daily 00:05 UTC)
┌───────────────────┐
│ FIRST.org │ Daily CSV: epss_scores-YYYY-MM-DD.csv.gz
│ (300k CVEs) │ ~15MB compressed
└────────┬──────────┘
┌───────────────────────────────────────────────────────────┐
│ Concelier: EpssIngestJob │
│ - Download/Import CSV │
│ - Parse (handle # comment, validate bounds) │
│ - Bulk insert: epss_scores (partitioned by month) │
│ - Compute delta: epss_changes (flags for enrichment) │
│ - Upsert: epss_current (latest projection) │
│ - Emit event: "epss.updated" │
└────────┬──────────────────────────────────────────────────┘
[PostgreSQL: concelier.epss_*]
├─────────────────────────────┐
│ │
▼ ▼
2. AT-SCAN CAPTURE (Immutable Evidence)
┌────────────────────────────────────────────────────────────┐
│ Scanner: On new scan │
│ - Bulk query: epss_current for CVE list │
│ - Store immutable evidence: │
│ * epss_score_at_scan │
│ * epss_percentile_at_scan │
│ * epss_model_date_at_scan │
│ * epss_import_run_id_at_scan │
│ - Use in lattice decision (SR→CR if EPSS≥90th) │
└─────────────────────────────────────────────────────────────┘
3. LIVE ENRICHMENT (Existing Findings)
┌─────────────────────────────────────────────────────────────┐
│ Concelier: EpssEnrichmentJob (on "epss.updated") │
│ - Read: epss_changes WHERE flags IN (CROSSED_HIGH, BIG_JUMP)│
│ - Find impacted: vuln_instance_triage BY cve_id │
│ - Update: current_epss_score, current_epss_percentile │
│ - If priority band changed → emit "vuln.priority.changed" │
└────────┬────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Notify: On "vuln.priority.changed" │
│ - Check tenant notification rules │
│ - Send: Slack / Email / Teams / In-app │
│ - Payload: EPSS delta, threshold crossed │
└─────────────────────────────────────────────────────────────┘
4. POLICY SCORING
┌─────────────────────────────────────────────────────────────┐
│ Policy Engine: Risk Score Formula │
│ risk_score = (cvss/10) + epss_bonus + kev_bonus + reach_mult│
│ │
│ EPSS Bonus (Simple Profile): │
│ - Percentile ≥99th: +10% │
│ - Percentile ≥90th: +5% │
│ - Percentile ≥50th: +2% │
│ - Percentile <50th: 0% │
│ │
│ VEX Lattice Rules: │
│ - SR + EPSS≥90th → Escalate to CR (Confirmed Reachable) │
│ - DV + EPSS≥95th → Flag for review (vendor denial) │
│ - U + EPSS≥95th → Prioritize for reachability analysis │
└─────────────────────────────────────────────────────────────┘
```
### Database Schema
**Location**: `concelier` database
#### epss_import_runs (Provenance)
Tracks each EPSS import with full provenance for audit trail.
```sql
CREATE TABLE concelier.epss_import_runs (
import_run_id UUID PRIMARY KEY,
model_date DATE NOT NULL UNIQUE,
source_uri TEXT NOT NULL,
file_sha256 TEXT NOT NULL,
row_count INT NOT NULL,
model_version_tag TEXT NULL,
published_date DATE NULL,
status TEXT NOT NULL, -- IN_PROGRESS, SUCCEEDED, FAILED
created_at TIMESTAMPTZ NOT NULL
);
```
#### epss_scores (Time-Series, Partitioned)
Immutable append-only history of daily EPSS scores.
```sql
CREATE TABLE concelier.epss_scores (
model_date DATE NOT NULL,
cve_id TEXT NOT NULL,
epss_score DOUBLE PRECISION NOT NULL,
percentile DOUBLE PRECISION NOT NULL,
import_run_id UUID NOT NULL,
PRIMARY KEY (model_date, cve_id)
) PARTITION BY RANGE (model_date);
```
**Partitions**: Monthly (e.g., `epss_scores_2025_12`)
#### epss_current (Latest Projection)
Materialized view of latest EPSS score per CVE for fast lookups.
```sql
CREATE TABLE concelier.epss_current (
cve_id TEXT PRIMARY KEY,
epss_score DOUBLE PRECISION NOT NULL,
percentile DOUBLE PRECISION NOT NULL,
model_date DATE NOT NULL,
import_run_id UUID NOT NULL,
updated_at TIMESTAMPTZ NOT NULL
);
```
**Usage**: Scanner bulk queries this table for new scans.
#### epss_changes (Delta Tracking, Partitioned)
Tracks material EPSS changes for targeted enrichment.
```sql
CREATE TABLE concelier.epss_changes (
model_date DATE NOT NULL,
cve_id TEXT NOT NULL,
old_score DOUBLE PRECISION NULL,
new_score DOUBLE PRECISION NOT NULL,
delta_score DOUBLE PRECISION NULL,
old_percentile DOUBLE PRECISION NULL,
new_percentile DOUBLE PRECISION NOT NULL,
delta_percentile DOUBLE PRECISION NULL,
flags INT NOT NULL, -- Bitmask
PRIMARY KEY (model_date, cve_id)
) PARTITION BY RANGE (model_date);
```
**Flags** (bitmask):
- `1` = NEW_SCORED (CVE newly appeared)
- `2` = CROSSED_HIGH (percentile ≥95th)
- `4` = BIG_JUMP (|Δscore| ≥0.10)
- `8` = DROPPED_LOW (percentile <50th)
- `16` = SCORE_INCREASED
- `32` = SCORE_DECREASED
---
## Configuration
### Scheduler Configuration
**File**: `etc/scheduler.yaml`
```yaml
scheduler:
jobs:
- name: epss.ingest
schedule: "0 5 0 * * *" # Daily at 00:05 UTC
worker: concelier
args:
source: online
date: null # Auto: yesterday
timeout: 600s
retry:
max_attempts: 3
backoff: exponential
```
### Concelier Configuration
**File**: `etc/concelier.yaml`
```yaml
concelier:
epss:
enabled: true
online_source:
base_url: "https://epss.empiricalsecurity.com/"
url_pattern: "epss_scores-{date:yyyy-MM-dd}.csv.gz"
timeout: 180s
bundle_source:
path: "/opt/stellaops/bundles/epss/"
thresholds:
high_percentile: 0.95 # Top 5%
high_score: 0.50 # 50% probability
big_jump_delta: 0.10 # 10 percentage points
low_percentile: 0.50 # Median
enrichment:
enabled: true
batch_size: 1000
flags_to_process:
- NEW_SCORED
- CROSSED_HIGH
- BIG_JUMP
```
### Scanner Configuration
**File**: `etc/scanner.yaml`
```yaml
scanner:
epss:
enabled: true
provider: postgres
cache_ttl: 3600
fallback_on_missing: unknown # Options: unknown, zero, skip
```
### Policy Configuration
**File**: `etc/policy.yaml`
```yaml
policy:
scoring:
epss:
enabled: true
profile: simple # Options: simple, advanced, custom
simple_bonuses:
percentile_99: 0.10 # +10%
percentile_90: 0.05 # +5%
percentile_50: 0.02 # +2%
lattice:
epss_escalation:
enabled: true
sr_to_cr_threshold: 0.90 # SR→CR if EPSS≥90th percentile
```
---
## Daily Operation
### Automated Ingestion
EPSS data is ingested automatically daily at **00:05 UTC** via Scheduler.
**Workflow**:
1. Scheduler triggers `epss.ingest` job at 00:05 UTC
2. Concelier downloads `epss_scores-YYYY-MM-DD.csv.gz` from FIRST.org
3. CSV parsed (comment line metadata, rows scores)
4. Bulk insert into `epss_scores` partition (NpgsqlBinaryImporter)
5. Compute delta: `epss_changes` (compare vs `epss_current`)
6. Upsert `epss_current` (latest projection)
7. Emit `epss.updated` event
8. Enrichment job updates impacted vulnerability instances
9. Notifications sent if priority bands changed
**Monitoring**:
```bash
# Check latest model date
stellaops epss status
# Output:
# EPSS Status:
# Latest Model Date: 2025-12-16
# Import Time: 2025-12-17 00:07:32 UTC
# CVE Count: 231,417
# Staleness: FRESH (1 day)
```
### Manual Triggering
```bash
# Trigger manual ingest (force re-import)
stellaops concelier job trigger epss.ingest --date 2025-12-16 --force
# Backfill historical data (last 30 days)
stellaops epss backfill --from 2025-11-17 --to 2025-12-16
```
---
## Air-Gapped Operation
### Bundle Structure
EPSS data for offline deployments is packaged in risk bundles:
```
risk-bundle-2025-12-16/
├── manifest.json
├── epss/
│ ├── epss_scores-2025-12-16.csv.zst # ZSTD compressed
│ └── epss_metadata.json
├── kev/
│ └── kev-catalog.json
└── signatures/
└── bundle.dsse.json
```
### EPSS Metadata
**File**: `epss/epss_metadata.json`
```json
{
"model_date": "2025-12-16",
"model_version": "v2025.12.16",
"published_date": "2025-12-16",
"row_count": 231417,
"sha256": "abc123...",
"source_uri": "https://epss.empiricalsecurity.com/epss_scores-2025-12-16.csv.gz",
"created_at": "2025-12-16T00:00:00Z"
}
```
### Import Procedure
```bash
# 1. Transfer bundle to air-gapped system
scp risk-bundle-2025-12-16.tar.zst airgap-host:/opt/stellaops/bundles/
# 2. Import bundle
stellaops offline import --bundle /opt/stellaops/bundles/risk-bundle-2025-12-16.tar.zst
# 3. Verify import
stellaops epss status
# Output:
# EPSS Status:
# Latest Model Date: 2025-12-16
# Source: bundle://risk-bundle-2025-12-16
# CVE Count: 231,417
# Staleness: ACCEPTABLE (within 7 days)
```
### Update Cadence
**Recommended**:
- **Online**: Daily (automatic)
- **Air-gapped**: Weekly (manual bundle import)
**Staleness Thresholds**:
- **FRESH**: 1 day
- **ACCEPTABLE**: 7 days
- **STALE**: 14 days
- **VERY_STALE**: >14 days (alert, fallback to CVSS-only)
---
## Scanner Integration
### EPSS Evidence in Scan Findings
Every scan finding includes **immutable EPSS-at-scan** evidence:
```json
{
"finding_id": "CVE-2024-12345-pkg:npm/lodash@4.17.21",
"cve_id": "CVE-2024-12345",
"product": "pkg:npm/lodash@4.17.21",
"scan_id": "scan-abc123",
"scan_timestamp": "2025-12-17T10:30:00Z",
"evidence": {
"cvss_v4": {
"vector_string": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H",
"base_score": 9.3,
"severity": "CRITICAL"
},
"epss_at_scan": {
"epss_score": 0.42357,
"percentile": 0.88234,
"model_date": "2025-12-16",
"import_run_id": "550e8400-e29b-41d4-a716-446655440000"
},
"epss_current": {
"epss_score": 0.45123,
"percentile": 0.89456,
"model_date": "2025-12-17",
"delta_score": 0.02766,
"delta_percentile": 0.01222,
"trend": "RISING"
}
}
}
```
**Key Points**:
- **epss_at_scan**: Immutable, captured at scan time (deterministic replay)
- **epss_current**: Mutable, updated daily for live triage
- **Replay**: Historical scans always use `epss_at_scan` for consistent policy evaluation
### Bulk Query Optimization
Scanner queries EPSS for all CVEs in a single database call:
```sql
SELECT cve_id, epss_score, percentile, model_date, import_run_id
FROM concelier.epss_current
WHERE cve_id = ANY(@cve_ids);
```
**Performance**: <500ms for 10k CVEs (P95)
---
## Policy Engine Integration
### Risk Score Formula
**Simple Profile**:
```
risk_score = (cvss_base / 10) + epss_bonus + kev_bonus
```
**EPSS Bonus Table**:
| EPSS Percentile | Bonus | Rationale |
|----------------|-------|-----------|
| 99th | +10% | Top 1% most likely to be exploited |
| 90th | +5% | Top 10% high exploitation probability |
| 50th | +2% | Above median moderate risk |
| <50th | 0% | Below median no bonus |
**Advanced Profile**:
Adds:
- **KEV synergy**: If in KEV catalog multiply EPSS bonus by 1.5
- **Uncertainty penalty**: Missing EPSS -5%
- **Temporal decay**: EPSS >30 days stale → reduce bonus by 50%
### VEX Lattice Rules
**Escalation**:
- **SR (Static Reachable) + EPSS≥90th** → Auto-escalate to **CR (Confirmed Reachable)**
- Rationale: High exploit probability warrants confirmation
**Review Flags**:
- **DV (Denied by Vendor VEX) + EPSS≥95th** → Flag for manual review
- Rationale: Vendor denial contradicted by active exploitation signals
**Prioritization**:
- **U (Unknown) + EPSS≥95th** → Prioritize for reachability analysis
- Rationale: High exploit probability justifies effort
### SPL (Stella Policy Language) Syntax
```yaml
# Custom policy using EPSS
rules:
- name: high_epss_escalation
condition: |
epss.percentile >= 0.95 AND
lattice.state == "SR" AND
runtime.exposed == true
action: escalate_to_cr
reason: "High EPSS (top 5%) + Static Reachable + Runtime Exposed"
- name: epss_trend_alert
condition: |
epss.delta_score >= 0.10 AND
cvss.base_score >= 7.0
action: notify
channels: [slack, email]
reason: "EPSS jumped by 10+ points (was {epss.old_score}, now {epss.new_score})"
```
**Available Fields**:
- `epss.score` - Current EPSS score (0.0-1.0)
- `epss.percentile` - Current percentile (0.0-1.0)
- `epss.model_date` - Model date
- `epss.delta_score` - Change vs previous scan
- `epss.trend` - RISING, FALLING, STABLE
- `epss.at_scan.score` - Immutable score at scan time
- `epss.at_scan.percentile` - Immutable percentile at scan time
---
## Notification Integration
### Event: vuln.priority.changed
Emitted when EPSS change causes priority band shift.
**Payload**:
```json
{
"event_type": "vuln.priority.changed",
"vulnerability_id": "CVE-2024-12345",
"product_key": "pkg:npm/lodash@4.17.21",
"old_priority_band": "medium",
"new_priority_band": "high",
"reason": "EPSS percentile crossed 95th (was 88th, now 96th)",
"epss_change": {
"old_score": 0.42,
"new_score": 0.78,
"delta_score": 0.36,
"old_percentile": 0.88,
"new_percentile": 0.96,
"model_date": "2025-12-16"
}
}
```
### Notification Rules
**File**: `etc/notify.yaml`
```yaml
notify:
rules:
- name: epss_crossed_high
event_type: vuln.priority.changed
condition: "payload.epss_change.new_percentile >= 0.95"
channels: [slack, email]
template: epss_high_alert
digest: false # Immediate
- name: epss_big_jump
event_type: vuln.priority.changed
condition: "payload.epss_change.delta_score >= 0.10"
channels: [slack]
template: epss_rising_threat
digest: true
digest_time: "09:00" # Daily digest at 9 AM
```
### Slack Template Example
```
🚨 **High EPSS Alert**
**CVE**: CVE-2024-12345
**Product**: pkg:npm/lodash@4.17.21
**EPSS**: 0.78 (96th percentile) ⬆️ from 0.42 (88th percentile)
**Delta**: +0.36 (36 percentage points)
**Priority**: Medium → **High**
**Action Required**: Review and prioritize remediation.
[View in StellaOps →](https://stellaops.example.com/vulns/CVE-2024-12345)
```
---
## Troubleshooting
### EPSS Data Not Available
**Symptom**: Scans show "EPSS: N/A"
**Diagnosis**:
```bash
# Check EPSS status
stellaops epss status
# Check import runs
stellaops concelier jobs list --type epss.ingest --limit 10
```
**Resolution**:
1. **No imports**: Trigger manual ingest
```bash
stellaops concelier job trigger epss.ingest
```
2. **Import failed**: Check logs
```bash
stellaops concelier logs --job-id <id> --level ERROR
```
3. **FIRST.org down**: Use air-gapped bundle
```bash
stellaops offline import --bundle /path/to/risk-bundle.tar.zst
```
### Stale EPSS Data
**Symptom**: UI shows "EPSS stale (14 days)"
**Diagnosis**:
```sql
SELECT * FROM concelier.epss_model_staleness;
-- Output: days_stale: 14, staleness_status: STALE
```
**Resolution**:
1. **Online**: Check scheduler job status
```bash
stellaops scheduler jobs status epss.ingest
```
2. **Air-gapped**: Import fresh bundle
```bash
stellaops offline import --bundle /path/to/latest-bundle.tar.zst
```
3. **Fallback**: Disable EPSS temporarily (uses CVSS-only)
```yaml
# etc/scanner.yaml
scanner:
epss:
enabled: false
```
### High Memory Usage During Ingest
**Symptom**: Concelier worker OOM during EPSS ingest
**Diagnosis**:
```bash
# Check memory metrics
stellaops metrics query 'process_resident_memory_bytes{service="concelier"}'
```
**Resolution**:
1. **Increase worker memory limit**:
```yaml
# Kubernetes deployment
resources:
limits:
memory: 1Gi # Was 512Mi
```
2. **Verify streaming parser** (should not load full CSV into memory):
```bash
# Check logs for "EPSS CSV parsed: rows_yielded="
stellaops concelier logs --job-type epss.ingest | grep "CSV parsed"
```
---
## Best Practices
### 1. Combine Signals (Never Use EPSS Alone)
❌ **Don't**: `if epss > 0.95 then CRITICAL`
✅ **Do**: `if cvss >= 8.0 AND epss >= 0.95 AND runtime_exposed then CRITICAL`
### 2. Review High EPSS Manually
Manually review vulnerabilities with EPSS ≥95th percentile, especially if:
- CVSS is low (<7.0) but EPSS is high
- Vendor VEX denies exploitability but EPSS is high
### 3. Track Trends
Monitor EPSS changes over time:
- Rising EPSS → increasing threat
- Falling EPSS → threat subsiding
### 4. Update Regularly
- **Online**: Daily (automatic)
- **Air-gapped**: Weekly minimum, daily preferred
### 5. Verify During Audits
For compliance audits, use EPSS-at-scan (immutable) not current EPSS:
```sql
SELECT epss_score_at_scan, epss_model_date_at_scan
FROM scan_findings
WHERE scan_id = 'audit-scan-20251217';
```
---
## API Reference
### Query Current EPSS
```bash
# Single CVE
stellaops epss get CVE-2024-12345
# Output:
# CVE-2024-12345
# Score: 0.42357 (42.4% probability)
# Percentile: 88.2th
# Model Date: 2025-12-16
# Status: FRESH
```
### Batch Query
```bash
# From file
stellaops epss batch --file cves.txt --output epss-scores.json
# cves.txt:
# CVE-2024-1
# CVE-2024-2
# CVE-2024-3
```
### Query History
```bash
# Last 180 days
stellaops epss history CVE-2024-12345 --days 180 --format csv
# Output: epss-history-CVE-2024-12345.csv
# model_date,epss_score,percentile
# 2025-12-17,0.45123,0.89456
# 2025-12-16,0.42357,0.88234
# ...
```
### Top CVEs by EPSS
```bash
# Top 100
stellaops epss top --limit 100 --format table
# Output:
# Rank | CVE | Score | Percentile | CVSS
# -----|---------------|--------|------------|------
# 1 | CVE-2024-9999 | 0.9872 | 99.9th | 9.8
# 2 | CVE-2024-8888 | 0.9654 | 99.8th | 8.1
# ...
```
---
## References
- **FIRST EPSS Homepage**: https://www.first.org/epss/
- **EPSS Data & Stats**: https://www.first.org/epss/data_stats
- **EPSS API Docs**: https://www.first.org/epss/api
- **CVSS v4.0 Spec**: https://www.first.org/cvss/v4.0/specification-document
- **StellaOps Policy Guide**: `docs/policy/overview.md`
- **StellaOps Reachability Guide**: `docs/modules/scanner/reachability.md`
---
**Last Updated**: 2025-12-17
**Version**: 1.0
**Maintainer**: StellaOps Security Team

View File

@@ -1,290 +0,0 @@
# EPSS Integration Guide
## Overview
EPSS (Exploit Prediction Scoring System) is a FIRST.org initiative that provides probability scores for vulnerability exploitation within 30 days. StellaOps integrates EPSS as a risk signal alongside CVSS and KEV (Known Exploited Vulnerabilities) to provide more accurate vulnerability prioritization.
## How EPSS Works
EPSS uses machine learning to predict the probability that a CVE will be exploited in the wild within the next 30 days. The model considers:
- Vulnerability characteristics (CVSS metrics, CWE, etc.)
- Social signals (Twitter mentions, GitHub issues, etc.)
- Exploit database entries
- Historical exploitation patterns
EPSS outputs two values:
- **Score** (0.0-1.0): Probability of exploitation in next 30 days
- **Percentile** (0-100): Ranking relative to all other CVEs
## How EPSS Affects Risk Scoring in StellaOps
### Combined Risk Formula
StellaOps combines CVSS, KEV, and EPSS signals into a unified risk score:
```
risk_score = clamp01(
(cvss / 10) + # Base severity (0-1)
kevBonus + # +0.20 if in CISA KEV
epssBonus # +0.02 to +0.10 based on percentile
)
```
### EPSS Bonus Thresholds
| EPSS Percentile | Bonus | Rationale |
|-----------------|-------|-----------|
| >= 99th | +10% | Top 1% most likely to be exploited; urgent priority |
| >= 90th | +5% | Top 10%; high exploitation probability |
| >= 50th | +2% | Above median; moderate additional risk |
| < 50th | 0% | Below median; no bonus applied |
### Example Calculations
| CVE | CVSS | KEV | EPSS Percentile | Risk Score |
|-----|------|-----|-----------------|------------|
| CVE-2024-1234 | 9.8 | Yes | 99.5th | 1.00 (clamped) |
| CVE-2024-5678 | 7.5 | No | 95th | 0.80 |
| CVE-2024-9012 | 6.0 | No | 60th | 0.62 |
| CVE-2024-3456 | 8.0 | No | 30th | 0.80 |
## Implementation Reference
### IEpssSource Interface
```csharp
// Location: src/RiskEngine/StellaOps.RiskEngine/StellaOps.RiskEngine.Core/Providers/IEpssSources.cs
public interface IEpssSource
{
/// <summary>
/// Returns EPSS data for the given CVE identifier, or null if unknown.
/// </summary>
Task<EpssData?> GetEpssAsync(string cveId, CancellationToken cancellationToken);
}
public sealed record EpssData(double Score, double Percentile, DateTimeOffset? ModelVersion = null);
```
### Risk Providers
**EpssProvider** - Uses EPSS score directly as risk (0.0-1.0):
```csharp
// Location: src/RiskEngine/StellaOps.RiskEngine/StellaOps.RiskEngine.Core/Providers/EpssProvider.cs
public const string ProviderName = "epss";
```
**CvssKevEpssProvider** - Combined provider using all three signals:
```csharp
// Location: src/RiskEngine/StellaOps.RiskEngine/StellaOps.RiskEngine.Core/Providers/EpssProvider.cs
public const string ProviderName = "cvss-kev-epss";
```
## Policy Configuration
### Enabling EPSS Integration
```yaml
# etc/risk-engine.yaml
risk:
providers:
- name: cvss-kev-epss
enabled: true
priority: 1
epss:
enabled: true
source: database # or "api" for live FIRST API
cache_ttl: 24h
# Percentile-based bonus thresholds
thresholds:
- percentile: 99
bonus: 0.10
- percentile: 90
bonus: 0.05
- percentile: 50
bonus: 0.02
```
### Custom Threshold Configuration
Organizations can customize EPSS bonus thresholds based on their risk tolerance:
```yaml
# More aggressive (higher bonuses for high-risk vulns)
epss:
thresholds:
- percentile: 99
bonus: 0.15
- percentile: 95
bonus: 0.10
- percentile: 75
bonus: 0.05
# More conservative (smaller bonuses)
epss:
thresholds:
- percentile: 99
bonus: 0.05
- percentile: 95
bonus: 0.02
```
## EPSS in Lattice Decisions
EPSS influences VEX lattice state transitions for vulnerability triage:
| Current State | EPSS >= 90th Percentile | Recommended Action |
|---------------|-------------------------|-------------------|
| SR (Static Reachable) | Yes | Escalate to CR (Confirmed Reachable) priority |
| SU (Static Unreachable) | Yes | Flag for review - high exploit probability despite unreachable |
| DV (Denied by Vendor VEX) | Yes | Review denial validity - exploit activity contradicts vendor |
| U (Unknown) | Yes | Prioritize for reachability analysis |
### VEX Policy Example
```yaml
# etc/vex-policy.yaml
lattice:
transitions:
- from: SR
to: CR
condition:
epss_percentile: ">= 90"
action: auto_escalate
- from: SU
to: REVIEW
condition:
epss_percentile: ">= 95"
action: flag_for_review
reason: "High EPSS despite static unreachability"
```
## Offline EPSS Data
EPSS data is included in offline risk bundles for air-gapped environments.
### Bundle Structure
```
risk-bundle-2025-12-14/
├── manifest.json
├── kev/
│ └── kev-catalog.json
├── epss/
│ ├── epss-scores.csv.zst # Compressed EPSS data
│ └── epss-metadata.json # Model date, row count, checksum
└── signatures/
└── bundle.dsse.json
```
### EPSS Metadata
```json
{
"model_date": "2025-12-14",
"row_count": 248732,
"sha256": "abc123...",
"source": "first.org",
"created_at": "2025-12-14T00:00:00Z"
}
```
### Importing Offline EPSS Data
```bash
# Import risk bundle (includes EPSS)
stellaops offline import --kit risk-bundle-2025-12-14.tar.zst
# Verify EPSS data imported
stellaops epss status
# Output:
# EPSS Data Status:
# Model Date: 2025-12-14
# CVE Count: 248,732
# Last Import: 2025-12-14T10:30:00Z
```
## Accuracy Considerations
| Metric | Value | Notes |
|--------|-------|-------|
| EPSS Coverage | ~95% of NVD CVEs | Some very new CVEs (<24h) not yet scored |
| Model Refresh | Daily | Scores can change day-to-day |
| Prediction Window | 30 days | Probability of exploit in next 30 days |
| Historical Accuracy | ~85% AUC | Based on FIRST published evaluations |
### Limitations
1. **New CVEs**: Very recent CVEs may not have EPSS scores yet
2. **Model Lag**: EPSS model updates daily; real-world exploit activity may be faster
3. **Zero-Days**: Pre-disclosure vulnerabilities cannot be scored
4. **Context Blind**: EPSS doesn't consider your specific environment
## Best Practices
1. **Combine Signals**: Always use EPSS alongside CVSS and KEV, not in isolation
2. **Review High EPSS**: Manually review vulnerabilities with EPSS >= 95th percentile
3. **Track Changes**: Monitor EPSS score changes over time for trending threats
4. **Update Regularly**: Keep EPSS data fresh (daily in online mode, weekly for offline)
5. **Verify High-Risk**: For critical decisions, verify EPSS data against FIRST API
## API Usage
### Query EPSS Score
```bash
# Get EPSS score for a specific CVE
stellaops epss get CVE-2024-12345
# Batch query
stellaops epss batch --file cves.txt --output epss-scores.json
```
### Programmatic Access
```csharp
// Using IEpssSource
var epssData = await epssSource.GetEpssAsync("CVE-2024-12345", cancellationToken);
if (epssData is not null)
{
Console.WriteLine($"Score: {epssData.Score:P2}");
Console.WriteLine($"Percentile: {epssData.Percentile:F1}th");
}
```
## Troubleshooting
### EPSS Data Not Available
```bash
# Check EPSS source status
stellaops epss status
# Force refresh from FIRST API
stellaops epss refresh --force
# Check for specific CVE
stellaops epss get CVE-2024-12345 --verbose
```
### Stale EPSS Data
If EPSS data is older than 7 days:
```bash
# Check staleness
stellaops epss check-staleness
# Import fresh bundle
stellaops offline import --kit latest-bundle.tar.zst
```
## References
- [FIRST EPSS Model](https://www.first.org/epss/)
- [EPSS API Documentation](https://www.first.org/epss/api)
- [EPSS FAQ](https://www.first.org/epss/faq)
- [StellaOps Risk Engine Architecture](../modules/risk-engine/architecture.md)

View File

@@ -1,35 +0,0 @@
# Risk Explainability
> Source: `CONTRACT-RISK-SCORING-002` (2025-12-05). Fixtures live under `docs/modules/risk-engine/samples/explain/`; all hashes in `SHA256SUMS`. Keep outputs deterministic (frozen payloads, stable ordering).
## Purpose
- Show how the scoring engine produces per-factor contributions and traces that UI/CLI/export surfaces render for auditors and operators.
## Scope & Audience
- Audience: Console/CLI users, auditors, SREs.
- In scope: explainability payload shape, field meanings, provenance, UI/CLI mapping, offline/export behavior.
- Out of scope: formula math (see `formulas.md`), API specifics (see `api.md`).
## Payload Shape
- Envelope: `job_id`, `tenant_id`, `context_id`, `profile_id`, `profile_version`, `profile_hash`, `finding_id`, `raw_score`, `normalized_score`, `severity`, `signal_values{}`, `signal_contributions{}`, optional `override_applied`, `override_reason`, `gates_triggered[]`, `scored_at`, `provenance` (job hash + fixture hashes).
- Factor entries (from `signal_values`/`signal_contributions`): `name`, `source`, `type`, `path`, `raw_value`, `normalized_value`, `weight`, `contribution`, `provenance`.
- UI/CLI expectations: deterministic ordering (factor type → source → timestamp), highlight top contributors, show attestation status for each factor.
## UI/CLI Views
- Console: frame sample in `docs/modules/risk-engine/samples/explain/console-frame.json` shows top contributors, gate badges, and provenance hashes.
- CLI `stella risk explain job-001`: deterministic text fixture in `docs/modules/risk-engine/samples/explain/cli-explain.txt`; `--json` mirrors `explain-trace.json`.
- Export Center: embed explain payload + SHA256 manifest; CSV export keeps deterministic ordering.
## Determinism & Offline Posture
- Example payload: `docs/modules/risk-engine/samples/explain/explain-trace.json` (hash in `SHA256SUMS`).
- No live calls; all captures from frozen fixtures. Use exact ordering and timestamps when regenerating.
## Open Items
- Add schema file once JSON schema is frozen; update references accordingly.
## References
- `docs/modules/risk-engine/guides/overview.md`
- `docs/modules/risk-engine/guides/profiles.md`
- `docs/modules/risk-engine/guides/factors.md`
- `docs/modules/risk-engine/guides/formulas.md`
- `docs/modules/risk-engine/guides/api.md`

View File

@@ -1,46 +0,0 @@
# Risk Factors
> Aligned to `CONTRACT-RISK-SCORING-002` (published 2025-12-05). Keep fixtures deterministic and offline-friendly.
## Purpose
- Catalog supported factors (exploit likelihood, VEX state, reachability, runtime facts, fix availability, asset criticality, provenance trust, tenant overrides) and how they normalize into risk math.
## Scope & Audience
- Audience: risk engineers, policy authors, platform SREs.
- In scope: factor definitions, required/optional fields, normalization rules, TTLs, provenance expectations.
- Out of scope: full formula math (see `formulas.md`), API wiring (see `api.md`).
## Factor Catalog (mirrors profile `signals[]`)
| Factor | Required fields | Optional fields | Notes |
| --- | --- | --- | --- |
| CVSS / exploit likelihood | `name`, `source`, `type:"numeric"`, `path`, `transform:"normalize_10"` | `unit:"score"`, `last_seen`, `confidence` | Normalize 010 to 01; clamp and keep original in provenance. |
| KEV flag | `name`, `source`, `type:"boolean"`, `path` | `last_seen` | Boolean boost; drives severity overrides/decisions. |
| Reachability | `name`, `source`, `type:"numeric"`, `path` | `unit:"score"`, `guards` | May fuse static reachability + runtime observation; ordered by entrypoint/path hash. |
| Runtime facts | `name`, `source`, `type:"categorical" or "numeric"`, `path` | `trace_id`, `span_id` | Includes host/container identity and provenance for runtime traces. |
| Fix availability | `name`, `source`, `type`, `path` | `mitigation`, `vendor_status` | Decay older advisories; keep mitigation text intact. |
| Asset criticality | `name`, `source`, `type`, `path` | `tenant_scope`, `owner` | Used as multiplier/guard in formulas. |
| Provenance trust | `name`, `source`, `type:"categorical"`, `path` | `key_id`, `chain_of_custody` | Gate low-trust inputs; must carry attestation hash. |
| Custom overrides | `name`, `source`, `type`, `path` | `override_reason`, `reviewer`, `expires_at` | Logged and expiring; surfaced in `signal_contributions`. |
## Normalization Rules
- Validate against profile `signals.type` and known transforms; reject unknown fields.
- Clamp numeric inputs to 01; record original value in provenance for audit.
- TTL/decay: apply per-factor defaults (pending payload fixtures); drop expired signals deterministically.
- Precedence: signed → unsigned; runtime → static; newer → older; when tied, lowest hash order.
Interim notes: follow legacy profile guidance — preserve provenance, never mutate source evidence, and keep ordering stable so explainability hashes are repeatable across UI/CLI/exports.
## Determinism & Ordering
- Sort factors by `factor_type` then `source` then `timestamp_utc`; deterministic hashing for fixtures.
- Record SHA256 for sample payloads in `docs/modules/risk-engine/samples/factors/SHA256SUMS` once provided.
## Open Items
- Sample payloads per factor for fixtures + hashes.
- TTL/decay parameters from Risk Engine Guild.
- Provenance attestation examples (signed runtime traces, KEV ingestion evidence).
## References
- `docs/modules/risk-engine/guides/overview.md`
- `docs/modules/risk-engine/guides/profiles.md`
- `docs/modules/risk-engine/guides/formulas.md`
- `docs/modules/risk-engine/guides/api.md`

View File

@@ -1,62 +0,0 @@
# Risk Formulas
> Based on `CONTRACT-RISK-SCORING-002` (2025-12-05). Keep math examples deterministic with fixed fixtures.
## Purpose
- Describe how normalized factors combine into a 0100 risk score with severity bands.
- Capture gating, weighting, normalization, and override rules.
## Scope & Audience
- Audience: risk engineers, policy authors, auditors.
- In scope: weighting strategies, aggregation functions, severity thresholds, gating rules, tie-breakers.
- Out of scope: full API payloads (see `api.md`), factor definitions (see `factors.md`).
## Formula Building Blocks
- Weighted sum with per-factor caps; enforce max contribution per family (exploitability, reachability, runtime).
- Base rule (contract): `raw_score = Σ(signal_value × weight)`, `normalized_score = clamp(raw_score, 0.0, 1.0)`.
- VEX gate: if `signals.HasVexDenial`, return `0.0` immediately (mitigated finding).
- CVSS + KEV provider: `score = clamp01((cvss/10) + (kev ? 0.2 : 0))`.
- Guard rails: hard gates when `(exploit_likelihood >= T1) AND (reachability >= T2)` or when provenance trust below minimum.
- Decay/time weighting: exponential decay for stale runtime/KEV signals; fresh VEX `not_affected` may down-weight exploit scores.
- Tenant/asset overrides: additive/override blocks with expiry; always logged in explainability output.
- Safety: divide-by-zero and null handling must be deterministic and reflected in explain trace.
## Severity Mapping
- Contract levels: `critical`, `high`, `medium`, `low`, `informational` (priority 15).
- Map `normalized_score` to bands per profile policy; include band rationale in explainability payload.
## Determinism
- Stable ordering of factors before aggregation.
- Use fixed precision (e.g., 4 decimals) before severity mapping; round not truncate.
- Hash fixtures and record SHA256 for every example payload in `docs/modules/risk-engine/samples/formulas/SHA256SUMS`.
Interim notes: mirror legacy rule — simulation and production must share the exact evaluation codepath; no per-environment divergences. Severity buckets must be deterministic and governed by Authority scopes.
## Example (contract-aligned)
```json
{
"finding_id": "f-123",
"profile_id": "default-profile",
"profile_version": "1.0.0",
"raw_score": 0.75,
"normalized_score": 0.85,
"severity": "high",
"signal_values": { "cvss": 7.5, "kev": true, "reachability": 0.9 },
"signal_contributions": { "cvss": 0.4, "kev": 0.3, "reachability": 0.3 },
"override_applied": "kev-boost",
"override_reason": "Known Exploited Vulnerability",
"scored_at": "2025-12-05T00:00:02Z"
}
```
- CLI/Console screenshots pending telemetry assets (keep deterministic fixture IDs).
## Open Items
- Fixtures for jobs/results and explainability traces.
- Final per-profile severity thresholds (document once agreed).
- UI traces for console/CLI explainability views.
## References
- `docs/modules/risk-engine/guides/overview.md`
- `docs/modules/risk-engine/guides/profiles.md`
- `docs/modules/risk-engine/guides/factors.md`
- `docs/modules/risk-engine/guides/api.md`

View File

@@ -1,50 +0,0 @@
# Risk Overview
> Source of truth: `CONTRACT-RISK-SCORING-002` (published 2025-12-05). Keep fixtures deterministic (UTC timestamps, stable ordering, sealed sample payloads) and avoid external assets.
## Purpose
- Explain the risk model at a glance: factors, formulas, scoring semantics (0100), and severity bands.
- Show how risk flows through StellaOps services (ingest → evaluate → explain → export) and how provenance is preserved.
## Scope & Audience
- Audience: policy authors, risk engineers, auditors, and SREs consuming risk outputs.
- In scope: concepts, glossary, lifecycle, artifacts, cross-module data flow diagrams (add after schema approval).
- Out of scope: detailed factor math (goes to `formulas.md`), API specifics (goes to `api.md`).
## Core Concepts
- **Signal → evidence → factor:** raw events (scanner, VEX, runtime) become evidence once validated; evidence is normalized into factors listed under profile `signals[]`.
- **Profile vs. formula:** a profile bundles factor weights, thresholds, overrides, and severity mapping; formulas describe how weighted signals aggregate and when gates short-circuit.
- **Provenance:** every input keeps its attestation/signature and source hash; explainability echoes `profile_hash`, factor hashes, and job correlation IDs.
- **Explainability payloads:** UI/CLI show per-factor contributions (`signal_contributions`), source hashes, and rule gates; exports reuse the same envelope.
- **Determinism:** stable ordering (factor type → source → timestamp), UTC ISO-8601 timestamps, fixed precision math, sealed fixtures.
Profiles use normalized factors (exploit likelihood, KEV flag, reachability, runtime evidence, fix availability, asset criticality, provenance trust) to produce 01 scores mapped to severity buckets. Simulation and production share the exact code path.
## Lifecycle
1. **Job submit:** POST `/api/v1/risk/jobs` with `tenant_id`, `context_id`, `profile_id`, finding list; request is signed and queued.
2. **Evidence ingestion:** scanner surface + reachability graphs, Zastava runtime signals, VEX/KEV feeds, mirror bundles (offline).
3. **Normalization:** clamp units to 01, apply TTL/decay, dedupe by provenance hash, map to canonical factor catalog.
4. **Profile evaluation:** apply weighted sum and overrides; respect gates (e.g., KEV + reachability) and Authority-imposed rules.
5. **Severity assignment:** map `normalized_score` to severity levels (critical/high/medium/low/informational) with rationale.
6. **Explainability & observability:** emit per-factor contribution table, provenance pointers, evaluation latency metrics; surface via `/risk/jobs/{id}` and export bundles.
7. **Export/archival:** package explainability + profile version/hash for Findings Ledger/Export Center; mirror-friendly.
## Artifacts & Schemas
- Contract: `CONTRACT-RISK-SCORING-002` (2025-12-05) — risk scoring jobs, results, and profile model.
- Profile schema fields: `id`, `version`, `description`, optional `extends`, `signals[] {name, source, type, path, transform, unit}`, `weights{}`, `overrides{severity[], decisions[]}`, `metadata`, `provenance`.
- Job/result fields: `job_id`, `profile_hash`, `normalized_score`, `severity`, `signal_values`, `signal_contributions`, optional overrides and timestamps.
- Explainability envelope: reuse `signal_contributions` + `profile_hash`; store fixtures under `docs/modules/risk-engine/samples/explain/`.
## Determinism & Offline Posture
- Use frozen fixture sets with SHA256 tables; keep manifests in `docs/modules/risk-engine/samples/*/SHA256SUMS`.
- Regenerate examples via documented scripts only; no live network calls.
- Simulation, API, UI, and export consumers must share the same deterministic ordering and precision.
## Open Items
- Need real payload fixtures (jobs + explainability traces) and UI telemetry captures; placeholders remain in samples folders.
## References (to link once available)
- `docs/modules/risk-engine/guides/profiles.md`
- `docs/modules/risk-engine/guides/factors.md`
- `docs/modules/risk-engine/guides/formulas.md`
- `docs/modules/risk-engine/guides/api.md`

View File

@@ -1,85 +0,0 @@
# Risk Profiles
> Contract source: `CONTRACT-RISK-SCORING-002` (published 2025-12-05). This file supersedes `docs/modules/risk-engine/guides/risk-profiles.md` once fixtures are added.
## Purpose
- Define how profiles group factors, weights, thresholds, and severity bands.
- Describe authoring, simulation, promotion, rollback, and provenance for profiles.
## Scope & Audience
- Audience: policy authors, risk engineers, platform SREs.
- Coverage: profile schema, lifecycle, governance, promotion paths, rollback, and observability hooks.
## Schema (from CONTRACT-RISK-SCORING-002)
- Required: `id`, `version`, `description`, `signals[]`, `weights`, `metadata`.
- `signals[]` fields: `name`, `source`, `type` (`numeric|boolean|categorical`), `path`, optional `transform`, optional `unit`.
- Overrides: `overrides.severity[] { when, set }`, `overrides.decisions[] { when, action, reason }`.
- Optional: `extends`, rollout flags, tenant overrides, `valid_from`/`valid_until`.
- Storage rules: immutable once promoted; each change creates a new version with DSSE envelope and SHA256 manifest entry (`docs/modules/risk-engine/samples/profiles/SHA256SUMS`).
### Example Profile (contract snippet)
```json
{
"id": "default-profile",
"version": "1.0.0",
"description": "Default risk profile for vulnerability prioritization",
"extends": "base-profile",
"signals": [
{ "name": "cvss", "source": "nvd", "type": "numeric", "path": "/cvss/base_score", "transform": "normalize_10", "unit": "score" },
{ "name": "kev", "source": "cisa", "type": "boolean", "path": "/kev/in_catalog" },
{ "name": "reachability", "source": "scanner", "type": "numeric", "path": "/reachability/score" }
],
"weights": { "cvss": 0.4, "kev": 0.3, "reachability": 0.3 },
"overrides": {
"severity": [{ "when": { "kev": true }, "set": "critical" }],
"decisions": [{ "when": { "kev": true, "reachability": { "$gt": 0.8 } }, "action": "deny", "reason": "KEV with high reachability" }]
},
"metadata": {}
}
```
### Severity Levels
| Level | Value | Priority |
| --- | --- | --- |
| Critical | `critical` | 1 |
| High | `high` | 2 |
| Medium | `medium` | 3 |
| Low | `low` | 4 |
| Informational | `informational` | 5 |
## Lifecycle (outline)
1. Authoring in Policy Studio (draft state)
2. Simulation against fixtures (deterministic inputs)
3. Review/approval workflow
4. Promotion to environments (dev → staging → prod)
5. Rollback hooks and audit trail
## Governance & Determinism
- Profiles stored with DSSE/signatures; fixtures recorded in `docs/modules/risk-engine/samples/profiles/SHA256SUMS`.
- Simulation and production share the same evaluation codepath; feature flags must be documented in `metadata.flags`.
- Offline posture: include profiles, fixtures, and explainability bundles inside mirror packages with manifest hashes.
## Explainability & Observability
- Per-factor contribution outputs (JSON) with stable ordering (factor type → source).
- Metrics: evaluation latency (p50/p95), cache hit ratio, factor coverage %, profile hit rate, failed provenance validations.
- Dashboards/alerts: to be filled when telemetry payloads arrive; reserve panels for gating violations and override usage.
## Open Items
- Add signed fixtures (profiles + hashes) under `docs/modules/risk-engine/samples/profiles/` once payloads arrive.
- Capture feature-flag list for registry alignment.
- Telemetry field list for dashboards/alerts.
- Finalize migration note when legacy `docs/modules/risk-engine/guides/risk-profiles.md` is archived.
## References
- `docs/modules/risk-engine/guides/overview.md`
- `docs/modules/risk-engine/guides/factors.md`
- `docs/modules/risk-engine/guides/formulas.md`
- `docs/modules/risk-engine/guides/explainability.md`
- `docs/modules/risk-engine/guides/api.md`
- Existing context: `docs/modules/risk-engine/guides/risk-profiles.md` (to reconcile once schema lands)
## Interim Notes (carried from legacy `docs/modules/risk-engine/guides/risk-profiles.md`)
- Profiles define how evidence (CVSS/EPSS-like exploit likelihood, KEV flags, VEX status, reachability, runtime evidence, fix availability, asset criticality, provenance trust) normalizes into a 0100 score with severity buckets.
- Workflow highlights: author in Policy Studio → simulate with fixtures → activate in Policy Engine → explain outputs in CLI/Console → export for auditors via Export Center.
- Governance: draft/review/approval with DSSE/signatures; rollback hooks and promotion gates enforced by Authority scopes; determinism required (same codepath for simulation and production).
- Observability: record scoring latency, factor distribution, and profile usage; offline posture via mirror bundles with fixtures and hash manifests.

View File

@@ -1,57 +0,0 @@
# Risk Scoring Profiles
> Work of this type or tasks of this type on this component must also be applied everywhere else it should be applied.
## Overview
Risk Scoring Profiles define customizable formulas that convert raw evidence (CVSS, EPSS-like exploit likelihood, KEV exploited lists, VEX status, reachability, runtime evidence, fix availability, asset criticality, provenance trust) into normalized risk scores (0100) with severity buckets. Profiles are authored in Policy Studio, simulated, versioned, and executed by the scoring engine with full explainability.
- **Primary components:** Policy Engine, Findings Ledger, Conseiller, Excitor, Console, Policy Studio, CLI, Export Center, Authority & Tenancy, Observability.
- **Surfaces:** policy documents, scoring engine, factor providers, explainability artefacts, APIs, CLI, UI.
Aggregation-Only Contract remains in force: Conseiller and Excitor never merge or mutate source records. Risk scoring consumes linked evidence and preserves provenance for explainability.
## Core workflow
1. **Profile authoring:** Policy Studio exposes declarative DSL to define factors, weights, thresholds, and severity buckets.
2. **Simulation:** operators preview profiles against historical findings/SBOMs, compare with existing policies, and inspect factor breakdowns.
3. **Activation:** Policy Engine evaluates profiles on change streams, producing scores and detailed factor contributions per finding and per asset.
4. **Explainability:** CLI/Console display math traces, provenance IDs, and rationale for each factor. Export Center packages reports for auditors.
5. **Versioning:** profiles carry semantic versions, promotion workflows, and rollback hooks; Authority scopes enforce who can publish or edit.
## Factor model
| Factor | Description | Typical signal source |
| --- | --- | --- |
| Exploit likelihood | EPSS/KEV or internal intel | Conseiller enrichment |
| VEX status | not_affected / affected / fixed | Excitor (VEX Lens) |
| Reachability | entrypoint closure, runtime observations | Scanner + Zastava |
| Fix availability | patch released, vendor guidance | Conseiller, Policy Engine |
| Asset criticality | business context, tenant overrides | Policy Studio inputs |
| Provenance trust | signed evidence, attestation status | Attestor, Authority |
Factors feed into a weighted scoring engine with per-factor contribution reporting.
## Governance & guardrails
- Profiles live in Policy Studio with draft/review/approval workflows.
- Policy Engine enforces deterministic evaluation; simulations and production runs share the same scoring code.
- CLI parity enables automated promotion, export/import, and simulation from pipelines.
- Observability records scoring latency, factor distribution, and profile usage.
- Offline support: profiles, factor plugins, and explain bundles ship inside mirror bundles for air-gapped environments.
## Deliverables
- Policy language reference and examples.
- Simulation APIs/CLI with diff output.
- Scoring engine implementation with explain traces and determinism checks.
- Console visualizations (severity heatmaps, contribution waterfalls).
- Export Center reports with risk scoring sections.
- Observability dashboards for profile health and scoring throughput.
## References
- Policy core: `docs/modules/policy/architecture.md`
- Findings ledger: `docs/modules/vuln-explorer/architecture.md`
- VEX consensus: `docs/modules/vex-lens/architecture.md`
- Offline operations: `docs/airgap/airgap-mode.md`

View File

@@ -1,8 +0,0 @@
# Risk Samples Ingest Checklist (use when payloads arrive)
1) Drop payloads into the correct folder (`profiles/`, `factors/`, `explain/`, `api/`).
2) Normalize JSON deterministically (e.g., `jq -S .`) before hashing; keep UTC timestamps.
3) Run `sha256sum * > SHA256SUMS` in the target folder; keep file sorted.
4) Verify hashes: `sha256sum -c SHA256SUMS`.
5) Add a short README snippet in the sprint Execution Log noting files added and hashes updated.
6) Keep fixtures offline-only; no external calls or redactions after hashing.

View File

@@ -1,26 +0,0 @@
# Risk Samples (fixtures layout)
Use this folder for frozen, deterministic fixtures once schemas and payloads arrive.
Structure (proposed):
- `profiles/` — profile JSON (DSSE-wrapped where applicable) + `SHA256SUMS`
- `factors/` — factor input payloads grouped by source (epss/, kev/, reachability/, runtime/), each with `SHA256SUMS`
- `explain/` — explainability outputs paired with inputs; include `SHA256SUMS`
- `api/` — request/response examples for risk endpoints; include `SHA256SUMS`
Rules:
- UTC timestamps; stable ordering of arrays/objects.
- No live calls; fixtures only.
- Record hashes via `sha256sum` and keep manifests alongside samples.
Quick receipt checklist (see `INGEST_CHECKLIST.md` for detail):
1) Normalize JSON with `jq -S .`
2) Update `SHA256SUMS` in the target folder
3) Verify with `sha256sum -c`
4) Log files + hashes in the sprint Execution Log
Manifests created:
- `profiles/SHA256SUMS`
- `factors/SHA256SUMS`
- `explain/SHA256SUMS`
- `api/SHA256SUMS`

View File

@@ -1,3 +0,0 @@
Use the root `INGEST_CHECKLIST.md`.
Place request/response examples here; normalize with `jq -S .`, update `SHA256SUMS`, verify with `sha256sum -c`.
Include required headers; redact secrets; UTC timestamps only.

View File

@@ -1,3 +0,0 @@
fe460af2699ce335199f6e26597bab4530c6f3f476d4b1f93526175597565d10 README.md
00f8dc4e466eb95c06545e6336d7b0866b53ac430335b7fd1b7889da13529b93 error-catalog.json
96926cd81dfb6ff02d62d1fde5d7b2b7b5b3950e50eb651e51b8ae3042ac9506 risk-api-samples.json

View File

@@ -1,13 +0,0 @@
{
"errors": [
{"code": "risk.job.not_found", "message": "Risk job not found", "http_status": 404, "remediation": "Verify job_id"},
{"code": "risk.profile.invalid_signature", "message": "Profile DSSE signature failed", "http_status": 400, "remediation": "Re-sign profile and retry"},
{"code": "risk.job.rate_limited", "message": "Rate limit exceeded", "http_status": 429, "remediation": "Retry after backoff", "retry_after": 5},
{"code": "risk.tenant.scope_denied", "message": "Tenant scope not authorized", "http_status": 403, "remediation": "Provide required scope header"}
],
"headers": {
"etag": "\"risk-api-sample-etag\"",
"x-ratelimit-remaining": 99,
"retry-after": 5
}
}

View File

@@ -1,61 +0,0 @@
{
"submit_job_request": {
"method": "POST",
"path": "/api/v1/risk/jobs",
"headers": {
"Content-Type": "application/json",
"X-Stella-Tenant": "tenant-default"
},
"body": {
"tenant_id": "tenant-default",
"context_id": "ctx-001",
"profile_id": "default-profile",
"findings": [
{
"finding_id": "finding-123",
"component_purl": "pkg:npm/lodash@4.17.20",
"advisory_id": "CVE-2024-1234",
"trigger": "created"
}
],
"priority": "normal",
"requested_at": "2025-12-05T00:00:00Z"
},
"response": {
"status": 202,
"body": {"job_id": "job-001", "status": "queued"}
}
},
"get_job_status": {
"method": "GET",
"path": "/api/v1/risk/jobs/job-001",
"response": {
"status": 200,
"body": {
"job_id": "job-001",
"status": "completed",
"results": [
{
"finding_id": "finding-123",
"profile_id": "default-profile",
"profile_version": "1.0.0",
"raw_score": 0.75,
"normalized_score": 0.85,
"severity": "high",
"signal_values": {"cvss": 7.5, "kev": true, "reachability": 0.9},
"signal_contributions": {"cvss": 0.4, "kev": 0.3, "reachability": 0.3},
"scored_at": "2025-12-05T00:00:02Z"
}
]
}
}
},
"get_explain": {
"method": "GET",
"path": "/api/v1/risk/explain/job-001",
"response": {
"status": 200,
"body_ref": "../explain/explain-trace.json"
}
}
}

View File

@@ -1,3 +0,0 @@
Use the root `INGEST_CHECKLIST.md`.
Store explainability outputs paired with their inputs; normalize with `jq -S .`, update `SHA256SUMS`, verify with `sha256sum -c`.
Maintain ordering and UTC timestamps; no live data.

View File

@@ -1,4 +0,0 @@
fe460af2699ce335199f6e26597bab4530c6f3f476d4b1f93526175597565d10 README.md
abcacb431d35d649a0deae81aecce9996b28304da6342a083f9616af6b1ca6a2 cli-explain.txt
f3f1b41f5261f50f3fc104ebeeb2649cc9866d04f9634228778551e6c3364cb8 console-frame.json
1d2e56eebf0a266f80519f073e1db532c4a4f2d7fa604ea5c05d4e208719cc7c explain-trace.json

View File

@@ -1,15 +0,0 @@
stella risk explain job-001 --tenant tenant-default
==================================================
Finding: finding-123
Profile: default-profile v1.0.0 (hash sha256:profilehash)
Score: 0.85 (HIGH)
Gates: kev_and_reachability
Contributions (ordered)
- cvss 0.40 raw=7.5 source=nvd prov=sha256:cvsshash
- kev 0.30 raw=true source=cisa prov=sha256:kevhash
- reachability 0.30 raw=0.9 source=scanner prov=sha256:reachhash
Overrides: kev-boost (Known Exploited Vulnerability)
Provenance: job sha256:jobhash | fixtures [sha256:cvsshash, sha256:kevhash, sha256:reachhash]
Timestamp: 2025-12-05T00:00:02Z

View File

@@ -1,22 +0,0 @@
{
"frame_id": "console-explain-001",
"captured_at": "2025-12-05T00:05:00Z",
"ui_version": "1.0.0",
"tenant_id": "tenant-default",
"finding_id": "finding-123",
"profile_id": "default-profile",
"profile_hash": "sha256:profilehash",
"score": 0.85,
"severity": "high",
"gates": ["kev_and_reachability"],
"top_contributors": [
{"factor": "cvss", "contribution": 0.4, "raw": 7.5, "source": "nvd", "provenance": "sha256:cvsshash"},
{"factor": "kev", "contribution": 0.3, "raw": true, "source": "cisa", "provenance": "sha256:kevhash"},
{"factor": "reachability", "contribution": 0.3, "raw": 0.9, "source": "scanner", "provenance": "sha256:reachhash"}
],
"charts": {
"donut": {"critical": 0, "high": 1, "medium": 0, "low": 0, "informational": 0},
"stacked": [0.4, 0.3, 0.3]
},
"provenance": {"job_hash": "sha256:jobhash", "fixtures": ["sha256:cvsshash", "sha256:kevhash", "sha256:reachhash"]}
}

View File

@@ -1,34 +0,0 @@
{
"job_id": "job-001",
"tenant_id": "tenant-default",
"context_id": "ctx-001",
"profile_id": "default-profile",
"profile_version": "1.0.0",
"profile_hash": "sha256:profilehash",
"finding_id": "finding-123",
"raw_score": 0.75,
"normalized_score": 0.85,
"severity": "high",
"signal_values": {
"cvss": 7.5,
"kev": true,
"reachability": 0.9
},
"signal_contributions": {
"cvss": 0.4,
"kev": 0.3,
"reachability": 0.3
},
"override_applied": "kev-boost",
"override_reason": "Known Exploited Vulnerability",
"gates_triggered": ["kev_and_reachability"],
"scored_at": "2025-12-05T00:00:02Z",
"provenance": {
"job_hash": "sha256:jobhash",
"fixtures": [
"sha256:cvsshash",
"sha256:kevhash",
"sha256:reachhash"
]
}
}

View File

@@ -1,3 +0,0 @@
Use the root `INGEST_CHECKLIST.md`.
Drop factor payloads by source (epss/, kev/, reachability/, runtime/), normalize with `jq -S .`, update `SHA256SUMS`, verify with `sha256sum -c`.
Keep UTC timestamps and no live data.

View File

@@ -1,2 +0,0 @@
fe460af2699ce335199f6e26597bab4530c6f3f476d4b1f93526175597565d10 README.md
13cf45be5a287a38d000aff4db266616e765fc1acdc1df9f37b2e03eb729d1d2 factors-normalized.json

View File

@@ -1,44 +0,0 @@
{
"profile_id": "default-profile",
"context_id": "ctx-001",
"factors": [
{
"name": "cvss",
"source": "nvd",
"type": "numeric",
"path": "/cvss/base_score",
"raw_value": 7.5,
"normalized_value": 0.75,
"weight": 0.4,
"contribution": 0.4,
"timestamp_utc": "2025-12-05T00:00:00Z",
"provenance": "sha256:cvsshash"
},
{
"name": "kev",
"source": "cisa",
"type": "boolean",
"path": "/kev/in_catalog",
"raw_value": true,
"normalized_value": 1.0,
"weight": 0.3,
"contribution": 0.3,
"timestamp_utc": "2025-12-05T00:00:00Z",
"provenance": "sha256:kevhash"
},
{
"name": "reachability",
"source": "scanner",
"type": "numeric",
"path": "/reachability/score",
"raw_value": 0.9,
"normalized_value": 0.9,
"weight": 0.3,
"contribution": 0.3,
"timestamp_utc": "2025-12-05T00:00:01Z",
"provenance": "sha256:reachhash"
}
],
"ordering": "factor_type->source->timestamp_utc",
"precision": 4
}

View File

@@ -1,8 +0,0 @@
| Date (UTC) | Folder | Files added | SHA256SUMS updated | Notes |
| --- | --- | --- | --- | --- |
| 2025-__-__ | profiles/ | | yes/no | source + checklist step refs |
| 2025-__-__ | factors/ | | yes/no | source + checklist step refs |
| 2025-__-__ | explain/ | | yes/no | source + checklist step refs |
| 2025-__-__ | api/ | | yes/no | source + checklist step refs |
Instructions: copy a row per drop, fill actual date, list filenames, mark whether `SHA256SUMS` was updated, and note evidence source. Keep this file sorted by date for determinism.

View File

@@ -1,3 +0,0 @@
Use the root `INGEST_CHECKLIST.md`.
Place profile JSON/DSSE here, normalize with `jq -S .`, update `SHA256SUMS`, and verify with `sha256sum -c`.
UTC timestamps only; no live data.

View File

@@ -1,2 +0,0 @@
fe460af2699ce335199f6e26597bab4530c6f3f476d4b1f93526175597565d10 README.md
c8242d4051232152d024dd37324b346dcf019a5e46b7b82fae8349ad802affab default-profile.json

View File

@@ -1,18 +0,0 @@
{
"id": "default-profile",
"version": "1.0.0",
"description": "Default risk profile for vulnerability prioritization",
"extends": "base-profile",
"signals": [
{ "name": "cvss", "source": "nvd", "type": "numeric", "path": "/cvss/base_score", "transform": "normalize_10", "unit": "score" },
{ "name": "kev", "source": "cisa", "type": "boolean", "path": "/kev/in_catalog" },
{ "name": "reachability", "source": "scanner", "type": "numeric", "path": "/reachability/score", "unit": "score" }
],
"weights": { "cvss": 0.4, "kev": 0.3, "reachability": 0.3 },
"overrides": {
"severity": [ { "when": { "kev": true }, "set": "critical" } ],
"decisions": [ { "when": { "kev": true, "reachability": { "$gt": 0.8 } }, "action": "deny", "reason": "KEV with high reachability" } ]
},
"metadata": { "author": "docs-guild", "created_at": "2025-12-05T00:00:00Z" },
"provenance": { "hash": "sha256:placeholder", "signed": false }
}

View File

@@ -72,8 +72,7 @@ StellaOps.Router.slnx
│ ├── StellaOps.Router.Transport.RabbitMQ/
│ ├── StellaOps.Microservice/
│ └── StellaOps.Microservice.SourceGen/
├── src/Gateway/
│ └── StellaOps.Gateway.WebService/
├── src/Router/StellaOps.Gateway.WebService/ (moved from src/Gateway/ per Sprint 200)
└── tests/
└── (test projects)
```
@@ -181,5 +180,5 @@ dotnet build StellaOps.Router.slnx
dotnet test StellaOps.Router.slnx
# Run gateway
dotnet run --project src/Gateway/StellaOps.Gateway.WebService
dotnet run --project src/Router/StellaOps.Gateway.WebService
```

View File

@@ -7,7 +7,7 @@ Service impact ledger: `docs/technical/architecture/multi-tenant-service-impact-
Flow sequences: `docs/technical/architecture/multi-tenant-flow-sequences.md`
Rollout policy: `docs/operations/multi-tenant-rollout-and-compatibility.md`
> **Dual-location clarification (updated 2026-02-22).** The Router (`src/Router/`) hosts the evolved `StellaOps.Gateway.WebService` with advanced features not present in `src/Gateway/`: configurable route tables via `GatewayRouteCatalog`, reverse proxy support, SPA fallback hosting, WebSocket routing, Valkey messaging transport integration, and `StellaOpsRouteResolver` for front-door dispatching. This is the current canonical deployment for HTTP ingress. A simpler version exists at `src/Gateway/` for basic ingress scenarios. See also [Gateway Architecture](../gateway/architecture.md).
> **Location clarification (updated 2026-03-04).** The Router (`src/Router/`) hosts `StellaOps.Gateway.WebService` with configurable route tables via `GatewayRouteCatalog`, reverse proxy support, SPA fallback hosting, WebSocket routing, Valkey messaging transport integration, and `StellaOpsRouteResolver` for front-door dispatching. This is the canonical deployment for HTTP ingress. The standalone `src/Gateway/` was deleted in Sprint 200.
## System Architecture
@@ -296,7 +296,7 @@ Request ─►│ ForwardedHeaders │
- Per-request tenant override is disabled by default and only works when explicitly enabled with `Gateway:Auth:EnableTenantOverride=true` and the requested tenant exists in `stellaops:allowed_tenants`.
- Authorization/DPoP passthrough is fail-closed:
- route must be configured with `PreserveAuthHeaders=true`, and
- route prefix must also be in the approved passthrough allow-list (`/connect`, `/console`, `/api/admin`).
- route prefix must also be in the approved passthrough allow-list (`/connect`, `/console`, `/authority`, `/doctor`, `/api`).
- Tenant override attempts are logged with deterministic fields including route, actor, requested tenant, and resolved tenant.
### Connection State

View File

@@ -513,7 +513,7 @@ For each route:
| Scanner | StellaOps.Scanner.WebService | High | High | Streaming scans |
| Attestor | StellaOps.Attestor.WebService | Medium | Medium | Attestation gen |
| Excititor | StellaOps.Excititor.WebService | Medium | Low | VEX processing |
| Orchestrator | StellaOps.Orchestrator.WebService | Medium | Medium | Job coordination |
| Orchestrator | StellaOps.JobEngine.WebService | Medium | Medium | Job coordination |
| Scheduler | StellaOps.Scheduler.WebService | Low | Low | Job scheduling |
| Notify | StellaOps.Notify.WebService | Low | Low | Notifications |
| Notifier | StellaOps.Notifier.WebService | Low | Low | Alert dispatch |

View File

@@ -1,7 +1,7 @@
# Router TimelineIndexer Microservice Pilot
## Scope
- Pilot service: `TimelineIndexer` (`src/TimelineIndexer/StellaOps.TimelineIndexer/StellaOps.TimelineIndexer.WebService`).
- Pilot service: `TimelineIndexer` (`src/Timeline/StellaOps.TimelineIndexer.WebService`).
- Transport: `TransportType.Messaging` backed by Valkey.
- Gateway entry under pilot: `/api/v1/timeline*`.

View File

@@ -178,7 +178,7 @@ All WebServices have been updated with Router integration:
| Scanner.WebService | `src/Scanner/StellaOps.Scanner.WebService` | ✅ Complete |
| Concelier.WebService | `src/Concelier/StellaOps.Concelier.WebService` | ✅ Complete |
| Excititor.WebService | `src/Excititor/StellaOps.Excititor.WebService` | ✅ Complete |
| Gateway.WebService | `src/Gateway/StellaOps.Gateway.WebService` | ✅ Complete |
| Gateway.WebService | `src/Router/StellaOps.Gateway.WebService` (moved from `src/Gateway/`, Sprint 200) | ✅ Complete |
| VexHub.WebService | `src/VexHub/StellaOps.VexHub.WebService` | ✅ Complete |
| Attestor.WebService | `src/Attestor/StellaOps.Attestor/StellaOps.Attestor.WebService` | ✅ Complete |
| EvidenceLocker.WebService | `src/EvidenceLocker/StellaOps.EvidenceLocker/StellaOps.EvidenceLocker.WebService` | ✅ Complete |
@@ -188,11 +188,11 @@ All WebServices have been updated with Router integration:
| Notifier.WebService | `src/Notifier/StellaOps.Notifier/StellaOps.Notifier.WebService` | ✅ Complete |
| Notify.WebService | `src/Notify/StellaOps.Notify.WebService` | ✅ Complete |
| PacksRegistry.WebService | `src/PacksRegistry/StellaOps.PacksRegistry/StellaOps.PacksRegistry.WebService` | ✅ Complete |
| RiskEngine.WebService | `src/RiskEngine/StellaOps.RiskEngine/StellaOps.RiskEngine.WebService` | ✅ Complete |
| RiskEngine.WebService | `src/Findings/StellaOps.RiskEngine.WebService` | ✅ Complete |
| Signer.WebService | `src/Signer/StellaOps.Signer/StellaOps.Signer.WebService` | ✅ Complete |
| TaskRunner.WebService | `src/TaskRunner/StellaOps.TaskRunner/StellaOps.TaskRunner.WebService` | ✅ Complete |
| TimelineIndexer.WebService | `src/TimelineIndexer/StellaOps.TimelineIndexer/StellaOps.TimelineIndexer.WebService` | ✅ Complete |
| Orchestrator.WebService | `src/Orchestrator/StellaOps.Orchestrator/StellaOps.Orchestrator.WebService` | ✅ Complete |
| TimelineIndexer.WebService | `src/Timeline/StellaOps.TimelineIndexer.WebService` | ✅ Complete |
| Orchestrator.WebService | `src/JobEngine/StellaOps.JobEngine/StellaOps.JobEngine.WebService` | ✅ Complete |
| Scheduler.WebService | `src/Scheduler/StellaOps.Scheduler.WebService` | ✅ Complete |
| ExportCenter.WebService | `src/ExportCenter/StellaOps.ExportCenter/StellaOps.ExportCenter.WebService` | ✅ Complete |

View File

@@ -36,7 +36,7 @@ Legend:
| notifier.stella-ops.local | notifier-web | /api/v1/notifier, /notifier | D | Developer + Test Automation (Wave D) | Migrate API prefix first, then root compatibility path. | Route type revert + `NOTIFIER_ROUTER_ENABLED=false` (RMW-03). |
| notify.stella-ops.local | notify-web | /api/v1/notify, /notify | D | Developer + Test Automation (Wave D) | Migrate API prefix first, then root compatibility path. | Route type revert + `NOTIFY_ROUTER_ENABLED=false` (RMW-03). |
| opsmemory.stella-ops.local | opsmemory-web | /api/v1/opsmemory, /opsmemory | A | Developer + Test Automation (Wave A) | Migrate API prefix first, then root compatibility path. | Route type revert + `OPSMEMORY_ROUTER_ENABLED=false` (RMW-03). |
| orchestrator.stella-ops.local | orchestrator | /api/approvals, /api/orchestrator, /api/release-orchestrator, /api/releases, /api/v1/orchestrator, /api/v1/release-orchestrator, /api/v1/workflows, /orchestrator, /v1/runs | C | Developer + Test Automation (Wave C) | Migrate all API/v1 and v1 routes first; keep root compatibility path until control-plane acceptance. | Route type revert + `ORCHESTRATOR_ROUTER_ENABLED=false` (RMW-03). |
| jobengine.stella-ops.local | orchestrator | /api/approvals, /api/jobengine, /api/release-orchestrator, /api/releases, /api/v1/jobengine, /api/v1/release-orchestrator, /api/v1/workflows, /orchestrator, /v1/runs | C | Developer + Test Automation (Wave C) | Migrate all API/v1 and v1 routes first; keep root compatibility path until control-plane acceptance. | Route type revert + `ORCHESTRATOR_ROUTER_ENABLED=false` (RMW-03). |
| packsregistry.stella-ops.local | packsregistry-web | /packsregistry | A | Developer + Test Automation (Wave A) | Add API-form endpoint mapping if required, then migrate root compatibility route. | Route type revert + `PACKSREGISTRY_ROUTER_ENABLED=false` (RMW-03). |
| platform.stella-ops.local | platform | /api, /api/admin, /api/analytics, /api/v1/authority/quotas, /api/v1/gateway/rate-limits, /api/v1/platform, /envsettings.json, /platform | C | Developer + Test Automation (Wave C) | Migrate API prefixes to Microservice; keep `/platform` and `/envsettings.json` reverse proxy for static/bootstrap behavior. | Route type revert + `PLATFORM_ROUTER_ENABLED=false` (RMW-03). |
| policy-engine.stella-ops.local | policy-engine | /api/risk, /api/risk-budget, /api/v1/determinization, /policyEngine | C | Developer + Test Automation (Wave C) | Migrate API prefixes first; keep root compatibility path until control-plane verification completes. | Route type revert + `POLICY_ENGINE_ROUTER_ENABLED=false` (RMW-03). |

View File

@@ -57,9 +57,9 @@ Operational rules:
- `GET /internal/sbom/events` — internal diagnostics endpoint returning the in-memory event outbox for validation.
- `POST /internal/sbom/events/backfill` — replays existing projections into the event stream; deterministic ordering, clock abstraction for tests.
- `GET /internal/sbom/asset-events` — diagnostics endpoint returning emitted `sbom.asset.updated` envelopes for validation and air-gap parity checks.
- `GET/POST /internal/orchestrator/sources` — list/register orchestrator ingest/index sources (deterministic seeds; idempotent on artifactDigest+sourceType).
- `GET/POST /internal/orchestrator/control` — manage pause/throttle/backpressure signals per tenant; metrics emitted for control updates.
- `GET/POST /internal/orchestrator/watermarks` — fetch/set backfill watermarks for reconciliation and deterministic replays.
- `GET/POST /internal/jobengine/sources` — list/register orchestrator ingest/index sources (deterministic seeds; idempotent on artifactDigest+sourceType).
- `GET/POST /internal/jobengine/control` — manage pause/throttle/backpressure signals per tenant; metrics emitted for control updates.
- `GET/POST /internal/jobengine/watermarks` — fetch/set backfill watermarks for reconciliation and deterministic replays.
- `GET /internal/sbom/resolver-feed` list resolver candidates (artifact, purl, version, paths, scope, runtime_flag, nearest_safe_version).
- `POST /internal/sbom/resolver-feed/backfill` clear and repopulate resolver feed from current projections.
- `GET /internal/sbom/resolver-feed/export` NDJSON export of resolver candidates for air-gap delivery.

Some files were not shown because too many files have changed in this diff Show More