EvidenceLocker
Status: Implemented Source: src/EvidenceLocker/ Owner: Platform Team
Purpose
EvidenceLocker provides sealed, immutable storage for vulnerability-scan evidence and audit logs. It guarantees tamper-evident evidence chains for compliance and forensic analysis through content-addressable storage and cryptographic sealing (Merkle root + DSSE).
Audience: compliance and forensics operators who store, hold, and replay release evidence, and service authors integrating decision capsules, verdict attestations, or portable/air-gapped bundle export.
Components
Services:
StellaOps.EvidenceLocker.WebService- HTTP API for evidence submission and retrieval; also hosts the startup auto-migration, capsule retention-sweep, and legacy-PII audit hosted servicesStellaOps.EvidenceLocker.Worker- Background host. Verifies database connectivity and runs the shared hosted services (migration, retention sweep, legacy-PII audit). It does not perform sealing — sealing is synchronous on the write path.
Libraries:
StellaOps.EvidenceLocker- Composition-root project: HTTP contracts, minimal-API endpoint groups, Decision Capsule pipeline, repository contracts/implementationsStellaOps.EvidenceLocker.Core- Domain model, options, Merkle/signature abstractions, object-store and timeline interfacesStellaOps.EvidenceLocker.Infrastructure- Storage adapters (FileSystem / S3-compatible), persistence + embedded migrations, signers, hosted services__Libraries/StellaOps.EvidenceLocker.Exportand__Libraries/StellaOps.EvidenceLocker.Timestamping- portable/audit bundle export and RFC3161 (re)timestamping (standalone projects).__Libraries/StellaOps.EvidenceLocker.Import/EvidenceBundleImporter.csis a dormant source-only prototype, not a project or supported importer; it has no production composition or storage implementation.__Apps/StellaOps.EvidenceLocker.BackupCli- encrypted backup/restore CLI
API Surface
All routes require Authority OAuth2 bearer auth and a resolved tenant (RequireTenant()). The service FallbackPolicy requires evidence:read, so every route is at minimum read-gated. Scope policies map to: evidence:read → EvidenceRead, evidence:create → EvidenceCreate, evidence:hold → EvidenceHold, export.operator → ExportOperator, export.viewer → ExportViewer (constants in StellaOpsScopes; defined in Program.cs and the *Endpoints.cs files).
Evidence snapshots / gate artifacts (Program.cs):
POST /evidence— ingest producer gate artifact, compute deterministic evidence score (evidence:create)GET /evidence/score?artifact_id=...— get evidence score by artifact id (evidence:read)POST /evidence/snapshot— create an evidence snapshot/bundle (evidence:hold)GET /evidence/{bundleId:guid}— get bundle metadata (evidence:read)GET /evidence/{bundleId:guid}/chain— walk the supersede chain, oldest-first (evidence:read)GET /evidence/{bundleId:guid}/download— download sealed bundle asapplication/gzip(evidence:read)GET /evidence/{bundleId:guid}/portable— download portable/air-gapped bundle (evidence:read)POST /evidence/verify— verify bundle root hash (evidence:read)POST /evidence/hold/{caseId}— create a legal hold (evidence:create)POST /evidence/qa/assurance-cases/{caseId}/readback-import— QA-only readback/import proof (evidence:create)
Decision Capsules (Api/Capsules/CapsuleEndpoints.cs, group /api/v1/evidence/capsules):
POST /— create an unsealed capsule (evidence:create)POST /{capsuleId}/seal— seal via DSSE envelope (evidence:create)POST /{capsuleId}/verify— verify content hash + signature (evidence:read)POST /{capsuleId}/export— export asapplication/zip(evidence:read)POST /{capsuleId}/replay— reconstruct verdict from sealed capsule (evidence:read)POST /tlpt-packs— assemble + sign a DORA TLPT evidence pack capsule (evidence:create)
Bundle export jobs (Api/ExportEndpoints.cs, group /api/v1/bundles):
POST /{bundleId}/export— trigger async export job,202 Accepted(export.operator)GET /{bundleId}/export/{exportId}— poll export status (export.viewer)GET /{bundleId}/export/{exportId}/download— download completed export (export.viewer)
Verdict attestations (Api/VerdictEndpoints.cs, group /api/v1/verdicts):
POST /— store a verdict attestation (evidence:create)GET /{verdictId}— retrieve a verdict (evidence:read)POST /{verdictId}/verify— verify DSSE signature (currently fail-closed; trust roots unavailable) (evidence:read)GET /{verdictId}/envelope— download raw DSSE envelope JSON (evidence:read)GET /api/v1/runs/{runId}/verdicts— list verdicts for a policy run (evidence:read)
Evidence & Audit adapter (Api/EvidenceAuditEndpoints.cs, group /api/v1/evidence, all evidence:read):
GET /(evidence home summary),GET /packs,GET /packs/{id},GET /proofs/{subjectDigest},GET /receipts/cvss/{id}, andGET /audit(deprecated;Sunset 2027-10-19, successor/api/v1/audit/events?modules=evidencelockervia Timeline). Note:proofs,receipts/cvss, andauditcurrently return static in-process fixtures; packs are tenant-scoped viaIPackRepository.
Evidence Threads / Artifact Canonical Record (Api/EvidenceThreadEndpoints.cs, group /api/v1/evidence/thread, evidence:read):
GET /{canonicalId}andGET /?purl=...
EU regulatory artifact ledger (Api/RegulatoryArtifactLedgerEndpoints.cs, group /api/v1/regulatory/artifact-ledger, evidence:read):
GET /— tenant-scoped ledger query (regime/artifactType/reportingYear/cursor;requireMatch=trueis fail-closed)
Operational endpoints (anonymous): GET /healthz, GET /readyz, GET /health/ready (legacy), GET /buildinfo.json, and GET /openapi.
Configuration
Configuration binds to the EvidenceLocker section (EvidenceLockerOptions.SectionName); there is no etc/evidence-locker.yaml.sample shipped in this repo. Sealing is synchronous on the write path (no immediate-vs-batch policy toggle).
Key settings (see StellaOps.EvidenceLocker.Core/Configuration/EvidenceLockerOptions.cs):
EvidenceLocker:Database—ConnectionString(required) andApplyMigrationsAtStartup(defaulttrue)EvidenceLocker:ObjectStore—Kind(FileSystem=1 /AmazonS3=2),EnforceWriteOnce(WORM, defaulttrue), plusFileSystem.RootPathorAmazonS3.{BucketName,Region,ServiceKind,Endpoint,ForcePathStyle,Tls,Prefix,UseIntelligentTiering,ObjectLock}.ServiceKind=S3Compatiblerequires an explicit endpoint, addressing choice, and TLS verification choice; production external endpoints require verified HTTPS.EvidenceLocker:Signing—Enabled,Algorithm(default ES256),KeyId,PayloadType,UseCryptoRegistry(route signing throughICryptoProviderRegistry),AllowEphemeralKeyMaterial,AllowUnsignedCapsules, optionalKeyMaterial(Ed25519/EC) andTimestampingEvidenceLocker:Crypto—HashAlgorithm(default SHA256; also SHA384/SHA512/GOST3411-2012-256/512) andPreferredProviderEvidenceLocker:Quotas— material count/size and metadata limitsEvidenceLocker:Timeline— optional Timeline anchoring (Enabled,Endpoint, auth)EvidenceLocker:RetentionSweep— background sweep of expired non-RoI capsules (Enableddefaultfalse,DryRun,DefaultRetentionDaysdefault 3650,InitialDelay,Interval)EvidenceLocker:Incident— incident-mode retention extensionEvidenceLocker:DataResidency—DefaultRegion,AllowedRegionsEvidenceLocker:Portable— portable bundle artifact/file names
Dependencies
- PostgreSQL (schemas
evidence_lockerfor data +evidence_locker_appfor the RLSrequire_current_tenant()tenant-isolation function; forward-only embedded SQL migrations applied on startup byEvidenceLockerMigrationRunner/EvidenceLockerMigrationHostedService, tracked inevidence_locker.evidence_schema_version) - Object store: filesystem (
ObjectStore:Kind=FileSystem) or the S3 driver (ObjectStore:Kind=AmazonS3). RustFS/MinIO support is conditional on the validatedAmazonS3:ServiceKind=S3Compatibleendpoint/path-style/TLS contract documented inarchitecture.md§5.1; it is not an unqualified compatibility claim for every server version or Object Lock implementation. - StellaOps Cryptography (
ICryptoProviderRegistry, BouncyCastle Ed25519) for in-process bundle/capsule signing — there is no separate Signer service dependency - Authority (OAuth2 resource-server auth;
evidence:read/evidence:create/evidence:holdscopes) - Optional: RFC3161 timestamp authority, Timeline/TimelineIndexer, audit emission
- ExportCenter / Export Center scopes for bundle export (
export.operator/export.viewer)
Related Documentation
- Architecture:
architecture.md - API reference:
api-reference.md - Evidence-bundle contract:
evidence-bundle-v1.md - Encrypted backup & restore:
encrypted-backup-restore.md - Promotion evidence contract:
promotion-evidence-contract.md - DORA RoI retention policy:
dora-roi-retention.md - Portable audit pack — contract:
portable-audit-pack-contract.md; manifest schema:schemas/portable-audit-pack-manifest.v1.schema.json; compatibility:portable-audit-pack-compatibility.md; determinism:portable-audit-pack-determinism.md; Rekor offline:portable-audit-pack-rekor-offline.md; CLI runbook:portable-audit-pack-cli-runbook.md; Parquet profile:portable-audit-pack-parquet-profile.md; verification matrix:portable-audit-pack-test-matrix.md - Export Center:
../export-center/ - Attestor:
../attestor/ - High-Level Architecture:
../../ARCHITECTURE_OVERVIEW.md
Current Status
Implemented with WebService and Worker components. Supports sealed evidence storage with cryptographic verification (Merkle root + DSSE), decision-capsule create/seal/verify/export/replay, verdict attestations, an EU regulatory artifact ledger, evidence-thread (Artifact Canonical Record) reads, and portable/air-gapped bundle download. Bundle export jobs and capsule export integrate with the Export Center scopes (export.operator / export.viewer). The Decision Capsule pipeline enforces PII rejection (CapsulePiiGuard) and data residency on write/read. Verdict-attestation DSSE verification is currently fail-closed (trust roots not yet wired). The /api/v1/evidence/audit slice is deprecated in favour of Timeline’s unified audit store.
