Skip to content

Archive Retention Enforcement

Version: v1.2
Plan: PLAN-1611
Status: Production-ready


Overview

Archive retention enforcement is a hard compliance gate built into Cesivi's archive subsystem. Every imported item carries a retention record that specifies how long the item must be preserved. While an item is within its retention window, all delete and modify operations are unconditionally blocked — no administrator, service account, or API key can override this gate.

This document covers:


Policy Configuration

Retention policy is set per archive site and applies to all items imported into that site. The policy determines:

Field Default Description
DefaultWindowDays 2555 (7 years) Retention window in calendar days from the anchor date
Anchor ImportDate Which date is used as the start of the retention window
AllowExtension true Whether operators may extend the window via the admin API
PolicyVersion 1 Schema version (increment on policy changes)

Setting the policy via REST

PATCH /_api/archive/retention/policy?siteId={archiveSiteId}
Content-Type: application/json

{
  "DefaultWindowDays": 3650,
  "Anchor": "ItemCreated",
  "AllowExtension": true,
  "PolicyVersion": 1
}

Important: Policy changes apply only to new imports. Existing retention windows are never retroactively shortened. A policy change cannot reduce an already-assigned retention window — the longer of (existing, new) always wins.


Anchor Semantics

The retention window starts from the anchor date. Four anchor modes are available:

Mode Description
ImportDate The date/time the item was imported by the MigrationTool (default; always available)
ItemCreated The item's Created date from the source SharePoint farm
ItemModified The item's Modified date from the source SharePoint farm
CustomField Value from a named DateTime/DateTimeOffset field (specified in CustomFieldName)

If Anchor = CustomField and the field is absent or unparseable, Cesivi falls back to ImportDate and logs a WARN-level message, recording the fallback in the WORM audit event (RetentionAnchorAssigned.FallbackUsed = true).

Example: A legal archive with DefaultWindowDays=3650 and Anchor=ItemCreated means every document is retained for 10 years from its original creation date in the source SharePoint farm.


Hard Enforcement Guarantee

There is no admin override. Retention is a hard compliance gate.

  • No request header bypasses it.
  • No role or service account bypasses it.
  • The only legitimate path to removing an item before its window closes is Legal Hold release (PLAN-A-7, future capability).

What gets blocked

Operation HTTP Surface CSOM Method SOAP Method
Delete list item DELETE /_api/web/lists/.../items(N) ListItem.DeleteObject() Lists.UpdateListItems Cmd=Delete
Recycle list item POST /_api/web/lists/.../items(N)/recycle ListItem.Recycle()
Delete file DELETE /_api/web/getfilebyserverrelativeurl(...) File.DeleteObject()
Recycle file File.Recycle()
Delete folder Folder.DeleteObject()
Recycle folder Folder.Recycle()
Delete attachment DELETE /_api/web/lists/.../items(N)/AttachmentFiles('...')
Modify field PATCH /_api/web/lists/.../items(N) (update item) Lists.UpdateListItems Cmd=Update

HTTP responses on block

Surface Status Code Body
REST 409 Conflict { "error": { "code": "-2147024891", "message": "Item is within its retention window until …" } }
CSOM 500 with CesiviRetentionException in context CSOM error payload
SOAP SOAP fault 0x81020015 SOAP error element with block reason

Extension Workflow

Operators with site-admin privileges may extend a retention window — but never shorten it.

Via REST API

POST /_api/archive/retention/extend
Content-Type: application/json

{
  "SiteId": "00000000-0000-0000-0000-000000000000",
  "ListId": "00000000-0000-0000-0000-000000000001",
  "ItemId": 42,
  "NewUntilUtc": "2034-01-01T00:00:00Z",
  "Reason": "Litigation hold extended by Legal"
}

Response (200 OK):

{ "extended": true, "newUntilUtc": "2034-01-01T00:00:00Z", "oldUntilUtc": "2030-01-01T00:00:00Z" }

Rejection (400 Bad Request — shortening attempt):

{ "error": "New retention date (…) must be later than the current window end (…). Retention windows cannot be shortened." }

Every extension emits a RetentionWindowExtended event to the WORM audit log and broadcasts a RetentionExtended SignalR message to the live-tail dashboard.


Retention Dashboard Tour

Navigate to Cesivi Administration → Archive → Retention.

Header KPIs (top row)

Card Meaning
Total Items Number of items with any retention record in the site
In Retention Items still within their window (cannot be deleted/modified)
Expiring < 30 days Items whose window closes within 30 calendar days (flag for review)
Blocked (24 h) Blocked delete/modify attempts in the past 24 hours

Per-site cards

One card per registered archive farm. Stats mirror the KPIs but scoped to that farm.

Blocked attempts table (live-tail)

The table shows the most recent blocked attempts (up to 100 rows). New rows arrive via SignalR in real time — within ~1 second of the REST rejection. Each row shows:

  • Time — local time of the block
  • Action — Delete, Recycle, ModifyField, etc.
  • Item — canonical item key (last path segment shown; hover for full key)
  • Principal — the caller's login name
  • Surface — REST, CSOM, or SOAP
  • Until — retention window end date (or "list-level" for file/folder operations)

REST API Reference

All endpoints require site-administrator privilege.

GET /_api/archive/retention/status?siteId=…

Returns per-site retention statistics.

Response:

{
  "SiteId": "...",
  "TotalItems": 12500,
  "ItemsInRetention": 11200,
  "ExpiringIn30Days": 47,
  "ExpiringIn90Days": 213,
  "ExpiringIn365Days": 1840,
  "BlockedAttemptsLast24h": 3,
  "AsOf": "2026-05-27T10:00:00Z"
}

GET /_api/archive/retention/items?siteId=…&untilBefore=…&untilAfter=…&cursor=…&top=…

Paged enumeration of retention records. Cursor is the ItemKey of the last returned record.

GET /_api/archive/retention/blocked-attempts?siteId=…&since=…

Blocked-attempt forensic feed. since defaults to 7 days ago.

POST /_api/archive/retention/extend

Extends a retention window. Body: { SiteId, ListId, ItemId, NewUntilUtc, Reason }. Rejects shortening with HTTP 400.

GET /_api/archive/retention/policy?siteId=…

Returns the current per-site retention policy.

PATCH /_api/archive/retention/policy?siteId=…

Updates the per-site default retention policy for new imports. Applies to future imports only.


Troubleshooting

Items unexpectedly blocked

  1. Check the blocked-attempts table in the dashboard — confirm the ItemKey and Until date.
  2. Call GET /_api/archive/retention/items?siteId=… to see the retention record for the item.
  3. If the window was assigned incorrectly (wrong anchor date), the operator can call POST /_api/archive/retention/extend to set the correct window end date. The window cannot be shortened retroactively.

Items not blocked when expected

  1. Verify the item has a retention record: GET /_api/archive/retention/items?siteId=…&listId=…
  2. If no record exists, the ArchiveImporter may not have captured retention at import time. Run the backfill command:
    cesivi migrate retention-backfill --site <archiveSiteId>
    
  3. Check that Cesivi:Retention:UseInMemory is not set to true in production — InMemory store does not persist between restarts.

Blocked attempts not appearing in dashboard

  1. Verify SignalR is connected (green dot in top-right of the dashboard).
  2. Check that ASPNETCORE_ENVIRONMENT is not Testing — some test configurations disable SignalR.
  3. The dashboard only shows the last 100 rows in the browser; older records are in the store and accessible via GET /_api/archive/retention/blocked-attempts.

High CPU from retention gate

The gate performs an in-memory lookup (O(1) for per-item checks; O(N) for list-level file/folder checks). If the archive site has millions of records and list-level operations are frequent, consider: - Enabling FileSystemArchiveRetentionStore (default) rather than InMemory for production - Pre-computing per-list indexes (planned for v1.3)


Compliance Mapping

The retention enforcement gate is designed to support the following regulatory requirements:

Regulation Requirement How Cesivi Satisfies It
HIPAA § 164.530(j) Retain PHI records for 6 years Set DefaultWindowDays=2190 (6 years) per site
GDPR Art. 5(1)(e) Erase personal data when no longer needed; storage limitation Retention window defines the mandatory hold period; after expiry, items can be deleted
SOX § 802 Retain financial records for 7 years Set DefaultWindowDays=2555 (7 years, default)
SEC Rule 17a-4 WORM-compliant storage with audit trail Blocked-attempt log in FileSystem JSONL + WORM audit event; no in-place overwrites
ISO 27001 A.18.1.3 Protection of records Hard gate prevents accidental deletion; blocked attempts are audited to WORM log

Disclaimer: Cesivi provides the technical enforcement mechanism. Consult your legal and compliance team to verify that your specific policy parameters (window length, anchor mode) satisfy applicable regulatory requirements in your jurisdiction.


See Also


See also: Archive Admin Bundle — ControlCenter Quick Tour

See also: Archive Tools Operator Guide

See also: Tutorial G — SharePoint On-Premises Retirement Archive

See also: Cesivi Archive Variant A — Whitepaper

See also: Compliance Cookbook — HIPAA/GDPR/SOX/FRCP

See also: Archive Cluster Deployment Guide