Skip to content

Tutorial G: SharePoint On-Premises Retirement Archive

Overview

This tutorial walks you through using Cesivi as a long-term compliance archive for a retiring SharePoint on-premises farm. You will import all lists, libraries, items, versions, attachments, content types, fields, permissions, users, and groups into Cesivi, lock everything down with archive mode, set a retention policy, apply a legal hold, verify content integrity, and tour the ControlCenter archive dashboard.

Use case: A SharePoint 2013/2016/2019 farm is reaching end-of-life. Legal or compliance obligations require retaining the content for 7–30 years. Keeping the farm running costs money; migrating to SharePoint Online is expensive and still requires a retention archive. Cesivi becomes the archive — auditors see exactly what they would have seen on the live farm.

Time required: ~60 minutes on a fresh install

When to use Tutorial G vs Tutorial F: - Tutorial F — Deploy a production live SP-replacement that users actively work in. - Tutorial G — Deploy a read-only retirement archive of a farm that is being shut down.

Reference guides:

ARCHIVE_MODE.md | ARCHIVE_IDENTITY.md | ARCHIVE_IMPORTER.md | ARCHIVE_AUDIT.md | ARCHIVE_INTEGRITY.md | ARCHIVE_RETENTION.md | ARCHIVE_LEGAL_HOLD.md


Prerequisites

Item Notes
.NET 10.0 runtime dotnet --version must show 10.x
Windows host (or Linux — see Deployment Guide) Windows NTFS vol recommended for WORM (filesystem immutability)
8 GB RAM 16 GB recommended for large farms
50 GB disk For WORM audit segments and archive data
Source SP farm URL + Site Collection Administrator credentials Cesivi reads from the source farm
Cesivi.Setup installer (CesiviSetup.exe) Available in the Cesivi release package

Architecture

  Source SP Farm                 Cesivi Archive Instance
  ┌──────────────┐               ┌──────────────────────────────────────────┐
  │ Lists        │               │ Archive Site (archive_mode=true)         │
  │ Libraries    │  MigrationTool│ ┌──────────────────────────────────────┐ │
  │ Items        │ ─────────────▶│ │ Lists / Libraries / Items / Versions │ │
  │ Versions     │  archive-import│ │ Content Types / Fields               │ │
  │ Users        │               │ └──────────────────────────────────────┘ │
  │ Permissions  │               │                                          │
  └──────────────┘               │ ┌──────────┐  ┌──────────┐  ┌────────┐ │
                                  │ │WORM audit│  │Integrity │  │Retent. │ │
                                  │ │log (JSONL│  │SHA-256   │  │gate    │ │
                                  │ │hash chain│  │walker    │  │(hard)  │ │
                                  │ └──────────┘  └──────────┘  └────────┘ │
                                  │                                          │
                                  │ ┌────────────────────────────────────┐  │
                                  │ │ Legal Hold gate (hold > retention) │  │
                                  │ └────────────────────────────────────┘  │
                                  └──────────────────────────────────────────┘
                                              │
                                  ControlCenter  StorageBrowser

Table of Contents


Phase 1: Install and Configure

Step 1: Run the Setup Wizard

Launch the installer:

  • Windows: Double-click CesiviSetup.exe or run:
    CesiviSetup.exe
    
  • Linux: Run via dotnet:
    dotnet CesiviSetup.dll
    

Walk through the first three pages:

  1. Welcome — review the version and license.
  2. Install Path — choose install directory and data directory.

    Choose a data directory on a volume with at least 50 GB free. For WORM filesystem immutability the volume should be NTFS (Windows) or ext4 (Linux).


Step 2: Choose Archive Mode branch

On the Components page (step 3 of the standard wizard):

  1. Select the components to install: Server, WebUI, optionally CLI.
  2. Tick Enable Archive Mode.
  3. Click Next.

Ticking Enable Archive Mode inserts three additional configuration pages into the wizard (pages 4–6 of 12 total). These pages configure the archive subsystems.


Step 3: Configure WORM Backend

WORM Backend (page 4 of 12):

Option Notes
File System (recommended) Enter a WORM base path, e.g. C:\CesiviArchive\worm. The installer validates the path.
Amazon S3 (Object Lock) v1.3 planned — configure manually in appsettings.json after install.
Azure Blob (Immutability) v1.3 planned — configure manually after install.

The File System backend uses OS-level immutability: ReadOnly on Windows and chmod 0444 + chattr +i on Linux. Once a WORM segment is sealed it cannot be modified without breaking the hash chain.

v1.3 note: When cloud backends arrive they plug in via the IWormAuditLogStore SPI already shipped in v1.2. See ARCHIVE_AUDIT.md for the SPI contract.


Step 4: Set Retention Defaults

Retention Defaults (page 5 of 12):

Field Default Notes
Default retention (days) 2555 (7 years) HIPAA and SOX both require 7-year retention. Set 0 for no expiry.
Retention anchor Item Created date ItemCreated, ItemModified, or ImportTime

Retention windows can only be extended after assignment, never shortened. Once a record is stamped with a retention anchor the longer of (existing, new policy) always wins.


Step 5: Configure Federation

Federation (page 6 of 12):

Mode Behaviour
None (snapshot only) Identities served from import-time snapshot. Default for air-gapped deployments.
Active Directory (LDAP) Live LDAP lookup; falls back to snapshot if user not found.
Microsoft Entra ID Live Entra/Azure AD lookup; falls back to snapshot.

For a lab or initial pilot, None is correct — Cesivi resolves all identities from the import-time snapshot. For a production deployment where the source AD still runs, choose AD or Entra to get live display names for users who remain in the directory.

See ARCHIVE_IDENTITY.md for the production federation configuration (AD endpoint, bind credentials, cache TTL).

Complete the remaining wizard pages (IIS, Database, Authentication, Summary, Install, Complete) as for any standard Cesivi deployment.


Step 6: First-Boot Verification

After the wizard finishes:

  1. Start Cesivi.Server (the wizard shows the command or opens the browser automatically).
  2. Open your browser: http://localhost:15020 (ControlCenter — port from your IIS / branch-config).
  3. Click Archive in the top navigation.
  4. The Archive KPI Dashboard (/Archive) should open showing 7 KPI cards — all showing 0. This confirms the archive subsystem started correctly on a fresh installation.
Screenshot moment: /Archive KPI dashboard — all zeros, 7 cards rendered

Phase 2: Run the Import

Step 7: Dry-Run the Import

Before writing anything, validate that Cesivi can reach your source farm:

cd Cesivi.MigrationTool
dotnet run -- archive-import \
  --source "https://intranet.contoso.com" \
  --username "CONTOSO\administrator" \
  --password "Passw0rd" \
  --dry-run

A dry-run connects to the source, walks all site collections, lists, and items, and prints summary counts — without writing anything to the Cesivi target.

For this tutorial (reproducible without a real SP farm): use a Cesivi-on-Cesivi import. Start a second Cesivi instance with a small data set (R:/MockData or a YAML-seeded dataset), and use it as the --source:

dotnet run -- archive-import \
  --source "http://localhost:5010" \
  --username "admin" \
  --password "admin" \
  --dry-run

Expect output like:

[DRY RUN] Site collections found: 1
[DRY RUN] Lists/libraries: 5
[DRY RUN] Items: 47
[DRY RUN] Files: 12
No data was written.

Step 8: Full Import

Run the full import with a target:

cd Cesivi.MigrationTool
dotnet run -- archive-import \
  --source "http://localhost:5010" \
  --username "admin" \
  --password "admin" \
  --target  "http://localhost:15020" \
  --target-user "admin" \
  --target-password "admin"

--target is the running Cesivi archive instance URL. --target-user / --target-password default to the source credentials when omitted (safe for same-host test).

During the import, the tool prints a live progress table:

┌───────────────────────────────────────────────────┐
│  Farm: http://localhost:5010                       │
│  Phase: Items (3/5 lists complete)                 │
│  Items: 42/47   Files: 10/12   Errors: 0           │
│  Identity snapshots: 3   ACLs frozen: 5            │
└───────────────────────────────────────────────────┘

The import automatically: - Sets archive_mode = true on all imported webs and lists - Captures identity snapshots for every user encountered - Freezes role assignments into the archived ACL store - Writes ItemImported, IdentitySnapshotCaptured, and AclFrozen events to the WORM log

Resuming an interrupted import

If the import is killed (power loss, Ctrl+C), restart with --resume:

dotnet run -- archive-import \
  --source "http://localhost:5010" \
  --username "admin" \
  --password "admin" \
  --target  "http://localhost:15020" \
  --target-user "admin" \
  --target-password "admin" \
  --resume

The tool reads a checkpoint file (default: alongside the tool binary) and skips already-imported items.

Screenshot moment: /Archive/ImportProgress — live feed during import

Step 9: Verify Post-Import State

Once the import completes, verify the key post-import invariants:

9a. ARCHIVED banner

Navigate to the imported site in the WebUI: http://localhost:15020/sites/RootSite/SitePages/Home.aspx

A navy ARCHIVED banner should appear at the top of the page:

⚠  ARCHIVED — This site is a read-only retirement archive. Write operations are not permitted.

9b. Identity dashboard

Open ControlCenter → Archive → Identity (/Archive/Identity). You should see:

  • Total principals: matches the number of users in the source farm
  • At least some with Snapshot tier (users who exist in the snapshot but may not be in the live AD)
Screenshot moment: /Archive/Identity dashboard after import

9c. Audit log events

Open ControlCenter → Archive → Audit Log (/Archive/AuditLog). Filter to event_type = ItemImported. You should see one event per imported item. Also verify IdentitySnapshotCaptured and AclFrozen events.

Screenshot moment: /Archive/AuditLog showing ItemImported events

9d. Write-rejection

Attempt to delete an item via REST:

DELETE http://localhost:15020/_api/web/lists/getbytitle('Documents')/items(1)
Authorization: Basic YWRtaW46YWRtaW4=

Expected response: HTTP 403 Forbidden

{
  "error": {
    "code": "-2147024891, System.UnauthorizedAccessException",
    "message": {
      "lang": "en-US",
      "value": "Web is archived (read-only). Use X-Cesivi-Archive-Override header with admin credentials to bypass."
    }
  }
}

Troubleshooting

Symptom Likely cause Resolution
Import stalls at "Phase: Users" Source SP throttling Add --log-text and check logs; reduce concurrency
Identity snapshot count = 0 Source has no users with SIDs Check source farm — anonymous-only site has no snapshots
ARCHIVED banner missing WebUI cache Force-refresh browser; check ControlCenter /Archive/Mode for the web
Import fails with 401 on target Wrong --target-user / --target-password Use admin credentials with site collection admin rights on the target

Cross-reference: ARCHIVE_IMPORTER.md operator troubleshooting section.


Phase 3: Lock It Down

Step 10: Set Retention Policy

Set a 7-year retention policy on the archived site. Replace {siteId} with the site's archive site ID (visible in ControlCenter → Archive → Retention → Policy tab, or from the import log).

PATCH http://localhost:15020/_api/archive/retention/policy?siteId={siteId}
Content-Type: application/json
Authorization: Basic YWRtaW46YWRtaW4=

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

Expected: HTTP 204 No Content

Immediately after, check the audit log for a RetentionAnchorAssigned event per item, confirming the policy was applied to all imported items.

Attempt a destructive operation (verify gate)

Try to delete the same item again:

DELETE http://localhost:15020/_api/web/lists/getbytitle('Documents')/items(1)
Authorization: Basic YWRtaW46YWRtaW4=

Expected: HTTP 423 Locked

{
  "error": {
    "code": "RetentionBlocked",
    "message": {
      "value": "Item is within retention window (expires 2033-05-27). Deletion blocked by ArchiveRetentionGate."
    }
  }
}

This DeleteBlocked audit event appears in the Retention dashboard:

Screenshot moment: /Archive/Retention dashboard with blocked-attempt counter

No admin override: The retention gate has no bypass. Even admin credentials cannot delete a retained item. This is by design — it satisfies HIPAA and SOX tamper-proof requirements. See ARCHIVE_RETENTION.md.


Apply an item-scoped legal hold. You will need: - ArchiveSiteId — the farm GUID shown in ControlCenter → Archive → ImportProgress (or returned by GET /_api/archive/integrity/sites/ status endpoint) - ListId — the list's GUID (from GET /_api/web/lists/getbytitle('Documents')?$select=Id) - ItemId — the integer item ID

POST http://localhost:15020/_api/archive/legalhold/apply
Content-Type: application/json
Authorization: Basic YWRtaW46YWRtaW4=

{
  "ArchiveSiteId": "00000000-0000-0000-0000-000000000000",
  "Scope": "Item",
  "ListId": "00000000-0000-0000-0000-000000000001",
  "ItemId": 1,
  "CaseNumber": "CASE-2026-001",
  "Reason": "Pending litigation — do not destroy"
}

Replace the placeholder GUIDs with the actual values for your archive site and list.

Expected: HTTP 201 Created with a hold ID in the response body.

A HoldApplied event is written to the WORM audit log. Open /Archive/LegalHold to confirm the hold is listed as Active.

Screenshot moment: /Archive/LegalHold dashboard with active hold

Step 12: Verify Hold Beats Retention

The legal hold gate runs before the retention gate. Even if the retention window expired, an active hold blocks all mutation:

To demonstrate: apply the hold to an item whose retention window you have already expired (set DefaultWindowDays: 1 temporarily, wait for the reaper pass, then restore to 2555). Then attempt:

DELETE http://localhost:15020/_api/web/lists/getbytitle('Documents')/items(1)
Authorization: Basic YWRtaW46YWRtaW4=

Expected: HTTP 423 Locked — the response body references LegalHoldGate, not RetentionGate, confirming the hold evaluated first:

{
  "error": {
    "code": "LegalHoldBlocked",
    "message": {
      "value": "Item is subject to legal hold CASE-2026-001. Deletion blocked by ArchiveLegalHoldGate."
    }
  }
}

Cross-reference: ARCHIVE_LEGAL_HOLD.md — hold-beats-retention section.


Step 13: Run an Integrity Walk

Trigger a full SHA-256 integrity walk across all imported farms:

POST http://localhost:15020/_api/archive/integrity/walks/run
Authorization: Basic YWRtaW46YWRtaW4=

Expected: HTTP 202 Accepted — the walk starts in the background for every registered archive farm.

Per-farm variant: To walk a single farm, use POST /_api/archive/integrity/sites/{archiveSiteId}/walks/run where {archiveSiteId} is the farm GUID shown in ControlCenter → Archive → ImportProgress.

Open ControlCenter → Archive → Integrity (/Archive/Integrity) and watch the SignalR live-tail. The IntegrityWalkStarted event fires immediately, followed by per-item progress, then IntegrityWalkCompleted with a summary.

Screenshot moment: /Archive/Integrity dashboard during walk

Simulate a corruption to test the on-access gate

  1. Locate a BLOB file in the archive data directory (under {DataRootPath}/lists/...).
  2. Open it in a text editor and change one byte.
  3. Request the file via REST:
    GET http://localhost:15020/_api/web/lists/getbytitle('Documents')/items(1)/File/$value
    Authorization: Basic YWRtaW46YWRtaW4=
    
  4. Expected: HTTP 451 Unavailable For Legal Reasons
{
  "error": {
    "code": "IntegrityMismatch",
    "message": {
      "value": "Content integrity check failed. Item is quarantined pending remediation."
    }
  }
}
  1. Restore the BLOB to its original bytes to clear quarantine. Once the file matches its stored SHA-256 hash, the next on-access read will succeed.

Cross-reference: ARCHIVE_INTEGRITY.md — on-access gate and quarantine sections.


Phase 4: Acceptance and Tour

Step 14: Tour the ControlCenter Archive Bundle

Open http://localhost:15020 (ControlCenter) → Archive.

The _ArchiveLayout sticky sidebar nav appears on every archive page:

Page URL What to check
Dashboard /Archive 7 KPI cards populated (non-zero after import)
Archive Mode /Archive/Mode Toggle + audit trail; confirm imported sites show ARCHIVED
Identity /Archive/Identity Principal count, tier distribution (Live/Snapshot/Unknown)
Import /Archive/ImportProgress Last import status per farm
Audit Log /Archive/AuditLog Event rate, live-tail, filter + CSV export
Integrity /Archive/Integrity Walk status, mismatch count (0 = green)
Retention /Archive/Retention Items under retention, blocked-attempt heat map
Legal Hold /Archive/LegalHold Active holds, items under hold
Screenshot moment: /Archive KPI dashboard populated after import

Step 15: Open StorageBrowser Archive View

Start StorageBrowser if not already running:

cd Cesivi.StorageBrowser
export CESIVI_SERVER_URL=http://localhost:15020
export CESIVI_STORAGE_BROWSER_PORT=5610
dotnet run

Navigate to http://localhost:5610. In the tree:

  • Archived sites show a ARCHIVED badge (navy).
  • Sites with active legal holds show a 🔒 lock icon.
  • Integrity status shows as a green pill ("OK") or red pill ("Mismatches").
  • Retention countdown shows "720d remaining" or EXPIRED if lapsed.

Click Details next to an archived site to open the ArchiveDetail 4-card panel:

Card Content
Archive Mode true, archived-at timestamp, source farm
Retention 7-year window, anchor: ItemCreated, last blocked attempt
Legal Holds Active hold list with case numbers
Integrity Last walk timestamp, mismatch count
Screenshot moment: StorageBrowser ARCHIVED badge + ArchiveDetail panel

Step 16: Export Audit Log to CSV

In ControlCenter → Archive → Audit Log (/Archive/AuditLog):

  1. Set filter: Event type = DeleteBlocked; Date range = last 30 days.
  2. Click Export CSV.
  3. Verify the downloaded file has columns: eventType, importedAt, itemKey, principalIds, detail.

The CSV export is the primary artifact for compliance auditors. It captures all blocked deletion attempts for the retention period in a tamper-evident format.


Step 17: Acceptance Checklist

Before declaring the archive site live, tick off each of the following:

  • [ ] archive_mode = true confirmed on all imported webs and lists (ControlCenter /Archive/Mode)
  • [ ] Identity snapshot count = expected source user count (ControlCenter /Archive/Identity)
  • [ ] All role assignments frozen — AclFrozen events in audit log
  • [ ] Retention policy set — RetentionAnchorAssigned events in audit log
  • [ ] WORM segments sealed and hash-chain verified (ControlCenter /Archive/AuditLog → chain status indicator)
  • [ ] Integrity walker reports 0 mismatches (ControlCenter /Archive/Integrity)
  • [ ] Audit log live-tailing in real time (SignalR indicator green)
  • [ ] Legal hold mechanism tested — HoldApplied event in audit log
  • [ ] No destructive operations possible (attempted DELETE returns 403/423)
  • [ ] No admin override possible for retained items (verified with admin credentials)
  • [ ] All 7 ControlCenter sub-pages render correctly (/Archive through /Archive/LegalHold)
  • [ ] StorageBrowser archive badges correct (ARCHIVED, lock, integrity pill, retention countdown)
  • [ ] Audit log CSV export produces valid file

Step 18: What is Next

v1.2 is your production-ready on-premises retirement archive. The archive is now:

  • Tamper-resistant (WORM audit log with hash chain)
  • Content-verified (SHA-256 integrity walker)
  • Retention-enforced (hard gate, no bypass)
  • Legal-hold capable (hold beats retention)
  • Fully observable (ControlCenter + StorageBrowser)

v1.3 roadmap:

Feature Notes
Variant B — SPO local backup Archive SharePoint Online tenant exports into Cesivi
S3 Object Lock backend Plug in via IWormAuditLogStore SPI (already in v1.2)
Azure Blob Immutability backend Plug in via IWormAuditLogStore SPI
Identity federation at scale AD/Entra adapters for large user bases
Multi-tenant archive collections Host multiple independent farm archives on one Cesivi instance

For buying-decision and compliance-officer context, see: - Cesivi Archive Variant A whitepaper - Compliance Cookbook — HIPAA/GDPR/SOX/FRCP


See also