save progress

This commit is contained in:
StellaOps Bot
2025-12-26 22:03:32 +02:00
parent 9a4cd2e0f7
commit e6c47c8f50
3634 changed files with 253222 additions and 56632 deletions

123
docs/modules/README.md Normal file
View File

@@ -0,0 +1,123 @@
# StellaOps Module Documentation Index
This directory contains architecture documentation for all StellaOps modules.
## Module Categories
### Core Platform
| 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) |
| [Platform](./platform/) | Cross-cutting | Platform architecture overview |
### Data Ingestion
| Module | Path | Description |
|--------|------|-------------|
| [Concelier](./concelier/) | `src/Concelier/` | Vulnerability advisory ingestion and merge engine |
| [Excititor](./excititor/) | `src/Excititor/` | VEX document ingestion and export |
| [VexLens](./vex-lens/) | `src/VexLens/` | VEX consensus computation across issuers |
| [VexHub](./vexhub/) | `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](./binaryindex/) | `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 |
| [ReachGraph](./reachgraph/) | `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 |
| [SbomService](./sbomservice/) | `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](./provcache/) | Library | Provenance cache utilities |
### Policy & Risk
| Module | Path | Description |
|--------|------|-------------|
| [Policy](./policy/) | `src/Policy/` | Policy engine with K4 lattice logic |
| [RiskEngine](./riskengine/) | `src/RiskEngine/` | Risk scoring runtime |
| [VulnExplorer](./vuln-explorer/) | `src/VulnExplorer/` | Vulnerability exploration and triage |
| [Unknowns](./unknowns/) | `src/Unknowns/` | Unknown component tracking registry |
### Operations
| Module | Path | Description |
|--------|------|-------------|
| [Scheduler](./scheduler/) | `src/Scheduler/` | Job scheduling and queue management |
| [Orchestrator](./orchestrator/) | `src/Orchestrator/` | Workflow orchestration and task coordination |
| [TaskRunner](./taskrunner/) | `src/TaskRunner/` | Task pack execution engine |
| [Notify](./notify/) | `src/Notify/` | Notification toolkit (Email, Slack, Teams, Webhooks) |
| [Notifier](./notifier/) | `src/Notifier/` | Notifications Studio host |
| [PacksRegistry](./packsregistry/) | `src/PacksRegistry/` | Task packs registry |
| [TimelineIndexer](./timelineindexer/) | `src/TimelineIndexer/` | Timeline event indexing |
| [Replay](./replay/) | `src/Replay/` | Deterministic replay engine |
### Integration
| Module | Path | Description |
|--------|------|-------------|
| [CLI](./cli/) | `src/Cli/` | Command-line interface (Native AOT) |
| [Zastava](./zastava/) | `src/Zastava/` | Container registry webhook observer |
| [Web/UI](./ui/) | `src/Web/` | Angular 17 frontend SPA |
| [API](./api/) | `src/Api/` | OpenAPI contracts and governance |
| [Registry](./registry/) | `src/Registry/` | Container registry integration |
### Infrastructure
| Module | Path | Description |
|--------|------|-------------|
| [Cryptography](./cryptography/) | `src/Cryptography/` | Crypto plugins (FIPS, eIDAS, GOST, SM, PQ) |
| [Telemetry](./telemetry/) | `src/Telemetry/` | OpenTelemetry traces, metrics, logging |
| [Graph](./graph/) | `src/Graph/` | Call graph and reachability data structures |
| [Signals](./signals/) | `src/Signals/` | Runtime signal collection and correlation |
| [AirGap](./airgap/) | `src/AirGap/` | Air-gapped deployment support |
| [AOC](./aoc/) | `src/Aoc/` | Append-Only Contract enforcement |
### Testing & Benchmarks
| Module | Path | Description |
|--------|------|-------------|
| [Benchmark](./benchmark/) | Scanner library | Competitive benchmarking (accuracy comparison) |
| [Bench](./bench/) | `src/Bench/` | Performance benchmarks |
### Cross-Cutting Concepts
| Folder | Purpose |
|--------|---------|
| [Evidence](./evidence/) | Unified evidence model specification |
| [Snapshot](./snapshot/) | Knowledge snapshot and replay concepts |
| [Triage](./triage/) | Vulnerability triage workflows |
| [DevOps](./devops/) | DevOps and CI/CD infrastructure |
| [CI](./ci/) | CI pipeline documentation |
---
## Documentation Standards
Each module folder should contain:
| File | Purpose |
|------|---------|
| `README.md` | Quick overview, purpose, components |
| `architecture.md` | Detailed architecture specification |
| `AGENTS.md` | (Optional) Claude Code agent guidance |
| `operations/` | (Optional) Operational runbooks |
See the [Documentation Template Standard](../implplan/SPRINT_1228_0001_DOCS_module_documentation_consolidation.md#documentation-template-standard) for the full architecture.md template.

View File

@@ -0,0 +1,348 @@
# component_architecture_airgap.md - **Stella Ops AirGap** (2025Q4)
> Air-gapped deployment controller, importer, and time anchor services.
> **Scope.** Implementation-ready architecture for **AirGap**: the controller, importer, and time anchor subsystems enabling StellaOps operation in disconnected/air-gapped environments with sealed-mode state management.
---
## 0) Mission & boundaries
**Mission.** Enable **fully offline, air-gapped operation** of StellaOps with sealed-mode state management, bundle-based updates, and cryptographic time anchors for staleness detection.
**Boundaries.**
* AirGap **does not** connect to external networks in sealed mode.
* AirGap **does not** generate vulnerability data. It imports pre-packaged bundles.
* Bundle verification is **mandatory**. Unsigned or tampered bundles are rejected.
* Time anchors are **cryptographically verified** using Roughtime or RFC3161.
---
## 1) Solution & project layout
```
src/AirGap/
├─ StellaOps.AirGap.Controller/ # Seal/unseal state machine, status APIs
│ ├─ Services/
│ │ ├─ ISealingController.cs # Sealing state interface
│ │ ├─ SealingController.cs # State machine implementation
│ │ └─ StatusService.cs # Health and status endpoints
│ └─ Models/
│ ├─ SealState.cs # sealed | unsealed | transitioning
│ └─ SealTransition.cs # Transition metadata
├─ StellaOps.AirGap.Importer/ # Bundle verification and import
│ ├─ Services/
│ │ ├─ IBundleVerifier.cs # DSSE/TUF verification
│ │ ├─ BundleVerifier.cs # Verification implementation
│ │ ├─ ICatalogImporter.cs # Catalog update interface
│ │ └─ CatalogImporter.cs # Import orchestration
│ └─ Models/
│ ├─ ImportBundle.cs # Bundle metadata
│ └─ ImportResult.cs # Import outcome
├─ StellaOps.AirGap.Time/ # Time anchor verification
│ ├─ Services/
│ │ ├─ ITimeAnchorService.cs # Time anchor interface
│ │ ├─ RoughtimeAnchor.cs # Roughtime implementation
│ │ └─ Rfc3161Anchor.cs # RFC3161 timestamp
│ └─ Models/
│ ├─ TimeAnchor.cs # Anchor record
│ └─ StalenessResult.cs # Staleness calculation
├─ StellaOps.AirGap.Policy/ # Air-gap specific policy rules
│ ├─ StellaOps.AirGap.Policy/
│ ├─ StellaOps.AirGap.Policy.Analyzers/
│ └─ StellaOps.AirGap.Policy.Tests/
├─ __Libraries/
│ ├─ StellaOps.AirGap.Bundle/ # Bundle format and parsing
│ └─ StellaOps.AirGap.Persistence/ # State persistence
├─ __Tests/ # Test projects
├─ scripts/ # Operational scripts
└─ AGENTS.md # Guild charter
```
---
## 2) External dependencies
* **PostgreSQL** - State persistence, import history
* **Authority** - Scope enforcement (`airgap:seal`, `airgap:status:read`)
* **Cryptography** - Bundle signature verification
* **Object storage** - Bundle staging and quarantine
---
## 3) Contracts & data model
### 3.1 Seal State
```csharp
public enum SealState
{
Unsealed, // Normal operation, network allowed
Transitioning, // Sealing or unsealing in progress
Sealed // Air-gapped, no network egress
}
public sealed record SealStatus
{
public required SealState State { get; init; }
public required DateTimeOffset LastTransition { get; init; }
public required string TransitionedBy { get; init; }
public DateTimeOffset? SealedSince { get; init; }
public TimeAnchor? LastTimeAnchor { get; init; }
}
```
### 3.2 Import Bundle
```json
{
"bundleId": "airgap-2025-01-15-abc123",
"bundleType": "advisory-update",
"version": "2025.01.15.001",
"createdAt": "2025-01-15T10:30:00Z",
"contents": [
{
"type": "concelier-snapshot",
"digest": "sha256:abc123...",
"path": "data/concelier-2025-01-15.tar.zst"
},
{
"type": "trivy-db",
"digest": "sha256:def456...",
"path": "data/trivy-db-2025-01-15.tar.gz"
}
],
"signature": {
"keyId": "sha256:sigkey...",
"algorithm": "ecdsa-p256",
"value": "base64..."
},
"timeAnchor": {
"source": "roughtime",
"timestamp": "2025-01-15T10:25:00Z",
"proof": "base64..."
}
}
```
### 3.3 Time Anchor
```csharp
public sealed record TimeAnchor
{
public required string Source { get; init; } // roughtime, rfc3161
public required DateTimeOffset Timestamp { get; init; }
public required byte[] Proof { get; init; } // Cryptographic proof
public required string[] Servers { get; init; } // Servers used
public required bool Verified { get; init; }
}
public sealed record StalenessResult
{
public required TimeSpan Age { get; init; }
public required bool IsStale { get; init; }
public required TimeSpan StalenessThreshold { get; init; }
public string? Warning { get; init; }
}
```
---
## 4) REST API (Controller + Importer)
All under `/api/v1/airgap`. Auth: **OpTok** with airgap scopes.
### Controller APIs
```
GET /status → { state, lastTransition, timeAnchor }
POST /seal → { transitionId, status: "transitioning" }
POST /unseal → { transitionId, status: "transitioning" }
GET /transitions/{id} → { transition details }
```
### Importer APIs
```
POST /bundles/upload multipart → { bundleId, status: "pending" }
POST /bundles/{id}/verify → { valid: bool, details }
POST /bundles/{id}/import → { importId, status: "importing" }
GET /bundles/{id}/status → { status, progress, errors }
GET /bundles → { bundles: BundleSummary[] }
```
### Time APIs
```
GET /time/anchor → { anchor: TimeAnchor, staleness }
POST /time/anchor { source, proof } → { anchor, verified }
GET /time/staleness → { staleness: StalenessResult }
```
---
## 5) Configuration (YAML)
```yaml
AirGap:
Controller:
InitialState: "unsealed"
TransitionTimeoutSeconds: 300
RequireApproval: true
Importer:
StagingPath: "/data/airgap/staging"
QuarantinePath: "/data/airgap/quarantine"
MaxBundleSizeMb: 10240
TrustRoots:
- "sha256:abc123..." # StellaOps signing key
AllowedBundleTypes:
- "advisory-update"
- "trivy-db"
- "policy-pack"
Time:
StalenessThresholdHours: 168 # 7 days
RoughtimeServers:
- "roughtime.cloudflare.com"
- "roughtime.google.com"
Rfc3161Servers:
- "http://timestamp.digicert.com"
RequireMultipleServers: true
MinServerQuorum: 2
Quarantine:
TtlDays: 30
MaxQuotaMb: 51200
Postgres:
ConnectionString: "Host=postgres;Database=airgap;..."
```
---
## 6) Sealing State Machine
```
┌──────────────┐
│ Unsealed │
└──────┬───────┘
│ seal()
┌──────────────┐
│ Transitioning│
└──────┬───────┘
│ complete
┌──────────────┐
│ Sealed │
└──────┬───────┘
│ unseal()
┌──────────────┐
│ Transitioning│
└──────┬───────┘
│ complete
┌──────────────┐
│ Unsealed │
└──────────────┘
```
### Transition Actions
**Seal transition:**
1. Verify pending work is complete
2. Capture final time anchor
3. Disable network egress
4. Update state to Sealed
**Unseal transition:**
1. Verify network connectivity
2. Refresh time anchor
3. Enable network egress
4. Update state to Unsealed
---
## 7) Bundle Import Flow
```
1. Upload bundle to staging
└─ Validate manifest structure
2. Verify bundle
├─ Check signature against trust roots
├─ Verify content digests
└─ Validate time anchor
3. Import bundle
├─ Extract contents
├─ Update target catalogs (Concelier, Trivy, etc.)
└─ Record import in history
4. Cleanup or quarantine
├─ Success: Remove from staging
└─ Failure: Move to quarantine with TTL
```
---
## 8) Security & compliance
* **Signature verification**: All bundles must be signed
* **Trust roots**: Configurable trust root keys
* **Quarantine**: Failed imports isolated with TTL
* **Audit trail**: All imports and state changes logged
* **Scope enforcement**: Authority scopes for all operations
* **Rollback prevention**: Version monotonicity enforced
---
## 9) Performance targets
* **Seal/unseal transition**: < 30s
* **Bundle verification**: < 10s for 1GB bundle
* **Bundle import**: < 60s for typical advisory update
* **Time anchor verification**: < 5s
---
## 10) Observability
**Metrics:**
* `airgap.state{state=sealed|unsealed|transitioning}`
* `airgap.bundles.imported_total{type,result}`
* `airgap.bundles.quarantined_total{reason}`
* `airgap.time.staleness_seconds`
* `airgap.time.anchor_age_seconds`
**Tracing:** Spans for transitions, imports, verifications.
---
## 11) Testing matrix
* **Seal/unseal tests**: State machine transitions
* **Bundle tests**: Verification and import flows
* **Quarantine tests**: Failed import handling
* **Time tests**: Staleness calculations, anchor verification
* **Integration tests**: Full offline workflow simulation
---
## Related Documentation
* Evidence reconciliation: `./evidence-reconciliation.md`
* Exporter coordination: `./exporter-cli-coordination.md`
* Mirror DSSE plan: `./mirror-dsse-plan.md`
* Offline Kit: `../../24_OFFLINE_KIT.md`
* Time anchor schema: `../../airgap/time-anchor-schema.md`

View File

@@ -0,0 +1,37 @@
# AOC (Append-Only Contracts)
**Status:** Implemented
**Source:** `src/Aoc/`
**Owner:** Platform Team
## Purpose
AOC provides compile-time enforcement of append-only contract rules during data ingestion. Uses Roslyn analyzers to prevent connectors from writing to fields that should only be computed by downstream merge/decisioning pipelines.
## Components
**Analyzers:**
- `StellaOps.Aoc.Analyzers` - Roslyn DiagnosticAnalyzers (AOC0001, AOC0002, AOC0003)
**Libraries:**
- `StellaOps.Aoc` - Core abstractions (IAocGuard)
- `StellaOps.Aoc.AspNetCore` - ASP.NET Core integration
**CLI:**
- `StellaOps.Aoc.Cli` - Manual validation tool
## Key Concepts
**Forbidden Fields** (ingestion-time writes forbidden):
- `severity`, `cvss`, `cvss_vector` - Computed from CVSS + context
- `effective_status`, `effective_range` - VEX consensus outcomes
- `risk_score`, `reachability`, `asset_criticality` - Runtime analysis
**Derived Fields:**
- Any field prefixed with `effective_*` is treated as derived and forbidden
## Related Documentation
- Architecture: `./architecture.md`
- Concelier: `../concelier/` (uses AOC for connectors)
- Excititor: `../excititor/` (uses AOC for VEX ingestion)

View File

@@ -0,0 +1,126 @@
# component_architecture_aoc.md - **Stella Ops AOC** (2025Q4)
> Append-Only Contract enforcement via Roslyn analyzers.
> **Scope.** Library architecture for **AOC** (Append-Only Contracts): Roslyn-based code analyzers that enforce data integrity rules during vulnerability ingestion.
---
## 0) Mission & boundaries
**Mission.** Enforce **append-only contract rules** during data ingestion. Prevent connectors from writing to fields that should only be computed by downstream merge/decisioning pipelines (severity, CVSS, effective status, risk scores).
**Boundaries.**
* AOC provides **compile-time enforcement** via Roslyn analyzers.
* AOC analyzers run on **ingestion code** (Connectors, Ingestion assemblies).
* AOC **does not** run on merge/decisioning code (those are allowed to write derived fields).
---
## 1) Solution & project layout
```
src/Aoc/
├─ __Analyzers/
│ └─ StellaOps.Aoc.Analyzers/ # Roslyn DiagnosticAnalyzers
│ ├─ AocForbiddenFieldAnalyzer.cs # Main analyzer
│ └─ ...
├─ __Libraries/
│ ├─ StellaOps.Aoc/ # Core abstractions (IAocGuard, etc.)
│ └─ StellaOps.Aoc.AspNetCore/ # ASP.NET Core integration
├─ StellaOps.Aoc.Cli/ # CLI for manual validation
└─ __Tests/
├─ StellaOps.Aoc.Analyzers.Tests/
├─ StellaOps.Aoc.AspNetCore.Tests/
└─ StellaOps.Aoc.Tests/
```
---
## 2) Core concept
### 2.1 Forbidden Fields
During ingestion, the following fields are **forbidden** (computed by decisioning pipeline):
| Field | Reason |
|-------|--------|
| `severity` | Computed from CVSS + context |
| `cvss` | Normalized from multiple sources |
| `cvss_vector` | Parsed/validated post-merge |
| `effective_status` | VEX consensus outcome |
| `effective_range` | Merged affected ranges |
| `merged_from` | Provenance tracking |
| `consensus_provider` | VEX provider selection |
| `reachability` | Runtime analysis result |
| `asset_criticality` | Policy engine computation |
| `risk_score` | Final risk calculation |
### 2.2 Derived Fields
Any field prefixed with `effective_` is treated as derived and forbidden in ingestion context.
---
## 3) Diagnostic Rules
| ID | Severity | Description |
|----|----------|-------------|
| `AOC0001` | Error | Forbidden field write detected |
| `AOC0002` | Error | Derived field (effective_*) write detected |
| `AOC0003` | Warning | Unguarded database write without IAocGuard validation |
---
## 4) Usage
### 4.1 Analyzer Reference
Add analyzer reference to connector projects:
```xml
<ItemGroup>
<PackageReference Include="StellaOps.Aoc.Analyzers" PrivateAssets="all" />
</ItemGroup>
```
### 4.2 Guard Usage
Wrap database writes with AOC guard:
```csharp
public class MyConnector
{
private readonly IAocGuard _aocGuard;
public async Task IngestAsync(Advisory advisory, CancellationToken ct)
{
// Guard validates no forbidden fields are written
await _aocGuard.ValidateAsync(advisory, ct);
await _repository.InsertAsync(advisory, ct);
}
}
```
---
## 5) Configuration
AOC analyzers activate based on assembly/namespace patterns:
- `*.Connector.*` assemblies
- `*.Ingestion` assemblies
- `*.Connector` assemblies
- Namespaces containing `.Connector.` or `.Ingestion`
---
## Related Documentation
* Concelier: `../concelier/architecture.md` (uses AOC for connectors)
* Excititor: `../excititor/architecture.md` (uses AOC for VEX ingestion)
* Determinism: `../../telemetry/` (AOC ensures deterministic merge inputs)

View File

@@ -0,0 +1,48 @@
# API (OpenAPI Contracts)
**Status:** Implemented
**Source:** `src/Api/`
**Owner:** API Contracts Guild
## Purpose
API contains OpenAPI 3.1 specifications for all StellaOps services and governance rules for API consistency. This is a specifications repository, not a code module.
## Components
**OpenAPI Specs:**
- `StellaOps.Api.OpenApi/` - Per-service OpenAPI 3.1 YAML specifications
- `authority/openapi.yaml`
- `scanner/openapi.yaml`
- `scheduler/openapi.yaml`
- `policy/openapi.yaml`
- `graph/openapi.yaml`
- `export-center/openapi.yaml`
- `orchestrator/openapi.yaml`
**Shared Components:**
- `_shared/schemas/` - Common schema definitions
- `_shared/parameters/` - Shared parameters (paging, tenant)
- `_shared/responses/` - Standard response definitions
**Aggregate Spec:**
- `stella.yaml` - Composed aggregate specification
**Governance:**
- `StellaOps.Api.Governance/` - API governance rules and standards
## Usage
```bash
# Compose aggregate spec
node src/Api/StellaOps.Api.OpenApi/compose.mjs
# Lint OpenAPI specs
npm run api:lint
```
## Related Documentation
- Developer Portal: `../devportal/` (consumes these specs)
- Gateway: `../gateway/` (routes based on these contracts)
- Platform: `../platform/architecture-overview.md`

View File

@@ -0,0 +1,37 @@
# 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
## 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

@@ -7,6 +7,8 @@ The Benchmark module provides infrastructure for validating and demonstrating St
**Module Path**: `src/Scanner/__Libraries/StellaOps.Scanner.Benchmark/`
**Status**: PLANNED (Sprint 7000.0001.0001)
> **Note:** This module focuses on **competitive benchmarking** (accuracy comparison with other scanners). For **performance benchmarks** of StellaOps modules (LinkNotMerge, Notify, PolicyEngine, Scanner.Analyzers), see `src/Bench/`.
---
## Mission

View File

@@ -0,0 +1,298 @@
# component_architecture_cryptography.md - **Stella Ops Cryptography** (2025Q4)
> Pluggable cryptographic primitives supporting regional standards.
> **Scope.** Library architecture for **Cryptography**: pluggable cryptographic primitives enabling sovereign operation with regional crypto requirements (eIDAS, FIPS, GOST, SM, PQ) while maintaining deterministic signing operations.
---
## 0) Mission & boundaries
**Mission.** Provide **algorithm-agile cryptographic primitives** that support regional compliance requirements while ensuring deterministic, reproducible signing operations across all StellaOps services.
**Boundaries.**
* Cryptography is a **library layer**, not a standalone service.
* Cryptography **does not** manage keys. Key storage is handled by KMS, HSM, or Signer.
* All cryptographic operations are **deterministic** for reproducibility.
* Supports **offline operation** without external crypto services.
---
## 1) Solution & project layout
```
src/Cryptography/
├─ StellaOps.Cryptography/ # Core abstractions and plugin loader
│ ├─ ICryptoHash.cs # Hash computation interface
│ ├─ ISignatureProvider.cs # Signature interface
│ ├─ IKeyProvider.cs # Key loading interface
│ ├─ CryptoProfileLoader.cs # Profile plugin loader
│ └─ DefaultCryptoHash.cs # Default SHA-256/BLAKE3 implementation
├─ StellaOps.Cryptography.Profiles.Ecdsa/ # ECDSA signing profile
│ ├─ EcdsaSignatureProvider.cs
│ └─ Curves/
│ ├─ P256Provider.cs # NIST P-256
│ ├─ P384Provider.cs # NIST P-384
│ └─ Secp256k1Provider.cs # Bitcoin curve
├─ StellaOps.Cryptography.Profiles.EdDsa/ # EdDSA signing profile
│ ├─ EdDsaSignatureProvider.cs
│ └─ Curves/
│ ├─ Ed25519Provider.cs
│ └─ Ed448Provider.cs
├─ plugins/ # External crypto plugins
│ ├─ gost/ # GOST R 34.10-2012 (Russia)
│ ├─ sm/ # SM2/SM3/SM4 (China)
│ ├─ eidas/ # eIDAS qualified signatures (EU)
│ └─ pq/ # Post-quantum (experimental)
└─ StellaOps.Cryptography.sln
```
### Library projects under `src/__Libraries/`:
```
src/__Libraries/
├─ StellaOps.Cryptography.Tests/
├─ StellaOps.Cryptography.Plugin.CryptoPro/ # GOST via CryptoPro CSP
├─ StellaOps.Cryptography.Plugin.EIDAS/ # eIDAS qualified signatures
├─ StellaOps.Cryptography.Plugin.SmSoft/ # SM2 software implementation
├─ StellaOps.Cryptography.Plugin.SmRemote/ # SM2 via remote HSM
├─ StellaOps.Cryptography.Plugin.OfflineVerification/
├─ StellaOps.Cryptography.PluginLoader/
└─ StellaOps.Cryptography.Kms/ # KMS integration
```
---
## 2) External dependencies
* **.NET Cryptography APIs** - Built-in crypto primitives
* **BouncyCastle** (optional) - Extended algorithm support
* **CryptoPro CSP** (optional) - GOST support on Windows
* **HSM/TPM** (optional) - Hardware security modules
* **KMS** (optional) - Cloud key management services
---
## 3) Contracts & data model
### 3.1 Hash Computation
```csharp
public interface ICryptoHash
{
string ComputeSha256(ReadOnlySpan<byte> data);
string ComputeBlake3(ReadOnlySpan<byte> data);
string ComputeHash(ReadOnlySpan<byte> data, HashAlgorithm algorithm);
}
public enum HashAlgorithm
{
Sha256,
Sha384,
Sha512,
Blake3_256,
Blake3_512,
Sm3, // China
Streebog256, // Russia (GOST R 34.11-2012)
Streebog512
}
```
### 3.2 Signature Provider
```csharp
public interface ISignatureProvider
{
string AlgorithmId { get; }
string CurveId { get; }
Task<byte[]> SignAsync(
ReadOnlyMemory<byte> data,
IKeyProvider keyProvider,
CancellationToken ct);
Task<bool> VerifyAsync(
ReadOnlyMemory<byte> data,
ReadOnlyMemory<byte> signature,
ReadOnlyMemory<byte> publicKey,
CancellationToken ct);
}
```
### 3.3 Crypto Profile
```csharp
public sealed record CryptoProfile
{
public required string ProfileId { get; init; }
public required string DisplayName { get; init; }
public required HashAlgorithm PreferredHash { get; init; }
public required string SignatureAlgorithm { get; init; }
public required string KeyType { get; init; }
public bool RequiresHsm { get; init; }
public IReadOnlyList<string>? AllowedCurves { get; init; }
}
```
---
## 4) Supported Profiles
### 4.1 Built-in Profiles
| Profile | Hash | Signature | Use Case |
|---------|------|-----------|----------|
| `ecdsa-p256` | SHA-256 | ECDSA P-256 | Default, FIPS 140-2 |
| `ecdsa-p384` | SHA-384 | ECDSA P-384 | Higher security |
| `eddsa-ed25519` | SHA-512 | Ed25519 | Performance |
| `ecdsa-secp256k1` | SHA-256 | ECDSA secp256k1 | Blockchain compat |
### 4.2 Plugin Profiles
| Profile | Hash | Signature | Region |
|---------|------|-----------|--------|
| `gost-2012` | Streebog-256 | GOST R 34.10-2012 | Russia |
| `sm2` | SM3 | SM2 | China |
| `eidas-rsa` | SHA-256 | RSA-PSS | EU (qualified) |
| `eidas-ecdsa` | SHA-256 | ECDSA P-256 | EU (qualified) |
---
## 5) Plugin Architecture
### 5.1 Plugin Discovery
Plugins are loaded from `plugins/` directory:
```
plugins/
├─ gost/
│ ├─ manifest.json
│ └─ StellaOps.Cryptography.Plugin.Gost.dll
└─ sm/
├─ manifest.json
└─ StellaOps.Cryptography.Plugin.Sm.dll
```
### 5.2 Plugin Manifest
```json
{
"pluginId": "stellaops.crypto.gost",
"version": "1.0.0",
"profiles": ["gost-2012-256", "gost-2012-512"],
"dependencies": {
"cryptopro": "5.0+"
},
"platforms": ["win-x64", "linux-x64"]
}
```
### 5.3 Plugin Interface
```csharp
public interface ICryptoPlugin
{
string PluginId { get; }
IReadOnlyList<CryptoProfile> Profiles { get; }
ISignatureProvider GetSignatureProvider(string profileId);
ICryptoHash GetHashProvider(string profileId);
Task<bool> IsAvailableAsync(CancellationToken ct);
}
```
---
## 6) Configuration (YAML)
```yaml
Cryptography:
DefaultProfile: "ecdsa-p256"
Profiles:
ecdsa-p256:
Enabled: true
PreferredHash: "sha256"
eddsa-ed25519:
Enabled: true
PreferredHash: "sha512"
gost-2012:
Enabled: false # Requires CryptoPro
RequiresHsm: false
sm2:
Enabled: false
HsmEndpoint: "https://hsm.example.cn"
Plugins:
Directory: "/opt/stellaops/plugins/crypto"
AutoLoad: true
Hsm:
Provider: "pkcs11" # or "kms", "tpm"
LibraryPath: "/usr/lib/softhsm/libsofthsm2.so"
SlotId: 0
Kms:
Provider: "aws" # or "azure", "gcp", "vault"
Region: "us-east-1"
```
---
## 7) Security considerations
* **Algorithm agility**: Easy migration to new algorithms
* **Side-channel protection**: Constant-time implementations where available
* **Key isolation**: Keys never exposed in memory dumps
* **Determinism**: Same input produces same signature (where algorithm allows)
* **Audit trail**: All crypto operations logged
---
## 8) Performance targets
* **SHA-256**: > 500 MB/s on modern hardware
* **BLAKE3**: > 1 GB/s on modern hardware
* **ECDSA P-256 sign**: < 1ms per signature
* **Ed25519 sign**: < 0.5ms per signature
* **HSM operations**: < 50ms (network latency dependent)
---
## 9) Testing matrix
* **Vector tests**: NIST/RFC test vectors for each algorithm
* **Interoperability**: Cross-platform signature verification
* **Determinism**: Same key + data = same signature
* **Plugin tests**: Load/unload, availability detection
* **HSM tests**: Mock HSM for CI, real HSM for integration
---
## 10) Offline Operation
For air-gapped deployments:
1. **Offline key bundles**: Pre-exported public keys for verification
2. **Local plugins**: All plugins loaded from local filesystem
3. **No KMS calls**: All operations use local keys or HSM
4. **Trust bundles**: Pre-configured trust roots
---
## Related Documentation
* Multi-profile signing: `./multi-profile-signing-specification.md`
* Signer module: `../signer/architecture.md`
* Attestor module: `../attestor/architecture.md`
* Offline operations: `../../24_OFFLINE_KIT.md`

View File

@@ -0,0 +1,278 @@
# component_architecture_evidence_locker.md - **Stella Ops EvidenceLocker** (2025Q4)
> Sealed, immutable storage for vulnerability scan evidence and audit logs.
> **Scope.** Implementation-ready architecture for **EvidenceLocker**: tamper-proof evidence chains for compliance and forensic analysis with content-addressable storage and cryptographic sealing.
---
## 0) Mission & boundaries
**Mission.** Provide **immutable, sealed storage** for vulnerability scan evidence, audit logs, and compliance artifacts. Ensure tamper-proof evidence chains with cryptographic verification for forensic analysis and regulatory compliance.
**Boundaries.**
* EvidenceLocker **stores** evidence; it does not generate verdicts.
* EvidenceLocker **seals** bundles; signing is delegated to Signer.
* Evidence is **immutable** once sealed; no modifications or deletions.
* Supports **offline export** for air-gapped compliance audits.
---
## 1) Solution & project layout
```
src/EvidenceLocker/StellaOps.EvidenceLocker/
├─ StellaOps.EvidenceLocker.Core/ # Sealing, verification, chain validation
│ ├─ Services/
│ │ ├─ ISealingService.cs # Sealing interface
│ │ ├─ SealingService.cs # Cryptographic sealing
│ │ ├─ IVerificationService.cs # Verification interface
│ │ └─ ChainValidator.cs # Evidence chain validation
│ └─ Models/
│ ├─ EvidenceBundle.cs # Bundle model
│ ├─ EvidenceItem.cs # Individual evidence item
│ └─ SealManifest.cs # Seal metadata
├─ StellaOps.EvidenceLocker.Infrastructure/ # Storage adapters, bundle management
│ ├─ Storage/
│ │ ├─ IEvidenceStore.cs # Storage interface
│ │ ├─ FileSystemStore.cs # Local filesystem
│ │ └─ ObjectStore.cs # S3/RustFS storage
│ └─ Persistence/
│ └─ PostgresRepository.cs # Metadata persistence
├─ StellaOps.EvidenceLocker.WebService/ # HTTP API for submission/retrieval
│ └─ Program.cs
├─ StellaOps.EvidenceLocker.Worker/ # Background sealing and archival
│ └─ SealingWorker.cs
└─ StellaOps.EvidenceLocker.Tests/ # Unit and integration tests
```
---
## 2) External dependencies
* **PostgreSQL** - Metadata storage (schema: `evidence_locker`)
* **RustFS/S3** - Object storage for evidence bundles
* **Signer** - Cryptographic sealing operations
* **Authority** - Authentication and authorization
* **ExportCenter** - Evidence bundle export
---
## 3) Contracts & data model
### 3.1 EvidenceBundle
```json
{
"bundleId": "eb-2025-01-15-abc123",
"tenantId": "tenant-xyz",
"createdAt": "2025-01-15T10:30:00.000000Z",
"sealedAt": "2025-01-15T10:30:05.000000Z",
"status": "sealed",
"items": [
{
"itemId": "item-001",
"type": "sbom",
"format": "cyclonedx-json",
"digest": "sha256:abc123...",
"size": 45678,
"casUri": "cas://evidence/items/abc123"
},
{
"itemId": "item-002",
"type": "scan-result",
"format": "stellaops-findings-v1",
"digest": "sha256:def456...",
"size": 12345,
"casUri": "cas://evidence/items/def456"
}
],
"seal": {
"algorithm": "sha256",
"rootHash": "sha256:fedcba...",
"signature": "base64...",
"keyId": "sha256:keyabc..."
},
"chain": {
"previousBundleId": "eb-2025-01-14-xyz789",
"previousRootHash": "sha256:prevhash...",
"sequenceNumber": 42
}
}
```
### 3.2 Evidence Item Types
| Type | Format | Description |
|------|--------|-------------|
| `sbom` | cyclonedx-json, spdx-json | Software Bill of Materials |
| `scan-result` | stellaops-findings-v1 | Vulnerability scan findings |
| `policy-verdict` | stellaops-verdict-v1 | Policy evaluation result |
| `vex-statement` | openvex-v1 | VEX statement |
| `audit-log` | ndjson | Audit trail events |
| `attestation` | dsse-v1 | DSSE attestation envelope |
### 3.3 Seal Manifest
```csharp
public sealed record SealManifest
{
public required string BundleId { get; init; }
public required string RootHash { get; init; }
public required string Algorithm { get; init; }
public required DateTimeOffset SealedAt { get; init; }
public required string KeyId { get; init; }
public required byte[] Signature { get; init; }
public required IReadOnlyList<string> ItemDigests { get; init; }
}
```
---
## 4) REST API (EvidenceLocker.WebService)
All under `/api/v1/evidence`. Auth: **OpTok**.
```
POST /bundles { items: EvidenceItem[] } → { bundleId, status: "pending" }
POST /bundles/{id}/items { item: EvidenceItem } → { itemId }
POST /bundles/{id}/seal → { status: "sealed", seal: SealManifest }
GET /bundles/{id} → { bundle: EvidenceBundle }
GET /bundles/{id}/items/{itemId} → binary content
GET /bundles/{id}/verify → { valid: bool, details }
GET /bundles?tenant={id}&from={date}&to={date} → { bundles: BundleSummary[] }
POST /export { bundleIds: string[], format: "zip"|"tar" } → { exportId }
GET /export/{id} → binary archive
GET /export/{id}/status → { status, progress }
GET /healthz | /readyz | /metrics
```
---
## 5) Configuration (YAML)
```yaml
EvidenceLocker:
Postgres:
ConnectionString: "Host=postgres;Database=evidence_locker;..."
Storage:
Provider: "rustfs" # or "filesystem", "s3"
RustFs:
Endpoint: "http://rustfs:8080"
Bucket: "stellaops-evidence"
Filesystem:
BasePath: "/data/evidence"
Sealing:
Policy: "immediate" # or "batch"
BatchSize: 100
BatchIntervalSeconds: 60
Algorithm: "sha256"
Retention:
DefaultDays: 2555 # 7 years
ComplianceDays: 3650 # 10 years for regulated
Export:
MaxBundlesPerExport: 1000
CompressionLevel: 6
Authority:
Issuer: "https://authority.stellaops.local"
RequiredScopes: ["evidence:read", "evidence:write"]
```
---
## 6) Sealing Process
### 6.1 Sealing Flow
```
1. Bundle created (status: "pending")
└─ Items added with content digests
2. Sealing triggered (immediate or batch)
├─ Compute Merkle root from item digests
├─ Include chain pointer (previous bundle hash)
└─ Request signature from Signer
3. Bundle sealed (status: "sealed")
└─ Immutable; no further modifications
```
### 6.2 Chain Integrity
Evidence chains are linked via Merkle roots:
```
Bundle N-1 Bundle N Bundle N+1
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ rootHash: H1│◄────────────│ prevHash: H1│◄─────────────│ prevHash: H2│
│ seq: 41 │ │ rootHash: H2│ │ rootHash: H3│
└─────────────┘ │ seq: 42 │ │ seq: 43 │
└─────────────┘ └─────────────┘
```
---
## 7) Security & compliance
* **Immutability**: Sealed bundles cannot be modified
* **Tamper detection**: Merkle tree verification for all items
* **Chain validation**: Linked bundle verification
* **Encryption at rest**: Optional bundle encryption
* **Access control**: Tenant-scoped with Authority tokens
* **Audit trail**: All access logged
---
## 8) Performance targets
* **Item ingestion**: < 100ms P95 per item
* **Sealing**: < 500ms P95 per bundle (up to 100 items)
* **Verification**: < 200ms P95 per bundle
* **Export**: < 5s P95 for 100 bundles
---
## 9) Observability
**Metrics:**
* `evidence.bundles.created_total{tenant}`
* `evidence.bundles.sealed_total{tenant}`
* `evidence.items.ingested_total{type}`
* `evidence.verification.duration_seconds`
* `evidence.storage.bytes_total`
**Tracing:** Spans for ingestion, sealing, verification, export.
---
## 10) Testing matrix
* **Sealing tests**: Correct Merkle root computation
* **Chain tests**: Linked bundle verification
* **Tamper tests**: Detection of modified items
* **Export tests**: Archive integrity verification
* **Retention tests**: Policy enforcement
---
## Related Documentation
* Bundle packaging: `./bundle-packaging.md`
* Attestation contract: `./attestation-contract.md`
* Evidence bundle spec: `./evidence-bundle-v1.md`
* ExportCenter: `../export-center/architecture.md`
* Attestor: `../attestor/architecture.md`

View File

@@ -0,0 +1,237 @@
# component_architecture_feedser.md - **Stella Ops Feedser** (2025Q4)
> Evidence collection library for backport detection and binary fingerprinting.
> **Scope.** Library architecture for **Feedser**: patch signature extraction, binary fingerprinting, and evidence collection supporting the four-tier backport proof system. Consumed primarily by Concelier's ProofService layer.
---
## 0) Mission & boundaries
**Mission.** Provide deterministic, cryptographic evidence collection for backport detection. Extract patch signatures from unified diffs and binary fingerprints from compiled code to enable high-confidence vulnerability status determination for packages where upstream fixes have been backported by distro maintainers.
**Boundaries.**
* Feedser is a **library**, not a standalone service. It does not expose REST APIs directly.
* Feedser **does not** make vulnerability decisions. It provides evidence that feeds into VEX statements and Policy Engine evaluation.
* Feedser **does not** store data. Storage is handled by consuming services (Concelier ProofService, Attestor).
* All outputs are **deterministic** with canonical JSON serialization and stable hashing.
---
## 1) Solution & project layout
```
src/Feedser/
├─ StellaOps.Feedser.Core/ # Patch signature extraction (HunkSig)
│ ├─ HunkSigExtractor.cs # Unified diff parser and normalizer
│ ├─ Models/
│ │ ├─ PatchSignature.cs # Deterministic patch identifier
│ │ ├─ HunkSignature.cs # Individual hunk with normalized content
│ │ └─ DiffParseResult.cs # Parse output with file paths and hunks
│ └─ Normalization/
│ └─ WhitespaceNormalizer.cs # Whitespace/comment stripping
├─ StellaOps.Feedser.BinaryAnalysis/ # Binary fingerprinting engine
│ ├─ BinaryFingerprintFactory.cs # Factory for fingerprinting strategies
│ ├─ IBinaryFingerprinter.cs # Fingerprinter interface
│ ├─ Models/
│ │ ├─ BinaryFingerprint.cs # Fingerprint record with method/value
│ │ └─ FingerprintMatchResult.cs # Match score and confidence
│ └─ Fingerprinters/
│ ├─ SimplifiedTlshFingerprinter.cs # TLSH fuzzy hashing
│ └─ InstructionHashFingerprinter.cs # Instruction sequence hashing
├─ plugins/
│ └─ concelier/ # Concelier integration plugin
└─ __Tests/
└─ StellaOps.Feedser.Core.Tests/ # Unit tests
```
---
## 2) External dependencies
* **Concelier ProofService** - Primary consumer; orchestrates four-tier evidence collection
* **Attestor ProofChain** - Consumes evidence for proof blob generation
* **.NET 10** - Runtime target
* No database dependencies (stateless library)
* No external network dependencies
---
## 3) Contracts & data model
### 3.1 Patch Signature (Tier 3 Evidence)
```csharp
public sealed record PatchSignature
{
public required string Id { get; init; } // Deterministic SHA256
public required string FilePath { get; init; } // Source file path
public required IReadOnlyList<HunkSignature> Hunks { get; init; }
public required string ContentHash { get; init; } // BLAKE3-256 of normalized content
public string? CommitId { get; init; } // Git commit SHA if available
public string? UpstreamCve { get; init; } // Associated CVE
}
public sealed record HunkSignature
{
public required int OldStart { get; init; }
public required int NewStart { get; init; }
public required string NormalizedContent { get; init; } // Whitespace-stripped
public required string ContentHash { get; init; }
}
```
### 3.2 Binary Fingerprint (Tier 4 Evidence)
```csharp
public sealed record BinaryFingerprint
{
public required string Method { get; init; } // tlsh, instruction_hash
public required string Value { get; init; } // Fingerprint value
public required string TargetPath { get; init; } // Binary file path
public string? FunctionName { get; init; } // Function if scoped
public required string Architecture { get; init; } // x86_64, aarch64, etc.
}
public sealed record FingerprintMatchResult
{
public required decimal Similarity { get; init; } // 0.0-1.0
public required decimal Confidence { get; init; } // 0.0-1.0
public required string Method { get; init; }
public required BinaryFingerprint Query { get; init; }
public required BinaryFingerprint Match { get; init; }
}
```
### 3.3 Evidence Tier Confidence Levels
| Tier | Evidence Type | Confidence Range | Description |
|------|--------------|------------------|-------------|
| 1 | Distro Advisory | 0.95-0.98 | Official vendor/distro statement |
| 2 | Changelog Mention | 0.75-0.85 | CVE mentioned in changelog |
| 3 | Patch Signature (HunkSig) | 0.85-0.95 | Normalized patch hash match |
| 4 | Binary Fingerprint | 0.55-0.85 | Compiled code similarity |
---
## 4) Core Components
### 4.1 HunkSigExtractor
Parses unified diff format and extracts normalized patch signatures:
```csharp
public interface IHunkSigExtractor
{
PatchSignature Extract(string unifiedDiff, string? commitId = null);
IReadOnlyList<PatchSignature> ExtractMultiple(string multiFileDiff);
}
```
**Normalization rules:**
- Strip leading/trailing whitespace
- Normalize line endings to LF
- Remove C-style comments (optional)
- Collapse multiple whitespace to single space
- Sort hunks by (file_path, old_start) for determinism
### 4.2 BinaryFingerprintFactory
Factory for creating fingerprinters based on binary type and analysis requirements:
```csharp
public interface IBinaryFingerprintFactory
{
IBinaryFingerprinter Create(FingerprintMethod method);
IReadOnlyList<IBinaryFingerprinter> GetAll();
}
public interface IBinaryFingerprinter
{
string Method { get; }
BinaryFingerprint Extract(ReadOnlySpan<byte> binary, string path);
FingerprintMatchResult Match(BinaryFingerprint query, BinaryFingerprint candidate);
}
```
**Fingerprinting methods:**
| Method | Description | Confidence | Use Case |
|--------|-------------|------------|----------|
| `tlsh` | TLSH fuzzy hash | 0.75-0.85 | General binary similarity |
| `instruction_hash` | Normalized instruction sequences | 0.55-0.75 | Function-level matching |
---
## 5) Integration with Concelier
Feedser is consumed via `StellaOps.Concelier.ProofService.BackportProofService`:
```
BackportProofService (Concelier)
├─ Tier 1: Query advisory_observations (distro advisories)
├─ Tier 2: Query changelogs via ISourceRepository
├─ Tier 3: Query patches via IPatchRepository + HunkSigExtractor
├─ Tier 4: Query binaries + BinaryFingerprintFactory
└─ Aggregate → ProofBlob with combined confidence score
```
The ProofService orchestrates evidence collection across all tiers and produces cryptographic proof blobs for downstream consumption.
---
## 6) Security & compliance
* **Determinism**: All outputs use canonical JSON with sorted keys, UTC timestamps
* **Tamper evidence**: BLAKE3-256 content hashes for all signatures
* **No secrets**: Library handles only public patch/binary data
* **Offline capable**: No network dependencies in core library
---
## 7) Performance targets
* **Patch extraction**: < 10ms for typical unified diff (< 1000 lines)
* **Binary fingerprinting**: < 100ms for 10MB ELF binary
* **Memory**: Streaming processing for large binaries; no full file buffering
* **Parallelism**: Thread-safe extractors; concurrent fingerprinting supported
---
## 8) Observability
Library consumers (ProofService) emit metrics:
* `feedser.hunk_extraction_duration_seconds`
* `feedser.binary_fingerprint_duration_seconds`
* `feedser.fingerprint_match_score{method}`
* `feedser.evidence_tier_confidence{tier}`
---
## 9) Testing matrix
* **Unit tests**: HunkSigExtractor parsing, normalization edge cases
* **Fingerprint tests**: Known binary pairs with expected similarity scores
* **Determinism tests**: Same input produces identical output across runs
* **Performance tests**: Large diff/binary processing within targets
---
## 10) Historical note
Concelier was formerly named "Feedser" (see `docs/airgap/airgap-mode.md`). The module was refactored:
- **Feedser** retained as evidence collection library
- **Concelier** became the advisory aggregation service consuming Feedser
---
## Related Documentation
* Concelier architecture: `../concelier/architecture.md`
* Attestor ProofChain: `../attestor/architecture.md`
* Backport proof system: `../../reachability/backport-proofs.md`

View File

@@ -0,0 +1,60 @@
# component_architecture_mirror.md - **Stella Ops Mirror** (2025Q4)
> Vulnerability feed mirror and distribution service.
> **Scope.** Architecture for **Mirror**: mirroring vulnerability feeds from upstream sources for offline distribution and reduced external dependencies.
---
## 0) Mission & boundaries
**Mission.** Provide **local mirrors** of vulnerability feeds (NVD, OSV, GHSA, etc.) for offline operation and reduced latency. Enable air-gapped deployments to receive updates via bundle import.
**Boundaries.**
* Mirror **caches upstream feeds**; it does not originate vulnerability data.
* Mirror **produces bundles** for air-gapped distribution.
* Feeds are **cryptographically verified** before distribution.
---
## 1) Integration with Concelier
Mirror is primarily integrated as part of Concelier's federation layer:
```
src/Concelier/__Libraries/
└─ StellaOps.Concelier.Federation/ # Bundle export/import for offline
```
The `StellaOpsMirror` connector in Concelier handles:
- Upstream feed synchronization
- Local cache management
- Bundle generation for offline distribution
---
## 2) Bundle Format
```json
{
"bundleId": "mirror-nvd-2025-01-15",
"source": "nvd",
"timestamp": "2025-01-15T10:30:00Z",
"contents": [
{
"path": "nvd/CVE-2025-*.json",
"digest": "sha256:abc123..."
}
],
"signature": { /* DSSE envelope */ }
}
```
---
## Related Documentation
* Concelier: `../concelier/architecture.md`
* AirGap: `../airgap/architecture.md`
* Provenance observers: `./provenance/observers.md`

View File

@@ -0,0 +1,38 @@
# Notifier (Notifications Studio Host)
**Status:** Implemented
**Source:** `src/Notifier/`
**Owner:** Notify Guild
> **Note:** Notifier is the **deployment host** for the Notifications Studio. For the underlying notification toolkit (engine, storage, queue, connectors), see [`../notify/`](../notify/).
## Purpose
Notifier provides the deployable WebService and Worker that compose the Notify libraries into the Notifications Studio experience. It's the entry point for notification delivery, rule management, and delivery history.
## Relationship to Notify
| Component | Path | Purpose |
|-----------|------|---------|
| **Notify** | `src/Notify/` | Reusable toolkit: engine, models, connectors, queue |
| **Notifier** | `src/Notifier/` | Host: WebService and Worker that compose Notify |
Per **2025-11-02 module boundary decision**: Maintain separation for packaging, offline kit parity, and cross-module governance.
## Components
**Deployables:**
- `StellaOps.Notifier.WebService` - REST API for rules/channels CRUD, test send, delivery browsing
- `StellaOps.Notifier.Worker` - Event consumers, evaluators, renderers, delivery workers
**Integration Points:**
- Uses `StellaOps.Notify.Models`, `StellaOps.Notify.Queue`
- Channels: Slack, Teams, Email, Webhook (via Notify connectors)
- Storage: PostgreSQL (notify schema)
- Queue: Valkey Streams / NATS JetStream
## Related Documentation
- Notify Architecture: `../notify/architecture.md`
- Authority: `../authority/` (OAuth clients)
- Scheduler: `../scheduler/` (event sources)

View File

@@ -0,0 +1,100 @@
# 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

@@ -0,0 +1,316 @@
# 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

@@ -0,0 +1,231 @@
# ReachGraph Module Architecture
## Overview
The **ReachGraph** module provides a unified store for reachability subgraphs, enabling fast, deterministic, audit-ready answers to "*exactly why* a dependency is reachable." It consolidates data from Scanner, Signals, and Attestor into content-addressed artifacts with edge-level explainability.
## Problem Statement
Before ReachGraph, reachability data was scattered across multiple modules:
| Module | Data | Limitation |
|--------|------|------------|
| Scanner.CallGraph | `CallGraphSnapshot` | No unified query API |
| Signals | `ReachabilityFactDocument` | Runtime-focused, not auditable |
| Attestor | PoE JSON | Per-CVE only, no slice queries |
| Graph | Generic nodes/edges | Not optimized for "why reachable?" |
**Result**: Answering "why is lodash reachable?" required querying multiple systems with no guarantee of consistency or auditability.
## Solution
ReachGraph provides:
1. **Unified Schema**: Extends PoE subgraph format with edge explainability
2. **Content-Addressed Store**: All artifacts identified by BLAKE3 digest
3. **Slice Query API**: Fast queries by package, CVE, entrypoint, or file
4. **Deterministic Replay**: Verify that same inputs produce same graph
5. **DSSE Signing**: Offline-verifiable proofs
## Architecture
```
┌─────────────────────────────────────────────────────────────────┐
│ Consumers │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Policy │ │ Web │ │ CLI │ │ Export │ │
│ │ Engine │ │ Console │ │ │ │ Center │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
└───────┼─────────────┼─────────────┼─────────────┼───────────────┘
│ │ │ │
└─────────────┴──────┬──────┴─────────────┘
┌────────────────────────────▼────────────────────────────────────┐
│ ReachGraph WebService │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ REST API │ │
│ │ POST /v1/reachgraphs GET /v1/reachgraphs/{d} │ │
│ │ GET /v1/reachgraphs/{d}/slice POST /v1/reachgraphs/replay│ │
│ └──────────────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Slice Query Engine │ │
│ │ - Package slice (by PURL) │ │
│ │ - CVE slice (paths to vulnerable sinks) │ │
│ │ - Entrypoint slice (reachable from entry) │ │
│ │ - File slice (changed file impact) │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Replay Driver │ │
│ │ - Rebuild graph from inputs │ │
│ │ - Verify digest matches │ │
│ │ - Log for audit trail │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────┬───────────────────────────────────┘
┌─────────────────────────────▼───────────────────────────────────┐
│ ReachGraph Core Library │
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │
│ │ Schema │ │ Serialization │ │ Signing │ │
│ │ │ │ │ │ │ │
│ │ ReachGraphMin │ │ Canonical JSON │ │ DSSE Wrapper │ │
│ │ EdgeExplanation│ │ BLAKE3 Digest │ │ Attestor Int. │ │
│ │ Provenance │ │ Compression │ │ │ │
│ └────────────────┘ └────────────────┘ └────────────────┘ │
└─────────────────────────────┬───────────────────────────────────┘
┌─────────────────────────────▼───────────────────────────────────┐
│ Persistence Layer │
│ ┌────────────────────────┐ ┌────────────────────────┐ │
│ │ PostgreSQL │ │ Valkey │ │
│ │ │ │ │ │
│ │ reachgraph.subgraphs │ │ Hot slice cache │ │
│ │ reachgraph.slice_cache│ │ (30min TTL) │ │
│ │ reachgraph.replay_log │ │ │ │
│ └────────────────────────┘ └────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────┴───────────────────────────────────┐
│ Producers │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Scanner │ │ Signals │ │ Attestor │ │
│ │ CallGraph │ │ RuntimeFacts │ │ PoE │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
## Data Model
### ReachGraphMinimal (v1)
The core schema extends the PoE predicate format:
```json
{
"schemaVersion": "reachgraph.min@v1",
"artifact": {
"name": "svc.payments",
"digest": "sha256:abc123...",
"env": ["linux/amd64"]
},
"scope": {
"entrypoints": ["/app/bin/svc"],
"selectors": ["prod"],
"cves": ["CVE-2024-1234"]
},
"nodes": [...],
"edges": [...],
"provenance": {...},
"signatures": [...]
}
```
### Edge Explainability
Every edge carries metadata explaining *why* it exists:
| Type | Description | Example Guard |
|------|-------------|---------------|
| `Import` | Static import | - |
| `DynamicLoad` | Runtime load | - |
| `Reflection` | Reflective call | - |
| `EnvGuard` | Env variable check | `DEBUG=true` |
| `FeatureFlag` | Feature flag | `FEATURE_X=enabled` |
| `PlatformArch` | Platform guard | `os=linux` |
| `LoaderRule` | PLT/IAT/GOT | `RTLD_LAZY` |
### Content Addressing
All artifacts are identified by BLAKE3-256 digest:
- Computed from canonical JSON (sorted keys, no nulls)
- Signatures excluded from hash computation
- Enables idempotent upserts and cache keying
## API Design
### Core Endpoints
| Method | Path | Description |
|--------|------|-------------|
| POST | `/v1/reachgraphs` | Upsert subgraph (idempotent) |
| GET | `/v1/reachgraphs/{digest}` | Get full subgraph |
| GET | `/v1/reachgraphs/{digest}/slice` | Query slice |
| POST | `/v1/reachgraphs/replay` | Verify determinism |
### Slice Query Types
1. **Package Slice** (`?q=pkg:npm/lodash@4.17.21`)
- Returns subgraph containing package and neighbors
- Configurable depth and direction
2. **CVE Slice** (`?cve=CVE-2024-1234`)
- Returns all paths from entrypoints to vulnerable sinks
- Includes edge explanations for each hop
3. **Entrypoint Slice** (`?entrypoint=/app/bin/svc`)
- Returns everything reachable from entry
- Optionally filtered to paths reaching sinks
4. **File Slice** (`?file=src/**/*.ts`)
- Returns impact of changed files
- Useful for PR-based analysis
## Integration Points
### Upstream (Data Producers)
- **Scanner.CallGraph**: Produces nodes and edges with edge explanations
- **Signals**: Provides runtime confirmation of reachability
- **Attestor**: DSSE signing integration
### Downstream (Data Consumers)
- **Policy Engine**: `ReachabilityRequirementGate` queries slices
- **Web Console**: "Why Reachable?" panel displays paths
- **CLI**: `stella reachgraph slice/replay` commands
- **ExportCenter**: Includes subgraphs in evidence bundles
## Determinism Guarantees
1. **Canonical Serialization**
- Sorted object keys (lexicographic)
- Sorted arrays by deterministic field
- UTC ISO-8601 timestamps
- No null fields (omit when null)
2. **Replay Verification**
- POST `/v1/reachgraphs/replay` rebuilds from inputs
- Returns `{match: true}` if digests match
- Logs all attempts for audit trail
3. **Content Addressing**
- Same content always produces same digest
- Enables cache keying and deduplication
## Performance Characteristics
| Operation | Target Latency | Notes |
|-----------|---------------|-------|
| Slice query | P95 < 200ms | Cached in Valkey |
| Full graph retrieval | P95 < 500ms | Compressed storage |
| Upsert | P95 < 1s | Idempotent, gzip compression |
| Replay | P95 < 5s | Depends on input size |
## Security Considerations
1. **Tenant Isolation**: RLS policies enforce at database level
2. **Rate Limiting**: 100 req/min reads, 20 req/min writes
3. **DSSE Signing**: All artifacts verifiable offline
4. **Input Validation**: Schema validation on all requests
## Related Documentation
- [Sprint 1227.0012.0001 - Core Library](../../implplan/SPRINT_1227_0012_0001_LB_reachgraph_core.md)
- [Sprint 1227.0012.0002 - Store APIs](../../implplan/SPRINT_1227_0012_0002_BE_reachgraph_store.md)
- [Sprint 1227.0012.0003 - Integration](../../implplan/SPRINT_1227_0012_0003_FE_reachgraph_integration.md)
- [PoE Predicate Spec](../../../src/Attestor/POE_PREDICATE_SPEC.md)
- [Module AGENTS.md](../../../src/__Libraries/StellaOps.ReachGraph/AGENTS.md)
---
_Last updated: 2025-12-27_

View File

@@ -0,0 +1,265 @@
# component_architecture_replay.md - **Stella Ops Replay** (2025Q4)
> Deterministic replay engine for vulnerability verdict reproducibility.
> **Scope.** Implementation-ready architecture for **Replay**: the deterministic replay engine ensuring vulnerability assessments can be reproduced byte-for-byte given the same inputs. Covers replay tokens, manifests, feed snapshots, and verification workflows.
---
## 0) Mission & boundaries
**Mission.** Enable **deterministic reproducibility** of vulnerability verdicts. Given identical inputs (SBOM, policy, feeds, toolchain), the system MUST produce identical outputs. Replay provides the infrastructure to capture, store, and verify these deterministic execution chains.
**Boundaries.**
* Replay **does not** make vulnerability decisions. It captures the inputs and outputs of decision-making services.
* Replay **does not** store SBOMs or vulnerability data. It stores references (digests) to content-addressed artifacts.
* Replay tokens are **cryptographically bound** to input digests.
* All timestamps are **UTC ISO-8601** with microsecond precision.
---
## 1) Solution & project layout
```
src/Replay/
├─ StellaOps.Replay.WebService/ # Token issuance and verification API
│ ├─ Program.cs # ASP.NET Core host
│ └─ VerdictReplayEndpoints.cs # Minimal API endpoints
└─ __Tests/
└─ StellaOps.Replay.Core.Tests/ # Unit tests
src/__Libraries/
├─ StellaOps.Replay.Core/ # Core replay manifest and validation
│ ├─ ReplayManifest.cs # Manifest schema (v1, v2)
│ ├─ ReplayManifestValidator.cs # Validation logic
│ ├─ DeterministicHash.cs # Hash computation utilities
│ ├─ PolicySimulationInputLock.cs # Input pinning for simulation
│ └─ FeedSnapshot/
│ └─ FeedSnapshotCoordinatorService.cs
├─ StellaOps.Audit.ReplayToken/ # Token generation and verification
│ ├─ ReplayToken.cs # Token model
│ ├─ ReplayTokenRequest.cs # Token request DTO
│ ├─ IReplayTokenGenerator.cs # Generator interface
│ └─ Sha256ReplayTokenGenerator.cs # SHA-256 based implementation
└─ StellaOps.Replay/ # Shared replay utilities
```
---
## 2) External dependencies
* **PostgreSQL** - Token storage and manifest persistence
* **Authority** - Authentication for token issuance/verification
* **Cryptography** - Hash computation (SHA-256, BLAKE3)
* **CAS (Content-Addressed Storage)** - Artifact storage for replay bundles
* **Policy Engine** - Consumes replay manifests for deterministic simulation
---
## 3) Contracts & data model
### 3.1 ReplayManifest
The manifest captures all inputs required to reproduce a verdict:
```json
{
"schemaVersion": "2.0",
"scan": {
"id": "scan-2025-01-15T10:30:00Z-abc123",
"time": "2025-01-15T10:30:00.000000Z",
"policyDigest": "sha256:abc123...",
"scorePolicyDigest": "sha256:def456...",
"feedSnapshot": "sha256:789abc...",
"toolchain": "stellaops/scanner:1.7.3",
"analyzerSetDigest": "sha256:feed12..."
},
"reachability": {
"analysisId": "reach-xyz",
"graphs": [
{
"kind": "static",
"casUri": "cas://reachability/graphs/abc123",
"hash": "blake3:a1b2c3d4...",
"hashAlg": "blake3-256",
"analyzer": "elf-callgraph",
"version": "1.2.0"
}
],
"runtimeTraces": [],
"code_id_coverage": {
"total_nodes": 1500,
"nodes_with_symbol_id": 1200,
"nodes_with_code_id": 1100,
"coverage_percent": 73.33
}
},
"proofSpines": [
{
"spineId": "spine-abc123",
"artifactId": "pkg:npm/lodash@4.17.21",
"vulnerabilityId": "CVE-2021-23337",
"verdict": "not_affected",
"segmentCount": 4,
"rootHash": "sha256:fedcba...",
"casUri": "cas://proofs/spines/abc123",
"createdAt": "2025-01-15T10:30:05.000000Z"
}
]
}
```
### 3.2 ReplayToken
Cryptographic token binding inputs to outputs:
```csharp
public sealed record ReplayToken
{
public required string TokenId { get; init; } // Unique token ID
public required string InputDigest { get; init; } // Hash of all inputs
public required string OutputDigest { get; init; } // Hash of verdict output
public required DateTimeOffset IssuedAt { get; init; } // UTC timestamp
public required string Issuer { get; init; } // Service that issued
public string? Signature { get; init; } // DSSE signature
}
```
### 3.3 PolicySimulationInputLock
Captures pinned versions for deterministic policy simulation:
```csharp
public sealed record PolicySimulationInputLock
{
public required string PolicyDigest { get; init; }
public required string FeedSnapshotDigest { get; init; }
public required string ScorePolicyDigest { get; init; }
public required DateTimeOffset LockedAt { get; init; }
public required IReadOnlyList<AnalyzerPin> AnalyzerPins { get; init; }
}
```
---
## 4) REST API (Replay.WebService)
All under `/api/v1/replay`. Auth: **OpTok** (DPoP/mTLS).
```
POST /tokens { request: ReplayTokenRequest } → { token: ReplayToken }
GET /tokens/{tokenId} → { token: ReplayToken, status }
POST /tokens/{tokenId}/verify { manifest: ReplayManifest } → { valid: bool, details }
GET /manifests/{scanId} → { manifest: ReplayManifest }
POST /manifests { manifest: ReplayManifest } → { manifestId }
GET /healthz | /readyz
```
**Authorization Policies:**
- `replay.token.read` - Read tokens and manifests
- `replay.token.write` - Issue new tokens
---
## 5) Configuration (YAML)
```yaml
Replay:
Authority:
Issuer: "https://authority.stellaops.local"
RequireHttpsMetadata: true
MetadataAddress: "https://authority.stellaops.local/.well-known/openid-configuration"
Audiences: ["replay-service"]
RequiredScopes: ["vuln:operate"]
Storage:
ConnectionString: "Host=postgres;Database=replay;..."
CasEndpoint: "http://rustfs:8080"
Tokens:
Algorithm: "SHA256"
ExpirationHours: 8760 # 1 year
Determinism:
EnforceCanonicalJson: true
HashAlgorithm: "blake3-256"
```
---
## 6) Determinism guarantees
### 6.1 Input pinning
All inputs that affect verdict output are captured:
| Input | Pinning Method | Storage |
|-------|---------------|---------|
| Policy YAML | Content digest | CAS |
| Score policy | Content digest | CAS |
| Feed snapshot | Snapshot digest + timestamp | CAS |
| Toolchain | Image digest | Manifest |
| Analyzers | Version + digest | Manifest |
| Reachability graphs | BLAKE3 hash | CAS |
### 6.2 Output determinism
| Guarantee | Implementation |
|-----------|----------------|
| Canonical JSON | Sorted keys, no whitespace variation |
| Stable ordering | Deterministic sort on all collections |
| UTC timestamps | Microsecond precision, always UTC |
| Hash stability | Same input → same hash |
---
## 7) Security & compliance
* **Token binding**: Tokens are cryptographically bound to input digests
* **Non-repudiation**: DSSE signatures on tokens (optional)
* **Audit trail**: All token operations logged with tenant context
* **Immutability**: Manifests and tokens are append-only
---
## 8) Performance targets
* **Token issuance**: < 50ms P95
* **Token verification**: < 100ms P95
* **Manifest storage**: < 200ms P95
* **Manifest retrieval**: < 50ms P95
---
## 9) Observability
**Metrics:**
* `replay.tokens.issued_total{issuer}`
* `replay.tokens.verified_total{result=valid|invalid}`
* `replay.manifests.stored_total`
* `replay.verification.duration_seconds`
**Tracing:** Spans for token operations, manifest storage, verification workflows.
---
## 10) Testing matrix
* **Determinism tests**: Same inputs produce identical tokens/manifests
* **Round-trip tests**: Store retrieve verify produces same result
* **Hash stability**: Canonical JSON hashing is stable across serialization
* **Integration tests**: Full token lifecycle with Policy Engine
---
## Related Documentation
* Scanner determinism: `../scanner/deterministic-execution.md`
* Policy simulation: `../policy/architecture.md`
* Evidence attestation: `../attestor/architecture.md`
* Replay protocol: `../../replay/DETERMINISTIC_REPLAY.md`

View File

@@ -0,0 +1,325 @@
# 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);
}
```
---
## 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"
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

@@ -0,0 +1,469 @@
# SBOM Lineage Graph Architecture
## Overview
The SBOM Lineage Graph provides a Git-like visualization of container image ancestry with hover-to-proof micro-interactions. It enables auditors and developers to explore SBOM/VEX deltas across artifact versions, turning evidence into an explorable UX.
## Core Concepts
### Lineage Graph
A directed acyclic graph (DAG) where:
- **Nodes** represent artifact versions (SBOM snapshots)
- **Edges** represent relationships between versions
### Edge Types
| Type | Description | Example |
|------|-------------|---------|
| `parent` | Direct version succession | v1.0 → v1.1 of same image |
| `build` | Same CI build produced multiple artifacts | Multi-arch build |
| `base` | Derived from base image | `FROM alpine:3.19` |
### Node Attributes
```
┌─────────────────────────────────────┐
│ Node: sha256:abc123... │
├─────────────────────────────────────┤
│ Artifact: registry/app:v1.2 │
│ Sequence: 42 │
│ Created: 2025-12-28T10:30:00Z │
│ Source: scanner │
├─────────────────────────────────────┤
│ Badges: │
│ • 3 new vulns (🔴) │
│ • 2 resolved (🟢) │
│ • signature ✓ │
├─────────────────────────────────────┤
│ Replay Hash: sha256:def456... │
└─────────────────────────────────────┘
```
## Data Flow
```
┌──────────────┐ ┌─────────────────┐ ┌──────────────────┐
│ Scanner │────▶│ SbomService │────▶│ VexLens │
│ │ │ │ │ │
│ • OCI Parse │ │ • Ledger Store │ │ • Consensus │
│ • Ancestry │ │ • Edge Persist │ │ • Delta Compute │
│ • SBOM Gen │ │ • Diff Engine │ │ • Status Track │
└──────────────┘ └─────────────────┘ └──────────────────┘
│ │ │
└────────────────────┼───────────────────────┘
┌─────────────────┐
│ Lineage API │
│ │
│ • Graph Query │
│ • Diff Compute │
│ • Export Pack │
└─────────────────┘
┌─────────────────┐
│ Frontend UI │
│ │
│ • Lane View │
│ • Hover Cards │
│ • Compare Mode │
└─────────────────┘
```
## Component Architecture
### 1. OCI Ancestry Extractor (Scanner)
Extracts parent/base image information from OCI manifests.
```csharp
public interface IOciAncestryExtractor
{
ValueTask<OciAncestry> ExtractAncestryAsync(
string imageReference,
CancellationToken cancellationToken);
}
public sealed record OciAncestry(
string ImageDigest,
string? BaseImageDigest,
string? BaseImageRef,
IReadOnlyList<string> LayerDigests,
IReadOnlyList<OciHistoryEntry> History);
public sealed record OciHistoryEntry(
string CreatedBy,
DateTimeOffset Created,
bool EmptyLayer);
```
**Implementation Notes:**
- Parse OCI image config `history` field
- Extract `FROM` instruction from first non-empty layer
- Handle multi-stage builds by tracking layer boundaries
- Fall back to layer digest heuristics when history unavailable
### 2. Lineage Edge Repository (SbomService)
Persists relationships between artifact versions.
```csharp
public interface ISbomLineageEdgeRepository
{
ValueTask<LineageEdge> AddAsync(
LineageEdge edge,
CancellationToken cancellationToken);
ValueTask<IReadOnlyList<LineageEdge>> GetChildrenAsync(
string parentDigest,
Guid tenantId,
CancellationToken cancellationToken);
ValueTask<IReadOnlyList<LineageEdge>> GetParentsAsync(
string childDigest,
Guid tenantId,
CancellationToken cancellationToken);
ValueTask<LineageGraph> GetGraphAsync(
string artifactDigest,
Guid tenantId,
int maxDepth,
CancellationToken cancellationToken);
}
public sealed record LineageEdge(
Guid Id,
string ParentDigest,
string ChildDigest,
LineageRelationship Relationship,
Guid TenantId,
DateTimeOffset CreatedAt);
public enum LineageRelationship
{
Parent,
Build,
Base
}
```
### 3. VEX Delta Repository (Excititor)
Tracks VEX status changes between artifact versions.
```csharp
public interface IVexDeltaRepository
{
ValueTask<VexDelta> AddAsync(
VexDelta delta,
CancellationToken cancellationToken);
ValueTask<IReadOnlyList<VexDelta>> GetDeltasAsync(
string fromDigest,
string toDigest,
Guid tenantId,
CancellationToken cancellationToken);
ValueTask<IReadOnlyList<VexDelta>> GetDeltasByCveAsync(
string cve,
Guid tenantId,
int limit,
CancellationToken cancellationToken);
}
public sealed record VexDelta(
Guid Id,
string FromArtifactDigest,
string ToArtifactDigest,
string Cve,
VexStatus FromStatus,
VexStatus ToStatus,
VexDeltaRationale Rationale,
string ReplayHash,
string? AttestationDigest,
Guid TenantId,
DateTimeOffset CreatedAt);
public sealed record VexDeltaRationale(
string Reason,
string? EvidenceLink,
IReadOnlyDictionary<string, string> Metadata);
```
### 4. SBOM-Verdict Link Repository (SbomService)
Links SBOM versions to VEX consensus decisions.
```csharp
public interface ISbomVerdictLinkRepository
{
ValueTask LinkAsync(
SbomVerdictLink link,
CancellationToken cancellationToken);
ValueTask<IReadOnlyList<SbomVerdictLink>> GetVerdictsBySbomAsync(
Guid sbomVersionId,
Guid tenantId,
CancellationToken cancellationToken);
ValueTask<IReadOnlyList<SbomVerdictLink>> GetSbomsByCveAsync(
string cve,
Guid tenantId,
int limit,
CancellationToken cancellationToken);
}
public sealed record SbomVerdictLink(
Guid SbomVersionId,
string Cve,
Guid ConsensusProjectionId,
VexStatus VerdictStatus,
decimal ConfidenceScore,
Guid TenantId,
DateTimeOffset LinkedAt);
```
### 5. Lineage Graph Service (SbomService)
Orchestrates lineage queries and diff computation.
```csharp
public interface ILineageGraphService
{
ValueTask<LineageGraphResponse> GetLineageAsync(
string artifactDigest,
Guid tenantId,
LineageQueryOptions options,
CancellationToken cancellationToken);
ValueTask<LineageDiffResponse> GetDiffAsync(
string fromDigest,
string toDigest,
Guid tenantId,
CancellationToken cancellationToken);
ValueTask<LineageCompareResponse> CompareAsync(
string digestA,
string digestB,
Guid tenantId,
CancellationToken cancellationToken);
}
public sealed record LineageQueryOptions(
int MaxDepth = 10,
bool IncludeVerdicts = true,
bool IncludeBadges = true);
```
## Database Schema
### sbom_lineage_edges
```sql
CREATE TABLE sbom_lineage_edges (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
parent_digest TEXT NOT NULL,
child_digest TEXT NOT NULL,
relationship TEXT NOT NULL CHECK (relationship IN ('parent', 'build', 'base')),
tenant_id UUID NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (parent_digest, child_digest, tenant_id)
);
CREATE INDEX idx_lineage_edges_parent ON sbom_lineage_edges(parent_digest, tenant_id);
CREATE INDEX idx_lineage_edges_child ON sbom_lineage_edges(child_digest, tenant_id);
CREATE INDEX idx_lineage_edges_created ON sbom_lineage_edges(tenant_id, created_at DESC);
```
### vex_deltas
```sql
CREATE TABLE vex_deltas (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
from_artifact_digest TEXT NOT NULL,
to_artifact_digest TEXT NOT NULL,
cve TEXT NOT NULL,
from_status TEXT NOT NULL,
to_status TEXT NOT NULL,
rationale JSONB NOT NULL DEFAULT '{}',
replay_hash TEXT NOT NULL,
attestation_digest TEXT,
tenant_id UUID NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (from_artifact_digest, to_artifact_digest, cve, tenant_id)
);
CREATE INDEX idx_vex_deltas_to ON vex_deltas(to_artifact_digest, tenant_id);
CREATE INDEX idx_vex_deltas_cve ON vex_deltas(cve, tenant_id);
CREATE INDEX idx_vex_deltas_created ON vex_deltas(tenant_id, created_at DESC);
```
### sbom_verdict_links
```sql
CREATE TABLE sbom_verdict_links (
sbom_version_id UUID NOT NULL,
cve TEXT NOT NULL,
consensus_projection_id UUID NOT NULL,
verdict_status TEXT NOT NULL,
confidence_score DECIMAL(5,4) NOT NULL,
tenant_id UUID NOT NULL,
linked_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (sbom_version_id, cve, tenant_id)
);
CREATE INDEX idx_verdict_links_cve ON sbom_verdict_links(cve, tenant_id);
CREATE INDEX idx_verdict_links_projection ON sbom_verdict_links(consensus_projection_id);
```
## API Endpoints
### GET /api/v1/lineage/{artifactDigest}
Returns the lineage graph for an artifact.
**Response:**
```json
{
"artifact": "sha256:abc123...",
"nodes": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"digest": "sha256:abc123...",
"artifactRef": "registry/app:v1.2",
"sequenceNumber": 42,
"createdAt": "2025-12-28T10:30:00Z",
"source": "scanner",
"badges": {
"newVulns": 3,
"resolvedVulns": 2,
"signatureStatus": "valid"
},
"replayHash": "sha256:def456..."
}
],
"edges": [
{
"from": "sha256:parent...",
"to": "sha256:abc123...",
"relationship": "parent"
}
]
}
```
### GET /api/v1/lineage/diff
Returns component and VEX diffs between two versions.
**Query Parameters:**
- `from` - Source artifact digest
- `to` - Target artifact digest
**Response:**
```json
{
"sbomDiff": {
"added": [
{"purl": "pkg:npm/lodash@4.17.21", "version": "4.17.21", "license": "MIT"}
],
"removed": [
{"purl": "pkg:npm/lodash@4.17.20", "version": "4.17.20", "license": "MIT"}
],
"versionChanged": [
{"purl": "pkg:npm/axios@1.6.0", "fromVersion": "1.5.0", "toVersion": "1.6.0"}
]
},
"vexDiff": [
{
"cve": "CVE-2024-1234",
"fromStatus": "affected",
"toStatus": "not_affected",
"reason": "Component removed",
"evidenceLink": "/evidence/abc123"
}
],
"reachabilityDiff": [
{
"cve": "CVE-2024-5678",
"fromStatus": "reachable",
"toStatus": "unreachable",
"pathsRemoved": 3,
"gatesAdded": ["auth_required"]
}
],
"replayHash": "sha256:ghi789..."
}
```
### POST /api/v1/lineage/export
Exports evidence pack for artifact(s).
**Request:**
```json
{
"artifactDigests": ["sha256:abc123..."],
"includeAttestations": true,
"sign": true
}
```
**Response:**
```json
{
"downloadUrl": "/exports/pack-xyz.zip",
"bundleDigest": "sha256:bundle...",
"expiresAt": "2025-12-28T11:30:00Z"
}
```
## Caching Strategy
### Hover Card Cache (Valkey)
- **Key:** `lineage:hover:{tenantId}:{artifactDigest}`
- **TTL:** 5 minutes
- **Invalidation:** On new SBOM version or VEX update
- **Target:** <150ms response time
### Compare Cache (Valkey)
- **Key:** `lineage:compare:{tenantId}:{digestA}:{digestB}`
- **TTL:** 10 minutes
- **Invalidation:** On new VEX data for either artifact
## Determinism Guarantees
1. **Node Ordering:** Sorted by `sequenceNumber DESC`, then `createdAt DESC`
2. **Edge Ordering:** Sorted by `(from, to, relationship)` lexicographically
3. **Component Diff:** Components sorted by `purl` (ordinal)
4. **VEX Diff:** Sorted by `cve` (ordinal)
5. **Replay Hash:** SHA256 of deterministically serialized inputs
## Security Considerations
1. **Tenant Isolation:** All queries scoped by `tenant_id`
2. **Digest Validation:** Verify artifact digest format before queries
3. **Rate Limiting:** Apply per-tenant rate limits on graph queries
4. **Export Authorization:** Verify `lineage:export` scope for pack generation
## Metrics
| Metric | Type | Description |
|--------|------|-------------|
| `sbom_lineage_graph_queries_total` | Counter | Graph queries by tenant |
| `sbom_lineage_diff_latency_seconds` | Histogram | Diff computation latency |
| `sbom_lineage_hover_cache_hits_total` | Counter | Hover card cache hits |
| `sbom_lineage_export_size_bytes` | Histogram | Evidence pack sizes |
| `vex_deltas_created_total` | Counter | VEX deltas stored |
## Error Handling
| Error Code | Description | HTTP Status |
|------------|-------------|-------------|
| `LINEAGE_NOT_FOUND` | Artifact not in lineage graph | 404 |
| `LINEAGE_DEPTH_EXCEEDED` | Max depth limit reached | 400 |
| `LINEAGE_DIFF_INVALID` | Same digest for from/to | 400 |
| `LINEAGE_EXPORT_TOO_LARGE` | Pack exceeds size limit | 413 |

View File

@@ -0,0 +1,319 @@
-- SBOM Lineage Graph Database Schema
-- Version: 1.0.0
-- Created: 2025-12-28
-- ============================================================================
-- TABLE: sbom_lineage_edges
-- Purpose: Stores relationships between SBOM versions (parent/child, build, base)
-- ============================================================================
CREATE TABLE IF NOT EXISTS sbom_lineage_edges (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Edge endpoints (using artifact digest as stable identifier)
parent_digest TEXT NOT NULL,
child_digest TEXT NOT NULL,
-- Relationship type
relationship TEXT NOT NULL CHECK (relationship IN ('parent', 'build', 'base')),
-- Tenant isolation
tenant_id UUID NOT NULL,
-- Audit
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
-- Prevent duplicate edges
CONSTRAINT uq_lineage_edge UNIQUE (parent_digest, child_digest, tenant_id)
);
-- Index for traversing from parent to children
CREATE INDEX IF NOT EXISTS idx_lineage_edges_parent
ON sbom_lineage_edges(parent_digest, tenant_id);
-- Index for traversing from child to parents
CREATE INDEX IF NOT EXISTS idx_lineage_edges_child
ON sbom_lineage_edges(child_digest, tenant_id);
-- Index for time-based queries
CREATE INDEX IF NOT EXISTS idx_lineage_edges_created
ON sbom_lineage_edges(tenant_id, created_at DESC);
-- Index for relationship filtering
CREATE INDEX IF NOT EXISTS idx_lineage_edges_relationship
ON sbom_lineage_edges(tenant_id, relationship);
COMMENT ON TABLE sbom_lineage_edges IS 'Stores directed edges between SBOM versions representing lineage relationships';
COMMENT ON COLUMN sbom_lineage_edges.parent_digest IS 'SHA256 digest of parent artifact';
COMMENT ON COLUMN sbom_lineage_edges.child_digest IS 'SHA256 digest of child artifact';
COMMENT ON COLUMN sbom_lineage_edges.relationship IS 'Type of relationship: parent (version succession), build (same CI build), base (FROM instruction)';
-- ============================================================================
-- TABLE: vex_deltas
-- Purpose: Tracks VEX status changes between artifact versions
-- ============================================================================
CREATE TABLE IF NOT EXISTS vex_deltas (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Artifact pair
from_artifact_digest TEXT NOT NULL,
to_artifact_digest TEXT NOT NULL,
-- Vulnerability
cve TEXT NOT NULL,
-- Status transition
from_status TEXT NOT NULL,
to_status TEXT NOT NULL,
-- Explanation
rationale JSONB NOT NULL DEFAULT '{}',
-- Determinism
replay_hash TEXT NOT NULL,
-- Signed attestation reference (if signed)
attestation_digest TEXT,
-- Tenant isolation
tenant_id UUID NOT NULL,
-- Audit
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
-- Prevent duplicate deltas
CONSTRAINT uq_vex_delta UNIQUE (from_artifact_digest, to_artifact_digest, cve, tenant_id)
);
-- Index for querying deltas by target artifact
CREATE INDEX IF NOT EXISTS idx_vex_deltas_to
ON vex_deltas(to_artifact_digest, tenant_id);
-- Index for querying deltas by CVE
CREATE INDEX IF NOT EXISTS idx_vex_deltas_cve
ON vex_deltas(cve, tenant_id);
-- Index for time-based queries
CREATE INDEX IF NOT EXISTS idx_vex_deltas_created
ON vex_deltas(tenant_id, created_at DESC);
-- Index for finding status transitions
CREATE INDEX IF NOT EXISTS idx_vex_deltas_status
ON vex_deltas(tenant_id, from_status, to_status);
-- GIN index for rationale JSON queries
CREATE INDEX IF NOT EXISTS idx_vex_deltas_rationale
ON vex_deltas USING GIN (rationale);
COMMENT ON TABLE vex_deltas IS 'Tracks VEX status changes between artifact versions with rationale';
COMMENT ON COLUMN vex_deltas.rationale IS 'JSON object with reason, evidenceLink, and metadata';
COMMENT ON COLUMN vex_deltas.replay_hash IS 'SHA256 hash of inputs for deterministic replay verification';
COMMENT ON COLUMN vex_deltas.attestation_digest IS 'SHA256 digest of signed delta verdict attestation';
-- ============================================================================
-- TABLE: sbom_verdict_links
-- Purpose: Links SBOM versions to VEX consensus decisions
-- ============================================================================
CREATE TABLE IF NOT EXISTS sbom_verdict_links (
-- SBOM version reference
sbom_version_id UUID NOT NULL,
-- Vulnerability
cve TEXT NOT NULL,
-- Consensus reference
consensus_projection_id UUID NOT NULL,
-- Verdict snapshot
verdict_status TEXT NOT NULL,
confidence_score DECIMAL(5,4) NOT NULL CHECK (confidence_score >= 0 AND confidence_score <= 1),
-- Tenant isolation
tenant_id UUID NOT NULL,
-- Audit
linked_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
-- Composite primary key
PRIMARY KEY (sbom_version_id, cve, tenant_id)
);
-- Index for querying by CVE
CREATE INDEX IF NOT EXISTS idx_verdict_links_cve
ON sbom_verdict_links(cve, tenant_id);
-- Index for querying by consensus projection
CREATE INDEX IF NOT EXISTS idx_verdict_links_projection
ON sbom_verdict_links(consensus_projection_id);
-- Index for time-based queries
CREATE INDEX IF NOT EXISTS idx_verdict_links_linked
ON sbom_verdict_links(tenant_id, linked_at DESC);
-- Index for finding specific statuses
CREATE INDEX IF NOT EXISTS idx_verdict_links_status
ON sbom_verdict_links(tenant_id, verdict_status);
COMMENT ON TABLE sbom_verdict_links IS 'Links SBOM versions to VEX consensus decisions for traceability';
COMMENT ON COLUMN sbom_verdict_links.confidence_score IS 'Consensus confidence score (0.0-1.0)';
-- ============================================================================
-- TABLE: vex_consensus_projections (migrated from in-memory VexLens)
-- Purpose: Persistent storage for VEX consensus projections
-- ============================================================================
CREATE TABLE IF NOT EXISTS vex_consensus_projections (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Target
vulnerability_id TEXT NOT NULL,
product_key TEXT NOT NULL,
-- Tenant isolation
tenant_id UUID NOT NULL,
-- Consensus result
status TEXT NOT NULL,
confidence_score DECIMAL(5,4) NOT NULL CHECK (confidence_score >= 0 AND confidence_score <= 1),
outcome TEXT NOT NULL,
-- Statistics
statement_count INT NOT NULL DEFAULT 0,
conflict_count INT NOT NULL DEFAULT 0,
-- Timestamps
computed_at TIMESTAMPTZ NOT NULL,
stored_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
-- History linkage
previous_projection_id UUID REFERENCES vex_consensus_projections(id),
status_changed BOOLEAN NOT NULL DEFAULT FALSE
);
-- Unique constraint for latest projection per (vuln, product, tenant, time)
CREATE UNIQUE INDEX IF NOT EXISTS idx_consensus_unique
ON vex_consensus_projections(tenant_id, vulnerability_id, product_key, computed_at);
-- Index for finding status changes
CREATE INDEX IF NOT EXISTS idx_consensus_status_changed
ON vex_consensus_projections(tenant_id, status_changed, computed_at DESC)
WHERE status_changed = TRUE;
-- Index for history traversal
CREATE INDEX IF NOT EXISTS idx_consensus_previous
ON vex_consensus_projections(previous_projection_id)
WHERE previous_projection_id IS NOT NULL;
-- Index for product queries
CREATE INDEX IF NOT EXISTS idx_consensus_product
ON vex_consensus_projections(product_key, tenant_id);
COMMENT ON TABLE vex_consensus_projections IS 'Persistent VEX consensus projections with full history';
COMMENT ON COLUMN vex_consensus_projections.outcome IS 'Consensus outcome: Unanimous, Majority, Plurality, ConflictResolved, NoData';
COMMENT ON COLUMN vex_consensus_projections.status_changed IS 'True if status differs from previous projection';
-- ============================================================================
-- EXTENSION: Add replay_hash to sbom_snapshots (alter existing table)
-- ============================================================================
-- Note: This ALTER should be applied to existing sbom_snapshots table
-- ALTER TABLE sbom_snapshots ADD COLUMN IF NOT EXISTS replay_hash TEXT;
-- CREATE INDEX IF NOT EXISTS idx_sbom_snapshots_replay ON sbom_snapshots(replay_hash) WHERE replay_hash IS NOT NULL;
-- ============================================================================
-- FUNCTIONS: Helper functions for lineage queries
-- ============================================================================
-- Function to get lineage depth from a starting node
CREATE OR REPLACE FUNCTION get_lineage_depth(
p_artifact_digest TEXT,
p_tenant_id UUID,
p_max_depth INT DEFAULT 10
) RETURNS INT AS $$
DECLARE
v_depth INT := 0;
v_current_count INT;
BEGIN
WITH RECURSIVE lineage AS (
SELECT child_digest, 1 as depth
FROM sbom_lineage_edges
WHERE parent_digest = p_artifact_digest AND tenant_id = p_tenant_id
UNION ALL
SELECT e.child_digest, l.depth + 1
FROM sbom_lineage_edges e
JOIN lineage l ON e.parent_digest = l.child_digest
WHERE e.tenant_id = p_tenant_id AND l.depth < p_max_depth
)
SELECT COALESCE(MAX(depth), 0) INTO v_depth FROM lineage;
RETURN v_depth;
END;
$$ LANGUAGE plpgsql STABLE;
-- Function to get all ancestors of an artifact
CREATE OR REPLACE FUNCTION get_ancestors(
p_artifact_digest TEXT,
p_tenant_id UUID,
p_max_depth INT DEFAULT 10
) RETURNS TABLE (
ancestor_digest TEXT,
depth INT,
relationship TEXT
) AS $$
BEGIN
RETURN QUERY
WITH RECURSIVE ancestors AS (
SELECT parent_digest, 1 as depth, e.relationship
FROM sbom_lineage_edges e
WHERE child_digest = p_artifact_digest AND tenant_id = p_tenant_id
UNION ALL
SELECT e.parent_digest, a.depth + 1, e.relationship
FROM sbom_lineage_edges e
JOIN ancestors a ON e.child_digest = a.parent_digest
WHERE e.tenant_id = p_tenant_id AND a.depth < p_max_depth
)
SELECT parent_digest, ancestors.depth, ancestors.relationship
FROM ancestors
ORDER BY depth, parent_digest;
END;
$$ LANGUAGE plpgsql STABLE;
-- Function to get all descendants of an artifact
CREATE OR REPLACE FUNCTION get_descendants(
p_artifact_digest TEXT,
p_tenant_id UUID,
p_max_depth INT DEFAULT 10
) RETURNS TABLE (
descendant_digest TEXT,
depth INT,
relationship TEXT
) AS $$
BEGIN
RETURN QUERY
WITH RECURSIVE descendants AS (
SELECT child_digest, 1 as depth, e.relationship
FROM sbom_lineage_edges e
WHERE parent_digest = p_artifact_digest AND tenant_id = p_tenant_id
UNION ALL
SELECT e.child_digest, d.depth + 1, e.relationship
FROM sbom_lineage_edges e
JOIN descendants d ON e.parent_digest = d.child_digest
WHERE e.tenant_id = p_tenant_id AND d.depth < p_max_depth
)
SELECT child_digest, descendants.depth, descendants.relationship
FROM descendants
ORDER BY depth, child_digest;
END;
$$ LANGUAGE plpgsql STABLE;
COMMENT ON FUNCTION get_lineage_depth IS 'Returns the maximum depth of descendants from an artifact';
COMMENT ON FUNCTION get_ancestors IS 'Returns all ancestor artifacts up to max_depth';
COMMENT ON FUNCTION get_descendants IS 'Returns all descendant artifacts up to max_depth';

View File

@@ -0,0 +1,71 @@
# component_architecture_symbols.md - **Stella Ops Symbols** (2025Q4)
> Symbol resolution and debug information management.
> **Scope.** Library and service architecture for **Symbols**: managing debug symbols, build IDs, and symbol-to-package mappings for reachability analysis.
---
## 0) Mission & boundaries
**Mission.** Provide **symbol resolution infrastructure** for native binary analysis. Map symbols to packages, manage debug information, and support stripped binary analysis.
**Boundaries.**
* Symbols **resolves and maps** symbols; execution is handled by Scanner.
* Debug symbols are **optional**; stripped binaries use heuristics.
* Supports **offline symbol stores** for air-gapped operation.
---
## 1) Solution & project layout
```
src/Symbols/
├─ StellaOps.Symbols/ # Core symbol resolution
│ ├─ Services/
│ │ ├─ ISymbolResolver.cs
│ │ └─ BuildIdResolver.cs
│ └─ Models/
│ ├─ SymbolInfo.cs
│ └─ BuildIdMapping.cs
└─ Integration with Scanner:
└─ src/Scanner/__Libraries/StellaOps.Scanner.Symbols.Native/
```
---
## 2) Contracts & data model
### 2.1 Symbol Info
```json
{
"symbolId": "sha256:abc123",
"name": "_ZN3foo3barEv",
"demangledName": "foo::bar()",
"sourceFile": "src/foo.cpp",
"lineNumber": 42,
"buildId": "abc123def456",
"packagePurl": "pkg:deb/debian/libfoo@1.2.3"
}
```
### 2.2 Build ID Mapping
```json
{
"buildId": "abc123def456",
"path": "/usr/lib/libfoo.so.1",
"packagePurl": "pkg:deb/debian/libfoo@1.2.3",
"debugPath": "/usr/lib/debug/.build-id/ab/c123def456.debug"
}
```
---
## Related Documentation
* Scanner native analysis: `../scanner/architecture.md`
* Reachability: `../../reachability/`

View File

@@ -0,0 +1,74 @@
# component_architecture_timelineindexer.md - **Stella Ops TimelineIndexer** (2025Q4)
> Timeline event indexing and query service.
> **Scope.** Implementation-ready architecture for **TimelineIndexer**: indexing and querying timeline events for vulnerability findings, scans, and policy evaluations.
---
## 0) Mission & boundaries
**Mission.** Provide **fast, indexed access** to timeline events across all StellaOps services. Enable efficient querying of vulnerability history, scan timelines, and policy evaluation trails.
**Boundaries.**
* TimelineIndexer **indexes events**; it does not generate them.
* Events are received from **event streams** (NATS, Valkey).
* Supports **time-range queries** with filtering.
---
## 1) Solution & project layout
```
src/TimelineIndexer/StellaOps.TimelineIndexer/
├─ StellaOps.TimelineIndexer.Core/ # Event models, indexing logic
├─ StellaOps.TimelineIndexer.Infrastructure/ # Storage adapters
├─ StellaOps.TimelineIndexer.WebService/ # Query API
├─ StellaOps.TimelineIndexer.Worker/ # Event consumer
└─ StellaOps.TimelineIndexer.Tests/
```
---
## 2) External dependencies
* **PostgreSQL** - Event storage with time-series indexes
* **NATS/Valkey** - Event stream consumption
* **Authority** - Authentication
---
## 3) Contracts & data model
### 3.1 TimelineEvent
```json
{
"eventId": "evt-2025-01-15-abc123",
"eventType": "scan.completed",
"timestamp": "2025-01-15T10:30:00Z",
"tenantId": "tenant-xyz",
"subjectId": "image:sha256:abc123",
"payload": { /* event-specific data */ }
}
```
---
## 4) REST API
```
GET /timeline?subject={id}&from={date}&to={date} → { events[] }
GET /timeline/{eventId} → { event }
GET /timeline/stats?subject={id} → { statistics }
GET /healthz | /readyz | /metrics
```
---
## Related Documentation
* Signals: `../signals/architecture.md`
* Scanner: `../scanner/architecture.md`

View File

@@ -1,5 +1,11 @@
# StellaOps Console UI
**Status:** Implemented
**Source:** `src/Web/StellaOps.Web/`
**Owner:** UI Guild
> **Related:** See [`../web/`](../web/) for triage-specific UX documentation (Smart-Diff, Triage Canvas, Risk Dashboard).
The Console presents operator dashboards for scans, policies, VEX evidence, runtime posture, and admin workflows.
## Latest updates (2025-11-30)

View File

@@ -0,0 +1,70 @@
# component_architecture_unknowns.md - **Stella Ops Unknowns** (2025Q4)
> Unknown component and symbol tracking registry.
> **Scope.** Library architecture for **Unknowns**: tracking unresolved components, symbols, and mappings that Scanner and other analyzers cannot definitively identify.
---
## 0) Mission & boundaries
**Mission.** Provide a **structured registry** for tracking unknown components, unresolved symbols, and incomplete mappings. Enable visibility into coverage gaps and guide future enhancement priorities.
**Boundaries.**
* Unknowns is a **library layer** consumed by Scanner and Signals.
* Unknowns **does not** guess identities. It records what cannot be determined.
* All unknowns are **categorized** for actionability.
---
## 1) Solution & project layout
```
src/Unknowns/
├─ __Libraries/
│ ├─ StellaOps.Unknowns.Core/ # Unknown models, categorization
│ ├─ StellaOps.Unknowns.Persistence/ # Storage abstractions
│ └─ StellaOps.Unknowns.Persistence.EfCore/
└─ __Tests/
├─ StellaOps.Unknowns.Core.Tests/
└─ StellaOps.Unknowns.Persistence.Tests/
```
---
## 2) Contracts & data model
### 2.1 Unknown Record
```json
{
"unknownId": "unk-2025-01-15-abc123",
"category": "symbol_unmapped",
"context": {
"scanId": "scan-xyz",
"binaryPath": "/usr/lib/libfoo.so",
"symbolName": "_ZN3foo3barEv"
},
"reason": "No PURL mapping available",
"firstSeen": "2025-01-15T10:30:00Z",
"occurrences": 42
}
```
### 2.2 Categories
| Category | Description |
|----------|-------------|
| `component_unidentified` | Binary without package mapping |
| `symbol_unmapped` | Symbol without PURL resolution |
| `version_ambiguous` | Multiple version candidates |
| `purl_invalid` | Malformed package URL |
---
## Related Documentation
* Scanner: `../scanner/architecture.md`
* Signals: `../signals/architecture.md`

View File

@@ -4,6 +4,8 @@
**Source:** `src/Web/`
**Owner:** UI Guild
> **Note:** This folder documents triage-specific frontend features. For the comprehensive Web UI architecture, see [`../ui/architecture.md`](../ui/architecture.md).
## Purpose
Web provides the Angular 17 single-page application (SPA) frontend for StellaOps. Delivers the user interface for vulnerability exploration, policy management, scan results, SBOM visualization, and administrative functions.

View File

@@ -0,0 +1,105 @@
# component_architecture_web.md - **Stella Ops Web** (2025Q4)
> Angular 17 frontend SPA for StellaOps console.
> **Scope.** Frontend architecture for **Web**: the Angular 17 single-page application providing the StellaOps console interface.
> **Note:** For the comprehensive Web UI architecture including all features, see [`../ui/architecture.md`](../ui/architecture.md). This file provides a condensed overview.
---
## 0) Mission & boundaries
**Mission.** Provide an **intuitive, responsive web interface** for StellaOps operators to manage scans, review findings, configure policies, and monitor system health.
**Boundaries.**
* Web is a **frontend-only** application. All data comes from backend APIs.
* Web **does not** store sensitive data locally beyond session tokens.
* Supports **offline-first** patterns for air-gapped console access.
---
## 1) Solution & project layout
```
src/Web/StellaOps.Web/
├─ src/
│ ├─ app/
│ │ ├─ core/ # Core services, guards, interceptors
│ │ │ ├─ services/
│ │ │ ├─ guards/
│ │ │ └─ interceptors/
│ │ ├─ shared/ # Shared components, pipes, directives
│ │ │ ├─ components/
│ │ │ ├─ pipes/
│ │ │ └─ directives/
│ │ ├─ features/ # Feature modules
│ │ │ ├─ dashboard/
│ │ │ ├─ scans/
│ │ │ ├─ findings/
│ │ │ ├─ policies/
│ │ │ ├─ settings/
│ │ │ └─ admin/
│ │ └─ app.routes.ts
│ ├─ assets/
│ ├─ environments/
│ └─ styles/
├─ angular.json
├─ package.json
└─ tsconfig.json
```
---
## 2) Technology stack
* **Framework**: Angular 17 with standalone components
* **State management**: NgRx Signals
* **UI components**: Angular Material
* **HTTP**: Angular HttpClient with interceptors
* **Routing**: Angular Router with lazy loading
* **Build**: Angular CLI with esbuild
---
## 3) Key features
| Feature | Path | Description |
|---------|------|-------------|
| Dashboard | `/dashboard` | Overview metrics and alerts |
| Scans | `/scans` | Scan history and details |
| Findings | `/findings` | Vulnerability findings list |
| Compare | `/compare` | Diff view between scans |
| Policies | `/policies` | Policy configuration |
| Settings | `/settings` | User and system settings |
---
## 4) Authentication
* **OIDC/OAuth2** via Authority module
* **Token refresh** handled by interceptor
* **Session timeout** with configurable duration
---
## 5) Configuration
```typescript
// environment.ts
export const environment = {
production: false,
apiUrl: 'http://localhost:5000/api/v1',
authorityUrl: 'http://localhost:5001',
clientId: 'stellaops-web'
};
```
---
## Related Documentation
* UI module: `../ui/architecture.md`
* Authority: `../authority/architecture.md`
* Auth smoke tests: `../ui/operations/auth-smoke.md`