Exception Governance Migration Guide
Audience: operators and platform engineers cutting a tenant over from legacy suppression/notification rules to unified Exception Governance.
Imposed rule: all exceptions must be time-bound, tenant-scoped, and auditable; legacy perpetual suppressions are prohibited after cutover.
This guide explains how to migrate from legacy suppressions and notifications to the unified Exception Governance model in Excititor and the Stella Ops Console. Follow the phases in order — each one is reversible until you decommission the legacy paths in phase 5.
1. What changes
- Unified exception object: replaces ad-hoc suppressions. Fields:
tenant,scope(purl/image/component),vuln(CVE/alias),justification,expiration,owner,evidence_refs,policy_binding,status(draft/staged/active/expired). - Two-phase activation:
draft → staged → activewith policy simulator snapshot; rollbacks produce a compensating exception markedsupersedes. - Notifications: move from broad email hooks to route-specific notifications (policy events, expiring exceptions) using Notify service templates.
- Auditability: each lifecycle change emits Timeline + Evidence Locker entries; exports include DSSE attestation of the exception set.
2. Migration phases
- Inventory legacy suppressions
- Export current suppressions and notification rules (per tenant) to NDJSON.
- Classify by scope: package, image, repo, tenant-wide.
- Normalize and enrich
- Map each suppression to the unified schema; add
expiration(default 30/90 days),owner,justification(use VEX schema categories when available). - Attach evidence references (ticket URL, VEX claim ID, scan report digest) where missing.
- Map each suppression to the unified schema; add
- Create staged exceptions
- Import NDJSON via Console or
stella exceptions import --stage(CLI guide:docs/modules/cli/guides/exceptions.md). - Run policy simulator; resolve conflicts flagged by Aggregation-Only Contract (AOC) enforcement.
- Import NDJSON via Console or
- Activate with guardrails
- Promote staged → active in batches; each promotion emits Timeline events and optional Rekor-backed attestation bundle (if Attestor is enabled).
- Configure Notify templates for expiring exceptions (T‑14/T‑3 days) and denied promotions.
- Decommission legacy paths
- Disable legacy suppression writes; keep read-only for 30 days with banner noting deprecation.
- Remove legacy notification hooks after confirming staged/active parity.
3. Data shapes
Import NDJSON record (minimal):
{ "tenant": "...", "vuln": "CVE-...", "scope": { "type": "purl|image|component", "value": "..." }, "justification": "...", "expiration": "...", "owner": "..." }
Export manifest:
{ "generated_at": "...", "tenant": "...", "count": 0, "sha256": "...", "aoc_enforced": true, "source": "migration-legacy-suppressions" }
Attestation (optional): DSSE over the exception-set digest, stored alongside the manifest in Evidence Locker.
4. Rollback plan
- Keep legacy suppressions read-only for 30 days.
- If a promotion batch causes regressions, mark affected exceptions
expiredand re-enable corresponding legacy suppressions for that tenant only. - Emit
rollback_noticeTimeline events and Notify operators.
5. Air-gap considerations
- Imports/exports are file-based (NDJSON + manifest); no external calls required.
- Verification uses bundled attestations; Rekor proofs are optional offline.
- Console shows AOC badge when Aggregation-Only Contract limits apply; exports record
aoc=true.
6. Checklists
- [ ] All legacy suppressions exported to NDJSON per tenant.
- [ ] Every exception has justification, owner, expiration.
- [ ] Policy simulator run and results attached to exception batch.
- [ ] Notify templates enabled for expiring/denied promotions.
- [ ] Legacy write paths disabled; read-only banner present.
- [ ] Attestation bundle stored (if Attestor available) and Evidence Locker entry created.
