DORA RoI Retention Policy
Status: implemented operator policy for sprint SPRINT_20260430_124_DOCS_dora_roi_runbook.md.
Purpose
This document defines the Evidence Locker retention behavior expected for annual DORA Register of Information (RoI) snapshots. It is operator-facing: financial-entity tenants own their filing, legal review, and effective retention setting. Stella supplies a conservative default, bounded tenant overrides, immutable storage, and audit evidence.
The governing local decision is DORA-Q6 in the EU compliance decisions log.
Policy
| Setting | Value |
|---|---|
| Policy id | dora-roi-annual-v1 |
| Default retention | 7 years |
| Tenant override | Allowed |
| Minimum override | 5 years |
| Maximum override | 10 years |
| Override audit | Required |
| Shortening rule | Do not shorten retention below an already committed regulatory or customer period without counsel approval. |
Retention is expressed in years at the operator-policy layer. Implementations may persist an absolute retainUntil timestamp or days for storage backends, but the resolved period must not be shorter than the approved year value.
Resolution Order
When an annual RoI snapshot is sealed, the effective retention is resolved in this order:
- Active legal hold or compliance hold, if present.
- Tenant DORA RoI retention override in the inclusive 5 to 10 year range.
- Platform default of 7 years.
Invalid override values must be rejected. Do not silently clamp 4 to 5 or 12 to 10; the operator must correct the tenant policy and produce an audit record.
Snapshot Metadata
Every annual RoI capsule or bundle should carry these retention fields in its metadata:
{
"retentionPolicyId": "dora-roi-annual-v1",
"retentionYears": 7,
"retainUntil": "2033-12-31T23:59:59Z",
"retentionSource": "platform-default",
"tenantOverride": null,
"legalHold": false,
"previousYearBundleHash": "sha256:<64 lowercase hex>",
"auditEventIds": ["evt-..."]
}
When a tenant override applies, tenantOverride must include:
{
"years": 10,
"previousYears": 7,
"ticket": "DORA-RET-2026-0042",
"reason": "Tenant policy requires ten-year RoI evidence retention",
"requestedBy": "operator@example.com",
"approvedBy": "compliance-owner@example.com",
"approvedAt": "2026-12-15T10:00:00Z",
"auditEventId": "evt-..."
}
Audit Events
Retention override changes and annual snapshot sealing must produce auditable events. The expected event vocabulary is:
| Event | Required fields |
|---|---|
dora.roi.retention.override.set | tenantId, previousYears, newYears, ticket, reason, requestedBy, approvedBy, effectiveAt |
dora.roi.retention.override.cleared | tenantId, previousYears, newYears=7, ticket, reason, requestedBy, approvedBy, effectiveAt |
dora.roi.snapshot.retention.applied | tenantId, reportingYear, bundleHash, retentionYears, retainUntil, retentionSource, overrideAuditEventId |
dora.roi.snapshot.retention.rejected | tenantId, reportingYear, requestedYears, reason, ticket |
The audit payload must allow an offline reviewer to answer who changed the tenant setting, why it changed, which annual snapshot used it, and what retain-until date resulted.
Storage Behavior
- Sealed RoI snapshots are immutable evidence artifacts.
- Object storage retention or filesystem deletion guards must use the resolved
retainUntildate. - Decision Capsule create requests may attach
regulatoryRetentionmetadata fordora-roi-snapshotartifacts. EvidenceLocker resolves the platform default toretentionPolicyId=dora-roi-annual-v1,retentionYears=7, andretainUntil=createdAt+7 yearswhen the request does not provide an override. - Tenant overrides must remain in the 5 to 10 year range and include
overrideAuditEventId; EvidenceLocker rejects override values without that audit reference. - Each new or updated retention row must keep
retainUntilat or beyond the persistedcreatedAt + retentionYearscalendar-year horizon. Leap-day behavior follows .NET/PostgreSQL calendar arithmetic; fixed 365-day multiplication is not permitted. Legal holds and shortening approvals cannot reduce this declared minimum. - Migration
019_regulatory_retention_horizon.sqlenforces that relationship for subsequent writes against the row’s originalcreated_at. The constraint startsNOT VALID; operators must audit historical rows before validating it. - Capsule rows store the existing
evidence_locker.regulatory_retention_recordsreference and API/export metadata exposes retention years, retain-until, legal hold, and override audit event id. - Legal holds override normal retention pruning.
- Purge jobs must skip annual RoI snapshots whose
retainUntilis in the future or whose legal hold is active. - Offline export metadata must include the retention policy id, effective years, retain-until date, and override audit event IDs.
Retention sweep (non-RoI decision capsules)
Sprint SPRINT_20260520_089 adds an automatic background retention sweep for ordinary (non Records-of-Importance) decision capsules, closing the TOPO-110 gap where retention was enforced only at write/seal time.
- Implemented by the
CapsuleRetentionSweepServicehosted service (mirrors Timeline’sAuditRetentionPurgeService). Driven byevidence_locker.sweep_expired_capsules(migration012). - Append-only safe. Expired capsules are not hard-deleted (that would break the content-hash + DSSE provenance chain). Instead the sweep writes a tombstone:
retention_swept_atis stamped and the manifest / assembly-inputs / DSSE payload bytes are replaced with a deterministic tombstone marker, whilecapsule_id,content_hash,tenant_id,region_tag,signing_status, and the regulatory linkage are preserved as the durable audit stub. - Records-of-Importance and held capsules are never swept. A capsule is preserved when its linked retention
retain_untilis in the future, itslegal_hold_untilis active, or its retentionartifact_typeis a protected regulatory class (dora-roi-snapshot,tlpt-evidence-pack). - A capsule with no regulatory retention record is swept only after the configured default window (
EvidenceLocker:RetentionSweep:DefaultRetentionDays, default 3650 days / 10 years) elapses from its creation. - The sweep is disabled by default (
EvidenceLocker:RetentionSweep:Enabled=false) and supports aDryRunmode that reports candidates without tombstoning. Cadence is controlled byInitialDelay/Interval.
Operator Workflow
- Review the tenant DORA retention setting during the annual filing window.
- Keep the default 7 years unless the customer/operator compliance owner approves a 5 to 10 year override.
- Record the override ticket, reason, requester, approver, and effective date.
- Generate the annual RoI bundle with the resolved retention setting.
- Verify that the Evidence Locker record and exported metadata show the same retention years and retain-until date.
- Capture the audit event IDs in the filing ticket.
For command-level filing steps, see the DORA RoI export runbook.
Current Implementation Status
EvidenceLocker implements this slice without adding new external schema assets:
- The baseline
001_v1_evidencelocker_baseline.sql(line 439 onward; folded in from the pre-1.0007_regulatory_audit_retention_ledger.sql) owns the reusable regulatory retention table and the optional Decision Capsule retention reference column. CapsuleServiceresolves DORA RoI default/override metadata, persists the regulatory retention record, and links the capsule row to it.PostgresCapsuleRepositoryreads the linked retention record back with the capsule, exposing retention years, retain-until, legal hold, and override audit event id.POST /api/v1/evidence/capsulesemitsdora.roi.snapshot.retention.appliedwhen DORA RoI retention metadata is applied to a capsule.
