# Verdict Attestation Implementation Status **Sprint**: SPRINT_3000_0100_0001 - Signed Delta-Verdicts **Status**: Phase 1 Complete (Policy Engine + Evidence Locker), Phase 2 Blocked **Last Updated**: 2025-12-23 ## Completed Work ### 1. Policy Engine - Verdict Predicate & Attestation Service ✅ **Location**: `src/Policy/StellaOps.Policy.Engine/Attestation/` #### Files Created: 1. **VerdictPredicate.cs** - Core predicate models - `VerdictPredicate` - Main predicate structure matching JSON schema - `VerdictInfo` - Verdict details (status/severity/score) - `VerdictRuleExecution` - Rule chain execution trace - `VerdictEvidence` - Evidence references (advisories, VEX, SBOM) - `VerdictVexImpact` - VEX impact assessment - `VerdictReachability` - Call graph reachability data - Uses canonical JSON serialization with lexicographic key ordering 2. **VerdictPredicateBuilder.cs** - Predicate assembly service - `Build(PolicyExplainTrace)` - Converts existing trace to attestation predicate - `Serialize(VerdictPredicate)` - Canonical JSON with sorted keys - `ComputeDeterminismHash(VerdictPredicate)` - SHA256 over sorted evidence + verdict - Maps all existing Policy Engine trace data to new attestation format 3. **IVerdictAttestationService.cs** - Service interface - `AttestVerdictAsync(trace)` - Orchestrates attestation creation - `VerdictAttestationRequest` - Request DTO with predicate, subject, metadata - `VerdictAttestationResult` - Response DTO with verdict ID and URI 4. **VerdictAttestationService.cs** - Service implementation - Feature-flagged via `VerdictAttestationOptions.Enabled` - Calls `VerdictPredicateBuilder` to create predicate - Calls `HttpAttestorClient` to request signing - Returns verdict ID for downstream tracking 5. **HttpAttestorClient.cs** - HTTP client for Attestor service - `POST /internal/api/v1/attestations/verdict` - Sends predicate JSON + subject descriptor - Receives signed attestation metadata **Design Decisions**: - Predicate strictly matches `stellaops-policy-verdict.v1.schema.json` - Determinism hash uses sorted evidence digests + verdict triple - Offline-first: no hard dependencies on external services - Feature flag allows gradual rollout ### 2. Evidence Locker - Verdict Storage & API ✅ **Location**: `src/EvidenceLocker/StellaOps.EvidenceLocker/` #### Files Created: 1. **Migrations/001_CreateVerdictAttestations.sql** - Table: `evidence_locker.verdict_attestations` - Columns: verdict_id (PK), tenant_id, run_id, finding_id, policy metadata, verdict triple, envelope (JSONB), digests, rekor_log_index, timestamps - Indexes: run_id, finding_id, tenant+evaluated_at - CHECK constraint on verdict_status enum - Audit trigger: `audit_verdict_attestations_changes` 2. **Storage/IVerdictRepository.cs** - Repository interface - `StoreVerdictAsync(record)` - Upsert verdict attestation - `GetVerdictAsync(verdictId)` - Retrieve full record with envelope - `ListVerdictsForRunAsync(runId, options)` - Query verdicts by run with filtering - `ListVerdictsAsync(tenantId, options)` - Query verdicts by tenant - `CountVerdictsForRunAsync(runId, options)` - Pagination count - Records: `VerdictAttestationRecord`, `VerdictAttestationSummary`, `VerdictListOptions` 3. **Storage/PostgresVerdictRepository.cs** - PostgreSQL implementation - Uses Npgsql + Dapper for data access - JSONB storage for DSSE envelope with GIN index - ON CONFLICT handling for idempotent upserts - Filtering by status/severity with pagination - Tenant isolation via WHERE clauses 4. **Api/VerdictContracts.cs** - API response DTOs - `GetVerdictResponse` - Full verdict with envelope - `ListVerdictsResponse` - Paged list of summaries - `VerdictSummary` - Lightweight verdict metadata - `VerifyVerdictResponse` - Signature verification results - `SignatureVerification`, `RekorVerification` - Crypto details - JSON serialization with snake_case naming 5. **Api/VerdictEndpoints.cs** - Minimal API endpoints - `GET /api/v1/verdicts/{verdictId}` - Retrieve verdict - `GET /api/v1/runs/{runId}/verdicts` - List verdicts for run - `POST /api/v1/verdicts/{verdictId}/verify` - Verify signature (TODO: implementation) - Structured logging for all operations - Error handling with problem details 6. **StellaOps.EvidenceLocker.csproj** - Project file - Dependencies: Npgsql 9.0.3, Dapper 2.1.35, OpenTelemetry, Serilog - Project references: Scheduler.Models, Policy.Engine, Configuration, DependencyInjection, Auth, Telemetry #### Integration Points: 1. **DI Registration** - Updated `EvidenceLockerInfrastructureServiceCollectionExtensions.cs` - Registered `IVerdictRepository` → `PostgresVerdictRepository` - Connection string from `EvidenceLockerOptions.Database.ConnectionString` 2. **WebService Wiring** - Updated `StellaOps.EvidenceLocker.WebService/Program.cs` - Added `using StellaOps.EvidenceLocker.Api` - Called `app.MapVerdictEndpoints()` before `app.Run()` 3. **Project References** - Updated `.csproj` files - `WebService.csproj` → references `StellaOps.EvidenceLocker.csproj` - `Infrastructure.csproj` → references `StellaOps.EvidenceLocker.csproj` - Fixed package versions: Npgsql 9.0.3, Dapper 2.1.35 **Design Decisions**: - PostgreSQL JSONB for envelope storage (queryable + compact) - Separate verdict fields for efficient indexing without JSON extraction - Determinism hash stored for replay verification - Optional Rekor log index for transparency - Repository pattern for testability - Minimal APIs for lightweight endpoints ## Blocked Work ### 3. Attestor - VerdictAttestationHandler ⚠️ BLOCKED **Blocking Issue**: Pre-existing build errors in Attestor dependencies: - `StellaOps.Replay.Core`: Missing YamlDotNet assembly reference - `StellaOps.Attestor.ProofChain`: Missing Envelope namespace, ILogger references **Planned Implementation** (when unblocked): **Location**: `src/Attestor/StellaOps.Attestor/StellaOps.Attestor.WebService/` #### Planned Files: 1. **Handlers/VerdictAttestationHandler.cs** ```csharp public class VerdictAttestationHandler { private readonly IAttestationSigningService _signingService; private readonly IVerdictRepository _verdictRepository; private readonly ITransparencyWitnessClient _transparencyClient; public async Task HandleAsync( VerdictAttestationRequest request, CancellationToken cancellationToken) { // 1. Validate predicate schema // 2. Create AttestationSignRequest with predicate payload // 3. Sign with IAttestationSigningService // 4. Extract DSSE envelope from result // 5. Store in Evidence Locker via IVerdictRepository // 6. Optional: Submit to Rekor via ITransparencyWitnessClient // 7. Return verdict ID + attestation URI } } ``` 2. **Endpoints** - Add to WebService Program.cs - `POST /internal/api/v1/attestations/verdict` - Create verdict attestation - Accepts predicate JSON + subject descriptor - Returns verdict ID + DSSE envelope URI 3. **DI Registration** - Register `VerdictAttestationHandler` as scoped service - Wire up dependencies: signing, storage, transparency **Dependencies**: - ✅ `IAttestationSigningService` - Existing - ✅ `ITransparencyWitnessClient` - Existing - ✅ `IVerdictRepository` - Implemented (Evidence Locker) - ❌ Build environment - **BLOCKED** ## Pending Work ### 4. Policy Engine Integration ⏸️ **Task**: Wire Policy Engine to call VerdictAttestationService after verdict evaluation **Location**: `src/Policy/StellaOps.Policy.Engine/Evaluation/PolicyEvaluator.cs` (or similar) **Changes Needed**: 1. Inject `IVerdictAttestationService` into evaluator 2. After successful policy evaluation, call `AttestVerdictAsync(trace)` 3. Store returned verdict ID in evaluation metadata 4. Ensure async context properly propagated **Depends On**: Task 3 (Attestor Handler) completion ### 5. Unit Tests ⏸️ **Location**: `src/Policy/StellaOps.Policy.Engine/__Tests/` **Files to Create**: 1. **VerdictPredicateBuilderTests.cs** - Test: Schema validation (predicate matches JSON schema) - Test: Determinism hash stability (same input → same hash) - Test: Rule chain mapping accuracy - Test: Evidence digest computation - Test: VEX impact extraction - Test: Reachability data mapping - Test: Canonical JSON serialization (key ordering) 2. **VerdictAttestationServiceTests.cs** - Test: Feature flag disabled (returns null) - Test: Successful attestation flow - Test: HTTP client failure handling - Test: Predicate serialization errors 3. **Integration Test** - Test: End-to-end policy run → verdict attestation → storage → retrieval - Test: Deterministic replay (same inputs → same determinism hash) - Test: Signature verification **Depends On**: Task 3, 4 completion ### 6. CLI Commands ⏸️ **Location**: `src/Cli/StellaOps.Cli/Commands/` **Commands to Add**: 1. `stella verdict get ` - Retrieve verdict attestation 2. `stella verdict verify ` - Verify signature 3. `stella verdict list --run ` - List verdicts for run 4. `stella verdict download --output ` - Download DSSE bundle **Depends On**: Task 3, 4, 5 completion ## Architecture Summary ``` ┌─────────────────┐ │ Policy Engine │ │ - Evaluates │ │ - Generates │ │ Trace │ └────────┬────────┘ │ PolicyExplainTrace ▼ ┌────────────────────────────┐ │ VerdictPredicateBuilder │ │ - Converts Trace │ │ - Computes Determinism │ │ - Serializes Canonical │ └────────┬───────────────────┘ │ VerdictPredicate ▼ ┌─────────────────────────────────┐ │ VerdictAttestationService │ │ - Orchestrates Signing │ │ - Calls Attestor │ └────────┬────────────────────────┘ │ HTTP POST /internal/api/v1/attestations/verdict ▼ ┌────────────────────────────────────┐ │ Attestor │ ⚠️ BLOCKED │ - VerdictAttestationHandler │ │ - IAttestationSigningService │ │ - Creates DSSE Envelope │ └────────┬───────────────────────────┘ │ VerdictAttestationRecord ▼ ┌──────────────────────────────────┐ │ Evidence Locker │ ✅ COMPLETE │ - PostgresVerdictRepository │ │ - Stores Attestations │ │ - Provides Query API │ └──────────────────────────────────┘ ``` ## Next Steps ### Immediate Actions Required: 1. **Fix Attestor Build Errors** (Infrastructure team) - Add missing YamlDotNet package reference to Replay.Core - Fix ProofChain namespace/using issues - Verify all Attestor dependencies compile 2. **Implement VerdictAttestationHandler** (This sprint) - Create handler class in Attestor.WebService - Wire up signing service, storage, transparency - Add endpoint to Program.cs - Test end-to-end flow 3. **Integrate Policy Engine** (This sprint) - Inject attestation service into evaluator - Call after successful verdicts - Store verdict IDs 4. **Write Unit Tests** (This sprint) - VerdictPredicateBuilder schema/determinism tests - Service integration tests - End-to-end replay tests 5. **CLI Commands** (Next sprint) - verdict get/verify/list/download - Interactive verification UI ### Success Criteria: - [ ] Policy runs generate cryptographically-signed verdict attestations - [ ] Verdicts stored in Evidence Locker with DSSE envelopes - [ ] Determinism hashes enable bit-for-bit replay verification - [ ] Optional Rekor anchoring for public auditability - [ ] CLI commands for verdict inspection and verification - [ ] Unit test coverage >80% for new code - [ ] Integration tests validate end-to-end flow ## Technical Debt 1. **EvidenceLockerDataSource Integration** - Current: PostgresVerdictRepository uses connection string directly - Future: Migrate to use EvidenceLockerDataSource.OpenConnectionAsync() - Benefit: Unified session management, tenant scoping 2. **Signature Verification Placeholder** - Current: `VerdictEndpoints.VerifyVerdictAsync` returns placeholder response - Future: Implement actual DSSE signature verification - Dependency: Integrate with Attestor verification service 3. **Pre-existing Attestor Errors** - EvidencePortableBundleService.cs static field access errors - Blocking unrelated to verdict attestation work - Needs separate investigation ## Resources - **JSON Schema**: `docs/schemas/stellaops-policy-verdict.v1.schema.json` - **Documentation**: `docs/policy/verdict-attestations.md` - **Sprint Plan**: `docs/implplan/SPRINT_3000_0100_0001_signed_verdicts.md` - **Evidence Pack Sprint**: `docs/implplan/SPRINT_3000_0100_0002_evidence_packs.md` --- **Status Legend**: - ✅ Complete - ⚠️ Blocked - ⏸️ Pending (blocked by dependencies) - 🔄 In Progress