# Competitor Ingest DB Snapshot Governance (CM3) Status: Draft · Date: 2025-12-04 Scope: Enforce database snapshot governance including versioning, freshness SLA, and rollback procedures for imported external feeds. ## Objectives - Define versioning scheme for imported snapshots. - Establish freshness SLA for external data. - Enable deterministic rollback to previous snapshots. - Support audit trail for all snapshot operations. ## Snapshot Versioning ### Version Scheme ``` {tool}-{timestamp}-{sequence} Examples: - syft-20251204T000000Z-001 - trivy-20251204T120000Z-001 - clair-20251204T060000Z-002 ``` ### Snapshot Record ```json { "id": "syft-20251204T000000Z-001", "tool": "syft", "toolVersion": "1.0.0", "importedAt": "2025-12-04T00:00:00Z", "sourceHash": "b3:...", "normalizedHash": "b3:...", "recordCount": 1234, "state": "active", "previousSnapshot": "syft-20251203T000000Z-001", "metadata": { "sourceUri": "https://example.com/sbom.json", "importUser": "system", "importReason": "scheduled_sync" } } ``` ## Freshness SLA ### Thresholds by Tool | Tool | Max Age | Stale Threshold | Critical Threshold | |------|---------|-----------------|-------------------| | Syft | 7 days | 14 days | 30 days | | Trivy | 7 days | 14 days | 30 days | | Clair | 7 days | 14 days | 30 days | | Custom | Configurable | Configurable | Configurable | ### Freshness States | State | Condition | Action | |-------|-----------|--------| | `fresh` | age < max_age | Normal operation | | `stale` | max_age <= age < critical | Emit warning | | `critical` | age >= critical | Block queries without override | | `expired` | Manual expiry | Data unavailable | ### SLA Monitoring ```json { "sla": { "tool": "syft", "snapshotId": "syft-20251204T000000Z-001", "importedAt": "2025-12-04T00:00:00Z", "age": "P2D", "state": "fresh", "nextCheck": "2025-12-05T00:00:00Z", "thresholds": { "maxAge": "P7D", "stale": "P14D", "critical": "P30D" } } } ``` ## Rollback Procedures ### Rollback Triggers | Trigger | Auto/Manual | Action | |---------|-------------|--------| | Import failure | Auto | Rollback to previous | | Validation failure | Auto | Rollback to previous | | Data corruption | Manual | Rollback to specified | | Compliance requirement | Manual | Rollback to specified | | User request | Manual | Rollback to specified | ### Rollback Workflow ``` ┌─────────────┐ │ Initiate │ │ Rollback │ └─────────────┘ │ ▼ ┌─────────────┐ │ Verify │──Fail──► Abort │ Target │ └─────────────┘ │ Pass │ ▼ ┌─────────────┐ │ Create │ │ Savepoint │ └─────────────┘ │ ▼ ┌─────────────┐ │ Restore │──Fail──► Restore Savepoint │ Snapshot │ └─────────────┘ │ Pass │ ▼ ┌─────────────┐ │ Verify │──Fail──► Restore Savepoint │ Restore │ └─────────────┘ │ Pass │ ▼ ┌─────────────┐ │ Commit │ │ Change │ └─────────────┘ │ ▼ ┌─────────────┐ │ Update │ │ Active │ └─────────────┘ ``` ### Rollback Command ```bash # Rollback to previous snapshot stellaops ingest rollback --tool syft # Rollback to specific snapshot stellaops ingest rollback --tool syft --snapshot-id syft-20251201T000000Z-001 # Dry run stellaops ingest rollback --tool syft --dry-run # Force rollback (skip confirmations) stellaops ingest rollback --tool syft --force ``` ### Rollback Response ```json { "rollback": { "status": "completed", "tool": "syft", "from": { "snapshotId": "syft-20251204T000000Z-001", "recordCount": 1234 }, "to": { "snapshotId": "syft-20251203T000000Z-001", "recordCount": 1200 }, "executedAt": "2025-12-04T12:00:00Z", "executedBy": "admin@example.com", "reason": "Data corruption detected" } } ``` ## Retention Policy ### Snapshot Retention | Category | Retention | Cleanup | |----------|-----------|---------| | Active | Indefinite | Never | | Previous (N-1) | 30 days | Auto | | Archived | 90 days | Auto | | Audit | 1 year | Manual | ### Cleanup Schedule ```json { "retention": { "schedule": "0 0 * * *", "rules": [ { "category": "previous", "maxAge": "P30D", "action": "archive" }, { "category": "archived", "maxAge": "P90D", "action": "delete" } ], "exceptions": [ { "snapshotId": "syft-20251101T000000Z-001", "reason": "Audit hold", "expiresAt": "2026-12-01T00:00:00Z" } ] } } ``` ## Audit Trail ### Audit Events | Event | Fields | Retention | |-------|--------|-----------| | `snapshot_imported` | id, tool, hash, user, timestamp | 1 year | | `snapshot_activated` | id, previous_id, user, timestamp | 1 year | | `snapshot_rolled_back` | from_id, to_id, reason, user | 1 year | | `snapshot_expired` | id, reason, user, timestamp | 1 year | | `snapshot_deleted` | id, reason, user, timestamp | 1 year | ### Audit Record Format ```json { "audit": { "id": "audit-12345", "event": "snapshot_rolled_back", "timestamp": "2025-12-04T12:00:00Z", "user": "admin@example.com", "details": { "fromSnapshot": "syft-20251204T000000Z-001", "toSnapshot": "syft-20251203T000000Z-001", "reason": "Data corruption detected", "recordsAffected": 34 }, "hash": "b3:..." } } ``` ## API Endpoints ### List Snapshots ```http GET /api/v1/ingest/snapshots?tool=syft&state=active ``` ### Get Snapshot Details ```http GET /api/v1/ingest/snapshots/{snapshotId} ``` ### Initiate Rollback ```http POST /api/v1/ingest/snapshots/{snapshotId}/rollback Content-Type: application/json { "reason": "Data corruption detected", "dryRun": false } ``` ### Check SLA Status ```http GET /api/v1/ingest/sla?tool=syft ``` ## Links - Sprint: `docs/implplan/SPRINT_0186_0001_0001_record_deterministic_execution.md` (CM3) - Normalization: `docs/modules/scanner/design/competitor-ingest-normalization.md` (CM1) - Feed Thresholds: `docs/modules/policy/contracts/feed-snapshot-thresholds.md` (SP6)