Data Retention Policy

Retention schedules, disposal methods, and legal hold procedures.

NIST controls:SI-12, MP-6, AU-11
Last updated:2026-03-15
Category:Governance
# Data Retention Policy **Document ID**: KD-POL-004 **Owner**: Chief Technology Officer **Approved By**: CEO, Arivaran **Effective Date**: 2026-03-28 **Next Review Date**: 2027-03-28 (recomputed 2026-10-02 on the stated semi-annual cycle from the Effective Date; the 2026-09-28 occurrence was not recorded, and this date does not assert that a review has taken place) **Review Cycle**: Semi-annual **Classification**: Internal ## 1. Purpose This policy defines how KuiperDesk retains, ages, and deletes customer backup data and system data. KuiperDesk uses content-addressed, deduplicated block storage where a single block may be referenced by multiple backup snapshots. Blocks are stored with S3 Object Lock in COMPLIANCE mode (WORM -- Write Once, Read Many) to guarantee immutability for the duration of each snapshot's retention period. Deletion is not a simple file removal but a snapshot-level mark-and-sweep garbage collection process. This policy ensures data is retained for as long as needed, protected from premature deletion or modification, and deleted reliably when retention expires. ## 2. Scope This policy covers: - **Customer backup blocks**: Content-addressed blocks stored in S3-compatible object storage, keyed by SHA-256 hash, protected by S3 Object Lock COMPLIANCE mode where the backend is S3-compatible. Depending on deployment profile, the storage backend is Arivaran-managed S3, Customer-provided ("BYOS") S3, or (on-prem/satellite deployments) a local filesystem/XFS+VDO or CubeFS+WORM gateway target. We no longer operate SeaweedFS for this path. - **Hash metadata**: Block location mappings and WORM lock state in PostgreSQL, managed by aaHashSvc. On the default managed deployment this is a per-node CNPG database (geo-local, one instance per product mesh node); satellite deployments run their own CNPG instance. - **Backup catalog**: Snapshot records, volume info, endpoint tier configuration, and digital twin state in PostgreSQL, managed by aaTwinSvc. This is a **PgEdge/Spock multi-master mesh across the product's mesh nodes** for the shared control-plane database only; per-service data (including the backup catalog itself) is geo-local CNPG, not globally replicated. We do not use Citus for this data; earlier internal design notes referencing Citus are stale and some schema comments retain "Citus-distribution friendly" phrasing as a naming convention only — there is no live Citus deployment. - **Agent SQLite databases**: Per-backup index databases stored in S3-compatible storage - **Audit events**: Append-only, hash-chained audit log in PostgreSQL. A verification function (`verify_audit_hash_chain`) exists in the schema (migration 009); see Section 3.7 for the current, corrected state of scheduled verification and monthly archival. - **System logs**: Application logs and monitoring data in Loki and VictoriaMetrics - **PKI material**: Certificates, CRLs, and CA chain data in OpenBao > **Terminology note**: this document previously described the default managed deployment as a "hub" with satellites reporting to it. The product's data-path architecture was migrated to a **v3 node-to-node mesh** (no hub-fronted relay in the agent-to-backend data path by default) — see the KuiperDesk architecture memory for detail. References below to "hub" have been corrected to "the default managed deployment" / "product mesh node(s)"; "satellite" remains accurate as a real, distinct self-hosted deployment mode. ## 3. Policy Statements ### 3.1 Endpoint Tier System and WORM Retention 3.1.1. Each endpoint is assigned a tier that determines its retention schedule, immutability window, and WORM lock duration. Tier configuration is stored in the `endpoint_tier_defaults` reference table in PostgreSQL (geo-local CNPG per product mesh node, satellites run their own CNPG instance) and enforced by database constraints. 3.1.2. Endpoint tiers and their retention parameters: | Tier | Immutable Hours | Max Retention Days | Daily Keep | Weekly Keep | Monthly Keep | Yearly Keep | Use Case | |------|----------------|-------------------|------------|-------------|--------------|-------------|----------| | Starter | 72 | 90 | 7 | 0 | 3 | 0 | Individual users, small teams | | Business | 72 | 365 | 14 | 4 | 6 | 0 | Small-to-mid business | | Enterprise | 720 (30 days) | 1,095 (3 years) | 30 | 8 | 12 | 3 | Regulated industries | | Sovereign | 8,760 (1 year) | 3,650 (10 years) | 90 | 12 | 36 | 10 | Government, defense, FedRAMP | 3.1.3. **Immutable hours** define the minimum period after backup completion during which a snapshot cannot be deleted by any user, including tenant administrators. This protects against ransomware that compromises admin credentials and attempts to delete backups. 3.1.4. **Max retention days** define the maximum S3 Object Lock COMPLIANCE duration applied to blocks referenced by snapshots in that tier. S3 Object Lock in COMPLIANCE mode prevents deletion by any principal, including the storage system root account. 3.1.5. The minimum configurable `max_retention_days` is 7 and the minimum `immutable_hours` is 24. These floors are enforced by a PostgreSQL trigger on the `endpoint_tier_defaults` table (migration 009). Attempts to set values below these minimums are rejected at the database level. 3.1.6. Tier changes are applied prospectively. Existing snapshots retain their original retention parameters. A tier upgrade (e.g., Starter to Enterprise) extends future retention. A tier downgrade cannot shorten retention on existing snapshots due to the WORM immutability guarantee enforced by a database trigger that prevents reduction of `retention_expires_at`. ### 3.2 Backup Block Lifecycle 3.2.1. **Block Creation**: When aaagent uploads a backup block via aaStorSvc, aaHashSvc checks if the SHA-256 hash already exists in PostgreSQL CNPG. If the block is new, it is stored in S3-compatible storage with an S3 Object Lock COMPLIANCE retention set to the endpoint's `max_retention_days`, and the hash plus S3 location are registered in the `hash_metadata` table. If the block already exists, a new location reference is added (deduplication). Only COMPLIANCE mode is used; GOVERNANCE mode is explicitly prohibited because it permits privileged users to shorten or remove retention. 3.2.2. **Block WORM Lock Extension**: The WormWorker background process in aaHashSvc runs on a configurable interval (default: 300 seconds). Each cycle it queries the maximum `retention_expires_at` across all snapshots referencing a given tenant's blocks, then extends S3 Object Lock retention for any block whose current `lock_until` is shorter than required. This ensures blocks remain locked for as long as any snapshot references them. 3.2.3. **Public Block Protection**: Blocks with scope `p` (public/shared dedup blocks) are locked with an extended retention period (configurable, default 100 years) to ensure they are never prematurely deleted. 3.2.4. **Block Retention**: A block is retained as long as any non-expired snapshot references it. The `lock_until` column in `hash_metadata` tracks the S3 Object Lock expiration. Blocks under active WORM lock cannot be deleted even if all snapshot references are removed -- the S3 Object Lock prevents physical deletion until the lock expires. 3.2.5. **Snapshot Expiration**: When a backup snapshot's `retention_expires_at` passes and the immutability window has elapsed, the snapshot becomes eligible for garbage collection. The associated SQLite index database in S3 is queued for deletion. ### 3.3 Snapshot-Level Mark-and-Sweep Garbage Collection Deletion of deduplicated blocks follows a snapshot-level mark-and-sweep process. There are no per-block reference counts; instead, garbage collection determines block liveness by scanning active snapshots. 3.3.1. **Mark Phase**: The GC process enumerates all non-expired snapshots and builds the set of live block hashes by reading the SQLite index databases. Any block hash not present in this live set is marked as a candidate for deletion. 3.3.2. **Sweep Phase**: Candidate blocks are checked against S3 Object Lock. If the block's COMPLIANCE lock has expired (`lock_until < now()`), the block is deleted from S3 storage and the `hash_metadata` row is removed from PostgreSQL. If the lock is still active, the block is skipped and will be retried in the next GC cycle. 3.3.3. **WORM Delete Prevention**: aaTwinSvc enforces a hard block on snapshot deletion when `retention_expires_at` is in the future. The `DeleteSnapshot` RPC returns `FAILED_PRECONDITION` and emits the `kuiperdesk.twinsvc.worm.delete_blocked_total` counter. This prevents any API caller, including compromised admin accounts, from deleting backup data under WORM hold. 3.3.4. **Safety Margins**: The GC process runs every 6 hours. Blocks are not deleted in the same GC cycle they are first identified as candidates. A minimum 24-hour dwell time between marking and sweeping prevents race conditions with concurrent backup uploads. Reducing the dwell time below 24 hours requires CTO approval and a risk assessment. ### 3.4 S3 Object Lock Configuration 3.4.1. All S3 buckets used for backup block storage must have S3 Object Lock enabled at bucket creation time with a default retention mode of COMPLIANCE. The WormWorker verifies this at startup and refuses to start if Object Lock is not enabled, emitting the `kuiperdesk.hashsvc.worm.bucket_lock_enabled` gauge as 0. 3.4.2. GOVERNANCE mode is prohibited. Only COMPLIANCE mode locks are issued. This is enforced in the S3Client library which does not expose a GOVERNANCE mode parameter. 3.4.3. S3 Object Lock retention dates can only be extended, never shortened. This is enforced by S3 itself (COMPLIANCE mode rejects retention shortening with HTTP 403) and verified by the WormWorker which only issues `putObjectRetention` calls with dates equal to or later than the current lock. 3.4.4. Legal hold locks (`s3:PutObjectLegalHold`) are available for litigation hold scenarios (see Section 3.8). Legal holds are independent of retention dates and prevent deletion even after the COMPLIANCE retention expires. ### 3.5 Erasure SLA 3.5.1. When a tenant requests complete data erasure (account deletion or GDPR Article 17 right to erasure), all tenant data shall be erased within 30 calendar days, subject to WORM lock constraints. 3.5.2. The erasure process: 1. All active snapshots for the tenant are marked as expired immediately in PostgreSQL 2. The mark-and-sweep GC identifies tenant-specific blocks no longer referenced by any active snapshot 3. For blocks whose S3 Object Lock COMPLIANCE retention has expired, physical deletion from S3 proceeds immediately 4. For blocks still under active COMPLIANCE lock, deletion is deferred until the lock expires. The tenant is notified that physical erasure of these blocks will complete when the WORM retention period ends 5. Tenant catalog data in PostgreSQL (geo-local CNPG per product mesh node, satellites run their own CNPG instance) is deleted (hard delete, not soft) 6. Tenant Keycloak Organization is disabled and scheduled for deletion 7. A completion certificate is generated confirming the erasure scope, completion date, and any deferred blocks with their expected deletion dates 3.5.3. WORM-locked blocks that cannot be immediately deleted are documented in the erasure certificate. This is an inherent property of COMPLIANCE mode Object Lock and is disclosed in the Data Processing Agreement (DPA). Crypto-erasure (see KD-POL-011 Media Sanitization Policy) may be applied to render the blocks unreadable before the lock expires. 3.5.4. Erasure requests are logged in the tenant's Forgejo issue tracker with the requestor, date, scope, and completion status. This log is retained for 3 years for regulatory compliance even after tenant data is deleted. ### 3.6 System Data Retention | Data Type | Retention Period | Storage | Justification | |-----------|-----------------|---------|---------------| | Application logs (Loki) | 90 days | VPS-3 | Operational troubleshooting | | Audit logs (auditd, Keycloak) | 12 months online, 3 years archived | Loki on VPS-3 (online), S3 Object Lock (archive) | SOC 2 audit evidence, FedRAMP AU-11 | | Audit events (hash-chained) | 12 months online, 3 years archived to S3 Object Lock | PostgreSQL (PgEdge/CNPG) + S3 | FedRAMP AU-9, AU-11, tamper evidence | | Metrics (VictoriaMetrics) | 12 months | VPS-3 | Capacity planning, SLA reporting | | CI/CD pipeline logs | 90 days | Forgejo | Change management evidence | | Certificate revocation lists | Indefinite | OpenBao | PKI integrity | | Incident records | 3 years | Forgejo | Regulatory and legal | | Access review records | 3 years | Forgejo | SOC 2 audit evidence | | WORM compliance metrics | 12 months | VictoriaMetrics on VPS-3 | WORM anomaly detection, audit evidence | 3.6.1. System log retention periods are minimums. Logs may be retained longer if required for an active incident investigation or legal hold. 3.6.2. **Corrected 2026-07 (#11596)**: the `audit_events`/`audit_archives` schema (append-only table + immutability triggers) exists in aaTwinSvc's database (migration 009), but as of this correction **no scheduled worker in aaTwinSvc performs the monthly export/hash/upload described above** — the trigger-level immutability protections in Section 3.7.3 are live, but the archival *pipeline* is not yet wired to a scheduler for this table. A comparable archival worker pattern (select-expired / hash / upload / record, AU-11-aligned) is implemented and scheduled for **aaM365Svc's own, separate** `m365_audit_log`/`m365_audit_archives` tables (M365-specific audit data only — not a substitute for the aaTwinSvc-wide audit trail). Standing up the aaTwinSvc-wide monthly archival job is tracked alongside the broader audit-event-catalog work (ticket #11595). ### 3.7 Audit Event Integrity 3.7.1. All audit events are stored in the `audit_events` table with a hash chain: each event's `event_hash` is computed over the event content and the previous event's hash (`prev_hash`). This provides tamper evidence -- any modification to a historical event breaks the chain. 3.7.2. **Corrected 2026-07 (#11596)**: the `verify_audit_hash_chain` database function (migration 009) exists and can be invoked to walk the hash chain and report broken links. As of this correction, **it is not yet invoked on a weekly (or any) schedule** — we found no Kubernetes CronJob or equivalent scheduler wiring it up. Chain-break handling as a P1 security incident per KD-POL-003 (Incident Response Policy) applies once a verification run is scheduled and surfaced to AlertManager; until then, treat scheduled integrity verification as a planned control, not an operating one. Standing this up is tracked in ticket #11595 (audit-event-catalog work), which the Incident Response Policy's related P1 trigger (Section "Audit hash chain break") also depends on. 3.7.3. The `audit_events` table has UPDATE and DELETE privileges revoked from all application service roles. Immutability triggers provide a second layer of protection at the database trigger level. ### 3.8 Legal Hold 3.8.1. When Arivaran receives a legal hold notice or is aware of pending litigation involving tenant data, the retention policy is suspended for the affected data. Affected snapshots are flagged as "legal hold" in PostgreSQL (geo-local CNPG per product mesh node, satellites run their own CNPG instance) and are excluded from expiration processing. Additionally, S3 Legal Hold is applied to the affected blocks, preventing deletion even after COMPLIANCE retention expires. 3.8.2. Legal holds are approved by the CEO and documented with the scope, reason, and expected duration. They are reviewed quarterly. ## 4. WORM Monitoring and Alerting ### 4.1 WORM Metrics Emitted by Services The following metrics are emitted by aaHashSvc and aaTwinSvc and scraped by VictoriaMetrics via VMAgent: | Metric | Type | Source | Description | |--------|------|--------|-------------| | `kuiperdesk.hashsvc.worm.blocks_unprotected` | Gauge | aaHashSvc WormWorker | Count of blocks in `hash_metadata` where `lock_until IS NULL`. Should converge to 0. | | `kuiperdesk.hashsvc.worm.locks_extended_total` | Counter | aaHashSvc WormWorker | Cumulative count of successful S3 Object Lock extensions, by `tenant_id`. | | `kuiperdesk.hashsvc.worm.locks_failed_total` | Counter | aaHashSvc WormWorker | Cumulative count of failed S3 putObjectRetention calls, by `tenant_id`. | | `kuiperdesk.hashsvc.worm.bucket_lock_enabled` | Gauge | aaHashSvc WormWorker | 1 if the S3 bucket has Object Lock enabled, 0 if not. Emitted at startup. | | `kuiperdesk.twinsvc.worm.delete_blocked_total` | Counter | aaTwinSvc | Count of `DeleteSnapshot` requests rejected due to active WORM retention hold, by `tenant_id`. | ### 4.2 Required AlertManager Rules The SRE team shall deploy the following AlertManager rules to the VPS-3 monitoring stack. These rules consume the WORM metrics above and are required for SOC 2 CC7.2 (monitoring of security-relevant events) and FedRAMP AU-6 (audit review, analysis, and reporting). **Rule 1: Unprotected Blocks Alert (P1)** ```yaml - alert: WormBlocksUnprotected expr: kuiperdesk_hashsvc_worm_blocks_unprotected > 0 for: 30m labels: severity: critical annotations: summary: "WORM: {{ $value }} blocks have no S3 Object Lock" description: > Blocks without Object Lock are not protected from deletion. The WormWorker should converge this to 0 within one cycle. If this persists for 30 minutes, WormWorker may be stuck or the S3 putObjectRetention calls are failing. runbook: "Check aaHashSvc logs for WormWorker errors. Verify S3 bucket Object Lock is enabled. Check kuiperdesk_hashsvc_worm_locks_failed_total for failures." compliance: "SOC 2 CC6.1, FedRAMP SC-28" ``` **Rule 2: Lock Extension Failures Alert (P2)** ```yaml - alert: WormLockExtensionFailures expr: rate(kuiperdesk_hashsvc_worm_locks_failed_total[15m]) > 0 for: 15m labels: severity: warning annotations: summary: "WORM: S3 Object Lock extension failures for tenant {{ $labels.tenant_id }}" description: > putObjectRetention calls are failing. Blocks may not have their retention extended before the current lock expires. Check S3 endpoint health, IAM permissions, and bucket Object Lock status. runbook: "Verify S3 connectivity from aaHashSvc pod. Check bucket policy allows s3:PutObjectRetention. Review aaHashSvc logs for HTTP status codes." compliance: "SOC 2 CC7.2, FedRAMP SC-28" ``` **Rule 3: Bucket Object Lock Disabled (P1)** ```yaml - alert: WormBucketLockDisabled expr: kuiperdesk_hashsvc_worm_bucket_lock_enabled == 0 for: 1m labels: severity: critical annotations: summary: "WORM: S3 bucket Object Lock is NOT enabled -- all WORM protection is ineffective" description: > WormWorker detected that the S3 bucket does not have Object Lock enabled. The worker has refused to start. All putObjectRetention calls would silently fail. This is a P1 compliance incident. runbook: "IMMEDIATE: Verify bucket configuration. Object Lock can only be enabled at bucket creation time. If the bucket was recreated without Object Lock, data must be migrated to a new Object Lock-enabled bucket." compliance: "SOC 2 CC6.1, FedRAMP SC-28, FedRAMP AU-9" ``` **Rule 4: WORM Delete Attempts Alert (P3)** ```yaml - alert: WormDeleteBlocked expr: rate(kuiperdesk_twinsvc_worm_delete_blocked_total[1h]) > 5 for: 10m labels: severity: warning annotations: summary: "WORM: Elevated snapshot deletion attempts blocked ({{ $value }}/hr) for tenant {{ $labels.tenant_id }}" description: > More than 5 snapshot deletion attempts per hour are being blocked by WORM retention holds. This may indicate a compromised admin account attempting to delete backups (ransomware exfiltration pattern) or a misconfigured automation script. runbook: "Review aaTwinSvc audit logs for the tenant. Check Keycloak session logs for the requesting user. If compromised credentials are suspected, escalate to P1 per KD-POL-003." compliance: "SOC 2 CC7.2, FedRAMP IR-4" ``` **Rule 5: WormWorker Not Running Alert (P1)** ```yaml - alert: WormWorkerDown expr: absent(kuiperdesk_hashsvc_worm_blocks_unprotected) == 1 for: 20m labels: severity: critical annotations: summary: "WORM: WormWorker metric absent -- worker may be down" description: > The blocks_unprotected gauge has not been emitted for 20 minutes. The WormWorker may have crashed or aaHashSvc may be down entirely. Without the WormWorker, new blocks will not receive S3 Object Lock protection and existing locks will not be extended. runbook: "Check aaHashSvc pod status. Check for WormWorker startup failure in logs (bucket Object Lock check). Restart aaHashSvc pod if needed." compliance: "SOC 2 CC7.2, FedRAMP AU-6" ``` ### 4.3 Storage Capacity Monitoring for WORM Data (FedRAMP AU-4) 4.3.1. S3-compatible storage backends used for WORM-protected backup blocks must be monitored for capacity to ensure audit and backup data is not lost due to storage exhaustion. This is required by FedRAMP AU-4 (Audit Log Storage Capacity). 4.3.2. **Current limitation**: The S3BlockStore implementation returns `UINT64_MAX` for `AvailableBytes()` and `0` for `UsedBytes()` because S3-compatible storage does not expose a fixed capacity through the S3 API. These sentinel values are not usable for capacity monitoring. 4.3.3. **Required capacity monitoring approach** (**corrected 2026-07, #11596**: the mechanism below previously named SeaweedFS-specific endpoints; the product no longer operates SeaweedFS for this path — see Section 2. This remains a "shall implement," not-yet-operating requirement per 4.3.2, now stated against the current backends): The SRE team shall implement capacity monitoring appropriate to the backend actually in use for a given deployment (Arivaran-managed/BYOS S3, on-prem XFS+VDO, or CubeFS+WORM gateway): - **Backend-native capacity metrics**: for CubeFS+WORM, scrape CubeFS's master/volume capacity API; for XFS+VDO, monitor the underlying VDO logical/physical extent usage. - **Underlying disk monitoring**: `node_exporter` on storage nodes shall expose `node_filesystem_avail_bytes` for the mount points backing object/block storage. - **Per-tenant usage tracking**: aaHashSvc shall periodically compute per-tenant storage usage by summing `block_size` from `hash_metadata` grouped by `tenant_id`, and emit a `kuiperdesk.hashsvc.tenant_storage_used_bytes` gauge. 4.3.4. **Required capacity AlertManager rules**: The SRE team shall deploy: ```yaml - alert: WormStorageCapacityWarning expr: (node_filesystem_avail_bytes{mountpoint="<backend-data-mount>"} / node_filesystem_size_bytes{mountpoint="<backend-data-mount>"}) < 0.20 for: 5m labels: severity: warning annotations: summary: "WORM storage below 20% free on {{ $labels.instance }}" description: "Storage backing WORM-protected blocks is running low. WORM blocks cannot be deleted before their retention expires. Plan capacity expansion." compliance: "FedRAMP AU-4" - alert: WormStorageCapacityCritical expr: (node_filesystem_avail_bytes{mountpoint="<backend-data-mount>"} / node_filesystem_size_bytes{mountpoint="<backend-data-mount>"}) < 0.05 for: 5m labels: severity: critical annotations: summary: "WORM storage below 5% free on {{ $labels.instance }} -- imminent write failure" description: "Storage is nearly full. New backup blocks may fail to write. WORM-locked blocks prevent deletion. Immediate capacity expansion required." compliance: "FedRAMP AU-4" ``` <!-- `<backend-data-mount>` corrected 2026-07 (#11596): replace with the real mount point for the backend in use (e.g. the CubeFS data dir or the XFS+VDO logical volume mount) — the previous example hardcoded `/opt/weed-data`, a SeaweedFS-specific path the product no longer uses. --> 4.3.5. When storage capacity falls below 10%, the SRE team shall initiate capacity expansion procedures. WORM-protected blocks cannot be deleted to free space; therefore, capacity planning must account for the maximum aggregate retention duration across all tenants. ## 5. Roles and Responsibilities | Role | Responsibility | |------|---------------| | CTO | Approve retention tier changes. Approve erasure SLA exceptions. Oversee GC and WORM process reliability. | | SRE / Infrastructure Engineers | Deploy and maintain AlertManager rules for WORM metrics. Operate the GC process. Monitor storage capacity. Execute erasure requests. Respond to WORM alerts. | | Tenant Administrators | Configure endpoint tier for their organization (within policy limits). Submit erasure requests. | ## 6. Compliance Mapping | Policy Statement | SOC 2 Criteria | ISO 27001:2022 | GDPR | FedRAMP | |-----------------|----------------|----------------|------|---------| | 3.1 Endpoint Tier / WORM Retention | CC6.5 | A.8.10 | Art. 5(1)(e) | SC-28 | | 3.2 Block Lifecycle | CC6.1, CC6.5 | A.8.10 | Art. 5(1)(e) | SC-28 | | 3.3 Mark-and-Sweep GC | CC6.5 | A.8.10 | Art. 5(1)(e) | -- | | 3.4 S3 Object Lock | CC6.1 | A.8.10, A.8.24 | -- | SC-28, AU-9 | | 3.5 Erasure SLA | CC6.5 | A.8.10 | Art. 17 | -- | | 3.6 System Data Retention | CC7.1 | A.8.10 | Art. 5(1)(e) | AU-11 | | 3.7 Audit Integrity | CC7.1 | A.8.15 | -- | AU-9 | | 3.8 Legal Hold | CC6.5 | A.5.31 | Art. 17(3) | -- | | 4.2 WORM AlertManager Rules | CC7.2 | A.8.16 | -- | AU-6 | | 4.3 Capacity Monitoring | CC7.1 | A.8.6 | -- | AU-4 | ## 7. Exceptions Exceptions to retention periods (extending or shortening) require CTO approval with documented justification. Exceptions to the 30-day erasure SLA require CEO approval and customer notification. WORM retention periods cannot be shortened once applied due to S3 COMPLIANCE mode; this is by design and no exception process exists for shortening active WORM locks. ## 8. Enforcement Automated enforcement operates at multiple layers: 1. **S3 Object Lock (COMPLIANCE mode)**: The primary enforcement mechanism. Physical deletion is blocked by the S3 storage engine until the COMPLIANCE retention date passes. No principal, including root/admin, can override this. 2. **Database triggers**: The `trg_snapshot_worm` trigger prevents shortening `retention_expires_at`. The `trg_validate_tier_defaults` trigger enforces minimum retention floors. 3. **Application-level guards**: aaTwinSvc `DeleteSnapshot` RPC rejects deletion of WORM-held snapshots. aaHashSvc WormWorker extends locks proactively. 4. **Mark-and-sweep GC**: The GC process is the only authorized deletion path. Manual deletion outside the GC process is prohibited. 5. **AlertManager monitoring**: WORM metric anomalies trigger alerts per Section 4.2. ## 9. Revision History | Version | Date | Author | Changes | |---------|------|--------|---------| | 1.0 | 2026-03-19 | CTO | Initial release | | 2.0 | 2026-03-28 | CTO | Replace TiKV references with PostgreSQL (PgEdge on hub, CNPG on satellites). Replace refcount model with snapshot-level mark-and-sweep GC. Add WORM/S3 Object Lock sections (3.2-3.4). Add endpoint tier system (3.1). Add audit integrity section (3.7). Add WORM monitoring and AlertManager rules (Section 4). Add storage capacity monitoring requirements (4.3). Add FedRAMP compliance mapping. References KD-POL-011 for media sanitization. | | 2.1 | 2026-07-09 | Engineering (ticket #11596, fact-correction pass) | Removed stale SeaweedFS/Citus/"hub" storage-topology references (Section 2) — corrected to actual backends (S3-compatible, XFS+VDO, CubeFS+WORM) and the v3 node-to-node mesh (PgEdge/Spock control-plane mesh + geo-local per-node CNPG). Corrected §3.6.2 and §3.7.2: the monthly audit-archival worker and weekly `verify_audit_hash_chain` schedule described in the prior version are **not currently operating** for the aaTwinSvc-wide audit trail (schema/function exist; scheduler wiring does not) — flagged as a planned, not operating, control and tracked in #11595. This is an accuracy correction; no retention/WORM/GC/erasure enforcement behavior described elsewhere in this policy changed. |

This document is part of the Arivaran Twin compliance program. For questions or the latest version, contact compliance@arivaran.ai.

Release-ready. Saved on this browser.