Skip to content

Retention And Object Lock

Retention protects object versions. It does not verify that the provider served the latest valid commit. Use retention for deletion resistance; use signed commits and anchors for latest-state authority.

Protected Set

When retention is enabled, protect every object needed for restore:

  • keyring envelopes
  • format roots
  • signed commits, including payload and index-delta sections

If one class is missing, the backup may become retained but unrestorable.

Configuration

RS3_REPOSITORY_RETENTION_MODE=compliance
RS3_REPOSITORY_RETENTION_DAYS=30

Use compliance where the provider supports it. Use governance only when privileged bypass is intentional. Normal gateway credentials should not carry governance bypass permission.

Dedup Rule

Retention follows the newest protected reference:

Day 0:  segment A retain until Day 30
Day 20: new protected snapshot reuses A
Needed: A retain until at least Day 50

The gateway must extend reused objects and must never shorten retention. If the provider cannot extend retention, protected writes fail.

v2 full-GC maintenance reports when current or protected historical-root versions need renewal. A guarded apply renews and verifies the exact catalog, run, payload, format-root, and keyring-envelope versions before it attempts any orphan deletion. It consumes inventory through bounded provider pages, uses one immutable plan, and fails closed when a page/item budget, exact authority root, or protection fact is unavailable. The preview library defaults to at most 4,096 inventory pages and 2,000,000 raw provider inventory members per plan. Filtered members such as S3 delete markers consume that item budget too. The in-gateway maintenance supervisor exposes these ceilings as RS3_MAINTENANCE_MAX_INVENTORY_PAGES and RS3_MAINTENANCE_MAX_INVENTORY_ITEMS. Require an explicit, reviewed increase for larger repositories.

Full-GC planning wraps its store in a read-only ledger. Every logical provider HEAD, bounded range GET, and LIST page is charged before it is forwarded, including commit replay, referenced catalog runs, protected roots, and renewal inspection. Reports combine those observed reads with the exact planned delete, retention-extension, and post-extension verification calls. A zero remaining item budget performs no speculative LIST. Counts do not include retries hidden inside a provider SDK, so retry policy remains a separate transport control. The S3 adapter additionally caps each raw list response at 16 MiB before XML deserialization and rejects returned member counts above the requested MaxKeys; protocol-invalid providers therefore cannot turn a bounded page into an unbounded allocation.

The gateway does not expose historical-root registration or in-place format or data-key rotation. Automatic maintenance protects the current anchor graph. Repository-level maintenance rejects foreign-format protected roots before any storage read, and v02 rejects client legal holds. Treat those as unsupported capabilities. Do not bypass the rejection by omitting a root or mutating Object Lock metadata outside the documented provider qualification workflow.

The read-write gateway now runs guarded renewal and orphan reclamation as a background service when RS3_MAINTENANCE_MODE=auto, which is the default. manual requires an operator trigger and off disables renewal, so treat both as an explicit operational exception when retention is enabled. The supervisor parks rather than running without an enforced maintenance guard.

Size every Object Lock window strictly longer than:

maximum automatic-maintenance interval
+ renewal safety horizon

Configure the renewal horizon to cover the longest credible gateway or control-plane outage plus operator detection and response time. The production doctor and Helm production profile reject a window that does not exceed the maximum interval plus that horizon. This margin is not optional. A retention deadline that lapses during an outage cannot be repaired retroactively. A failed apply may already have strengthened some exact versions; retention extension is intentionally irreversible and the run must be retried from a new dry run.

v2 compaction can rewrite the current live namespace into a protected snapshot commit after verifying that snapshot with a fresh reader. Old source commits are not force-deleted by compaction; they remain subject to exact-version orphan GC, provider retention, legal hold, operator budgets, and any protected historical roots that have not been explicitly discarded. The legacy mixed-commit snapshot publisher has a data-dependent write and fresh-reader verification shape, so it fails finite request, HEAD, range-read, or write-byte ceilings until that mutation path has its own end-to-end ledger.

Cluster Takeover

If an attacker controls Kubernetes but not object-store retention, they may create bad backups or corrupt the anchor. They should not be able to delete retained object versions before expiry.

Required posture:

  • least-privilege object-store credentials
  • retention on all restore-critical classes
  • audit logs outside the cluster and storage account
  • repository keys protected outside the attacked namespace
  • external review of anchor and retained-version divergence

Provider Gate

A provider must verify:

  • retention can be set at write time
  • retention can be extended, not shortened
  • legal hold blocks deletion
  • delete markers do not remove locked historical versions
  • retained writes return provider version IDs
  • exact-version reads return the requested retained version after a newer latest version exists
  • retained multipart completion protects the returned version before the write is accepted
  • normal credentials cannot bypass governance

Native conditional create remains the preferred qualification profile when the provider supports it. Providers that accept duplicate same-key writes can still qualify for retained repositories through the retained-version profile, but only when Object Lock, version IDs, and exact-version reads pass live tests.

Do not infer Object Lock semantics from S3 compatibility alone. Do not treat HEAD before PUT as equivalent to native conditional create; it is not atomic and is not a production-preview safety guarantee. Do not infer governance-bypass safety from Object Lock alone. For governance mode, normal gateway credentials must not have s3:BypassGovernanceRetention or provider-equivalent bypass permission. The live retained-version test verifies retained writes, retained exact-version reads, retention extension, legal hold, and delete blocking without bypass headers; it does not replace an IAM or bucket policy review.

Preserve the schema-v4 report produced by the exact release candidate. The gateway rejects evidence from another source revision, backend target, qualification profile, or principal fingerprint. Rebuilds, credential rotations, endpoint or prefix changes, and candidate changes require a fresh provider run.

During restore verification, rs3 reports how many verified restore-critical objects expose retention or legal-hold metadata. Treat that as repository evidence. Run with --require-provider-delete-protection for protected restores, and use S3 CLI probes to confirm provider behavior before trusting a new backend.

Before enabling destructive v2 maintenance for a retained backend, run a GC rehearsal against a fresh prefix after retained-provider conformance has passed:

just v2-gc-rehearsal-live "$BACKEND_BUCKET" "$ENDPOINT_URL" "$REGION" "$BACKEND_PREFIX"

The rehearsal writes a retained anchor, one protected orphan, and one unprotected exact-version orphan. It dry-runs with a one-delete budget, applies only the unprotected exact-version delete, verifies the protected candidate remains blocked, and reloads the anchor-selected chain. The command uses the dedicated Kubernetes Lease writer fence by default. The xtask --unenforced-guard escape hatch is only for isolated development and does not qualify a retained provider.

Incident Rules

  • Do not delete retained objects during cleanup.
  • Freeze write credentials if bad writes continue.
  • Preserve gateway logs, metrics, trusted restore bundles, and anchor state.
  • Compare anchor with retained commit versions where configured.
  • Restore from a verified v2 anchor using read-only credentials where possible.