Table of Contents

Orleans.Lattice.Backup API reference

This page documents Orleans.Lattice.Backup 9.9.0, in the documentation for Orleans.Lattice 9.9.0 (release line 9.9), built 2026-10-04. It is also published as markdown, with every table and list, at api.md, and llms.txt lists every page.

Every public type and member of Orleans.Lattice.Backup, grouped by role. Types not listed here are internal and are described by behaviour in Architecture.

All serializable types are Orleans-serialized ([GenerateSerializer]) with stable aliases; the constructor parameter validation noted below is enforced at construction.

Registration

LatticeBackupServiceCollectionExtensions

Static extension methods on ISiloBuilder.

Method Signature Purpose
AddLatticeBackup ISiloBuilder AddLatticeBackup(this ISiloBuilder builder, Action<LatticeBackupOptions>? configure = null) Adds the backup storage and engine surface: the default in-cluster sink, the catalog store, the capture / incremental / restore engines, the scheduler, options, and the once-per-silo history bootstrap. Ensures the view infrastructure is present so the catalog tree gets durable per-key history. Must be called after AddLattice(...); throws InvalidOperationException when called first. Idempotent.
ConfigureLatticeBackup ISiloBuilder ConfigureLatticeBackup(this ISiloBuilder builder, Action<LatticeBackupOptions> configure) Layers an additional LatticeBackupOptions configuration delegate.
ConfigureLatticeBackupSchedule ISiloBuilder ConfigureLatticeBackupSchedule(this ISiloBuilder builder, Action<LatticeBackupScheduleOptions> configure) Configures the global LatticeBackupScheduleOptions applied to every scope. The delegate is registered for every named instance, so it also applies to a scope that has a per-scope delegate; the delegates run in registration order, so register the global defaults first for a per-scope override to win.
ConfigureLatticeBackupSchedule ISiloBuilder ConfigureLatticeBackupSchedule(this ISiloBuilder builder, string scopeKey, Action<LatticeBackupScheduleOptions> configure) Configures LatticeBackupScheduleOptions for a specific scope keyed by scopeKey (the value from BackupScopeKey.For). Throws ArgumentException when scopeKey is null or empty.
ConfigureLatticeBackupHealth ISiloBuilder ConfigureLatticeBackupHealth(this ISiloBuilder builder, Action<LatticeBackupHealthOptions> configure) Configures the cluster-wide LatticeBackupHealthOptions governing the periodic backup-health monitor: whether it runs and the default re-verification cadence. Health monitoring is auto-enrolled and on by default, so this is only needed to change the cadence or disable the monitor. The monitor stays inert against a non-durable sink regardless of these options. Throws ArgumentNullException when builder or configure is null.

Services

ILatticeBackupCaptureService

The full-capture engine.

  • Task<LatticeBackupCaptureResult> CaptureAsync(LatticeBackupCaptureRequest request, CancellationToken cancellationToken = default) - captures a full backup of the request's scope and returns its content-addressed id and manifest. Throws ArgumentNullException (request null), LatticeAuthorizationDeniedException (unauthorized), LatticeSnapshotReplayBudgetExceededException (scope exceeds the replay budget), LatticeSaturatedException (snapshot open shed under saturation), and LatticeCursorSnapshotExpiredException (pinned snapshot expired mid-capture). With a tenancy add-on active it also throws LatticeBackupTenantIsolationException (the tree is outside the caller's tenant) and LatticeTenantAccessDeniedException (the capture is not admitted under the calling tenant's request-rate budget).
  • Task<LatticeBackupSetCaptureResult> CaptureSetAsync(LatticeBackupSetCaptureRequest request, CancellationToken cancellationToken = default) - captures a backup set: one full backup per scope grouped under a single set manifest. When CrossTreeConsistent is set and the set spans more than one tree, every tree is captured inside a single causal fence, selected after in-flight cross-tree atomic sagas drain, so a cross-tree atomic write is never torn across the set (each tree is still captured by its own snapshot, one after another). When the set spans more than one tree, every member manifest is stamped with the set's SetId, name, and capture time (SetCreatedAtUtc) so the catalogued per-tree backups can be grouped back into one logical set entry, and the returned BackupSetManifest.SetId is that same id. A set of a single scope is deliberately left unstamped - it is indistinguishable from a plain backup and lists as one - so its returned SetId is null: the create response agrees with the catalog row, and there is no id that resolves to nothing. Restore such a backup with RestoreAsync(backupId), not RestoreSetAsync. Throws the same exceptions as CaptureAsync, plus LatticeBackupCrossTreeFenceException when a stable fence cannot be established within the configured attempts or drain timeout.

ILatticeBackupIncrementalCaptureService

The incremental-capture engine.

  • Task<LatticeBackupCaptureResult> CaptureIncrementalAsync(LatticeBackupIncrementalCaptureRequest request, CancellationToken cancellationToken = default) - captures an incremental backup layered on a base backup and records the base id as the manifest's BaseBackupId. The base manifest is read from the sink, and the increment inherits the base's scope (the request scope is advisory). When a sound delta cannot be produced - the base resume point was trimmed off the WAL, a range delete surfaced in the delta window, or the base chain was captured on a different cluster - it falls back to a fresh full capture and returns that result instead. Throws ArgumentNullException when request is null, KeyNotFoundException when the sink holds no manifest for BaseBackupId, and LatticeAuthorizationDeniedException when the caller is not authorized over the base's scope, plus the tenancy exceptions CaptureAsync documents. A fallback full capture can also throw the replay-budget, saturation, and snapshot-expiry exceptions CaptureAsync documents.

ILatticeBackupRestoreService

The causally-faithful restore engine.

  • Task<LatticeRestoreResult> RestoreAsync(LatticeRestoreRequest request, CancellationToken cancellationToken = default) - resolves the backup's manifest (catalog first, then the sink) and authorizes the restore; only then, when the target tree is replicated, is the request handed to the coordinated restore path through IRestoreSagaDispatcher, whose result it returns. Otherwise it walks the base chain, validates every artifact against its recorded digest, then applies the entries per LatticeRestoreRequest.Mode (in-place bulk-load / merge, or atomic shadow-cutover). Idempotent under retry. Authorization covers both trees the restore names: Restore over the target tree, and - only when TargetTreeId retargets the backup onto a different tree than the one captured - Backup over the source tree recorded in each chain manifest's own scope, at the range actually replayed. A same-tree restore is unaffected. Throws ArgumentNullException (request null), ArgumentException (the target is a reserved sys-backup-* tree), LatticeRestoreValidationException (pre-apply validation failure, including a backup absent from both the catalog and the sink, a broken chain, or a requested sub-scope outside the captured scope; for a replicated target, also a coordinated restore that was refused before it started or aborted), and LatticeAuthorizationDeniedException (unauthorized). With a tenancy add-on active it also throws LatticeBackupTenantIsolationException when the target tree, or a retargeted source tree, is outside the caller's tenant. A ShadowCutover restore also throws InvalidOperationException when the target tree is deleted, a delete of it is pending, or another alias change of it (a resize, a schema remediation, or a different restore or revert) holds its alias reservation, and LatticeTreeOwnershipDeniedException when the registered tree ownership guard refuses its alias swap; see Architecture.
  • Task<IReadOnlyList<LatticeRestoreResult>> RestoreSetAsync(string setId, CancellationToken cancellationToken = default) - expands a captured backup set into its member trees (by scanning the catalog for the manifests stamped with setId), authorizes Restore over every member tree before anything is dispatched, and restores each one via shadow-cutover, promoting to a single all-or-nothing coordinated saga when any member is replicated. A set naming one tree the caller may not restore is therefore refused whole with LatticeAuthorizationDeniedException (or, with a tenancy add-on active, LatticeBackupTenantIsolationException). Throws ArgumentException when setId is null or empty, or when it resolves to no member trees. That last failure distinguishes its two causes: an id that equals the set id a single catalogued backup would have been given (the content address of that one member id) names a single-tree set, which is captured as a plain backup and is never stamped as a set member, so the message names the exact backupId to pass to RestoreAsync instead; any other unresolved id is reported as absent from the catalog. A local per-member restore can also fail with the exceptions RestoreAsync documents for a ShadowCutover restore.
  • Task RevertRestoreAsync(LatticeRestoreResult restore, CancellationToken cancellationToken = default) - reverts a ShadowCutover restore by swapping the target tree's registry alias back to PreviousPhysicalTreeId. Idempotent. Authorization gates the logical target tree, so the physical tree ids on restore are separately re-validated against registry provenance: each must be the target itself, a shadow the engine built for that target, or the target's current physical tree. Throws ArgumentNullException (restore null), ArgumentException (not a shadow-cutover result), LatticeRestoreValidationException (a named physical tree does not belong to the target), and LatticeAuthorizationDeniedException (unauthorized). Also throws InvalidOperationException while the tree is deleted, a delete of it is pending, or another alias change holds its alias reservation, and LatticeTreeOwnershipDeniedException when the registered tree ownership guard refuses re-pointing the alias.

ILatticeBackupScheduler

The public entry point for on-demand triggers, schedule registration, and retention. Each operation targets a BackupScopeSelector and is coordinated per scope so triggers, scheduled captures, and retention for the same scope never overlap.

  • Task<string?> TriggerFullBackupAsync(BackupScopeSelector scope) - triggers a full backup; returns the backup id, or null when a capture for the scope is already in flight.
  • Task<string?> TriggerIncrementalBackupAsync(BackupScopeSelector scope) - triggers an incremental backup layered on the most recent backup for the scope (or a full baseline when none exists); returns the backup id, or null when skipped by the overlap guard.
  • Task ScheduleRecurringBackupAsync(LatticeBackupScheduleRequest request) - registers or updates a recurring backup of the request's scope that fires every Interval, capturing a full or incremental backup per the request. The interval is clamped up to the reminder minimum; a runtime schedule registered this way overrides the configured LatticeBackupScheduleOptions cadence for the chosen kind. Idempotent. Authorizes the caller's Backup capability over the scope at registration (the reminder-fired cycle later runs system-origin, because a reminder carries no caller). Throws ArgumentNullException when request is null and LatticeAuthorizationDeniedException when the caller is not authorized.
  • Task CancelScheduleAsync(BackupScopeSelector scope, bool incremental) - removes the scope's recurring schedule for one kind (incremental selects the incremental schedule, otherwise the full one): unregisters that kind's schedule reminder and clears any runtime interval override. Idempotent: a missing schedule is a no-op.
  • Task EnsureScheduleAsync(BackupScopeSelector scope) - registers or updates the recurring full and incremental schedule reminders for the scope per its LatticeBackupScheduleOptions: an enabled kind is (re-)registered at its configured cadence and a disabled kind's reminder is unregistered. It consults only the configured options, so running it after ScheduleRecurringBackupAsync replaces (or, for a kind disabled in options, removes) that kind's runtime schedule reminder; the runtime interval that call recorded is left in place, so BackupSchedulerRuntimeStatus keeps reporting it until CancelScheduleAsync clears it. Idempotent. Authorizes the caller's Backup capability over the scope and throws LatticeAuthorizationDeniedException when it is not granted.
  • Task<BackupRetentionReport> PruneAsync(BackupScopeSelector scope) - prunes the scope's backup chain per its retention policy, preserving the base chain of every retained increment; a no-op that retains everything when retention is disabled.

All five scope-typed methods throw ArgumentNullException when scope is null. The two triggers are authorized by the capture engine they run, and ScheduleRecurringBackupAsync and EnsureScheduleAsync authorize at registration; CancelScheduleAsync and PruneAsync perform no authorization check of their own.

ILatticeBackupCatalogStore

The durable, introspectable index of manifests, persisted into the reserved sys-backup-catalog tree keyed by backup id.

  • Task RegisterAsync(BackupManifest manifest, CancellationToken cancellationToken = default) - registers or replaces a manifest, keyed by manifest.Id. Re-registering an id that is already catalogued carries the existing row's CreatedAtUtc forward (a backup id is a content address, so the capture time is immutable); every other field, set membership included, takes the incoming manifest's value. Idempotent. Throws ArgumentNullException when manifest is null.
  • Task<BackupManifest?> GetAsync(string backupId, CancellationToken cancellationToken = default) - reads a manifest, or null. Throws ArgumentException when backupId is null or empty.
  • Task<bool> RemoveAsync(string backupId, CancellationToken cancellationToken = default) - removes a manifest; returns true when one was removed. Throws ArgumentException when backupId is null or empty.
  • IAsyncEnumerable<BackupManifest> ListAsync(CancellationToken cancellationToken = default) - enumerates every catalogued manifest in backup-id order.

ILatticeBackupSink

The pluggable storage sink a backup is written to and restored from. Stores streamed artifacts and self-describing manifests; the artifact surface moves the payload as an ordered chunk sequence so a large tree streams without being materialized whole. Re-writing the same artifact id with the same content is idempotent. The capture engine names each artifact with a per-capture id (scope tree id, scope kind, capture ticks, and a GUID) and records the artifact's SHA-256 digest (BackupContentHash) in the manifest's content descriptor; the content-addressed id is the backup (manifest) id, not the artifact id.

Capability members:

  • bool IsDurable - whether the sink stores payload outside the cluster it protects (an external, durable store such as Azure Blob or the filesystem sample sink), as opposed to the ephemeral in-cluster sink whose payload shares the fate of the cluster. Disaster-recovery features that only make sense against an off-cluster store - notably the periodic backup-health monitor - gate themselves on this flag: false keeps the monitor inert and hides the Explorer health column.

Artifact members:

  • Task WriteArtifactAsync(string artifactId, IAsyncEnumerable<ReadOnlyMemory<byte>> content, CancellationToken cancellationToken = default) - writes an artifact as an ordered chunk stream. Idempotent for the same id and content. Throws ArgumentException (artifactId null/empty) and ArgumentNullException (content null).
  • IAsyncEnumerable<ReadOnlyMemory<byte>> ReadArtifactAsync(string artifactId, CancellationToken cancellationToken = default) - reads an artifact back as an ordered chunk stream; yields nothing when absent. Throws ArgumentException when artifactId is null or empty.
  • Task<bool> DeleteArtifactAsync(string artifactId, CancellationToken cancellationToken = default) - removes an artifact; returns true when one was removed. Throws ArgumentException when artifactId is null or empty.
  • IAsyncEnumerable<string> ListArtifactIdsAsync(CancellationToken cancellationToken = default) - enumerates every artifact id in id order.

Manifest members:

  • Task WriteManifestAsync(BackupManifest manifest, CancellationToken cancellationToken = default) - creates or replaces a manifest keyed by manifest.Id. Idempotent. Throws ArgumentNullException when manifest is null.
  • Task<BackupManifest?> ReadManifestAsync(string backupId, CancellationToken cancellationToken = default) - reads a manifest, or null. Throws ArgumentException when backupId is null or empty.
  • IAsyncEnumerable<BackupManifest> ListManifestsAsync(CancellationToken cancellationToken = default) - enumerates every manifest in backup-id order.
  • Task<bool> DeleteManifestAsync(string backupId, CancellationToken cancellationToken = default) - removes a manifest; returns true when one was removed. Does not remove referenced artifacts. Throws ArgumentException when backupId is null or empty.

Sink-existence probe members (read-only, cheap - existence and committed-metadata only, never downloading or hashing payload):

  • Task<bool> ManifestExistsAsync(string backupId, CancellationToken cancellationToken = default) - the cheap liveness check used at selection time: true when the manifest is present in the sink. Throws ArgumentException when backupId is null or empty.
  • Task<BackupSinkResolution> ProbeAsync(string backupId, CancellationToken cancellationToken = default) - the richer resolvability probe used by reconcile / scrub: reports whether the manifest is present and which referenced artifacts are missing (absent, or - for sinks that mark commit - present but not committed). Throws ArgumentException when backupId is null or empty.

ILatticeBackupCatalogScrubService

Reconciles the in-cluster catalog against the durable sink in the opposite direction to the rebuild service: it finds catalog rows the sink can no longer resolve (orphans) rather than sink manifests the catalog is missing.

  • Task<BackupCatalogScrubReport> ScrubAsync(bool pruneOrphans = false, CancellationToken cancellationToken = default) - enumerates every catalog row and probes the sink (ILatticeBackupSink.ProbeAsync) for its resolvability, collecting the orphans - rows whose sink payload (manifest, or a referenced artifact) is gone. Non-destructive by default: it only flags orphans. When pruneOrphans is true it removes each orphan catalog row under system origin, which is idempotent on re-run (a pruned orphan is no longer scanned). Returns a BackupCatalogScrubReport summarizing counts scanned, orphaned, and removed, whether pruning ran, and the orphan backup ids.

ILatticeBackupCatalogRebuildService

Rebuilds the in-cluster catalog from the durable sink, treating the sink as the single source of truth and the catalog as a rebuildable, self-healing projection over it.

  • Task<BackupCatalogRebuildReport> RebuildFromSinkAsync(CancellationToken cancellationToken = default) - scans every manifest the sink holds (via ILatticeBackupSink.ListManifestsAsync) and re-registers each into the catalog under system-origin. Idempotent and safe to re-run: a manifest already catalogued is reconciled in place (keeping its immutable capture timestamp) rather than duplicated, and a catalog missing rows the sink has is repopulated. Returns a BackupCatalogRebuildReport summarizing counts scanned, freshly added, and reconciled.

ILatticeBackupColdRestoreService

Restores a backup into a fresh cluster from the durable sink alone, with zero dependency on any surviving sys-backup-catalog tree. This is the disaster-recovery entry point: a cluster that lost its grain storage (so its catalog is gone) but still has the external sink can enumerate, resolve, chain-walk, and restore its backups from the sink.

  • Task<LatticeRestoreResult> ColdRestoreAsync(LatticeRestoreRequest request, CancellationToken cancellationToken = default) - bootstraps the reserved sys- trees if they are absent, resolves the target (tip) manifest from the sink alone (never the catalog), then hands the request to the ordinary restore engine (ILatticeBackupRestoreService.RestoreAsync), which walks the BaseBackupId chain catalog-first with a sink fallback - so on a cluster that lost its catalog the whole chain resolves from the sink - validates every referenced artifact, read from the sink, against its recorded digest, and replays the chain through the HLC-preserving seams; it then re-projects the catalog from the sink so the recovered cluster is left with a correct catalog. Reuses LatticeRestoreRequest / LatticeRestoreResult. Idempotent. Throws LatticeRestoreValidationException when the backup is absent from the sink, the base chain is broken, or an artifact is missing or tampered; LatticeAuthorizationDeniedException when the caller is not authorized (the delegated restore authorizes exactly as RestoreAsync does); and ArgumentNullException when request is null. The delegated restore can also throw the other exceptions RestoreAsync documents.

ILatticeBackupHealthService

Verifies that a backup's durable sink payload is present and intact, layering content-hash verification on top of the cheap presence probe. Registered by AddLatticeBackup.

  • Task<BackupHealthReport> VerifyAsync(string backupId, CancellationToken cancellationToken = default) - resolves the backup's manifest, checks presence and committed-metadata of every referenced artifact (reusing ILatticeBackupSink.ProbeAsync), then downloads each present artifact and re-hashes it against its recorded BackupContentDescriptor.ContentHash to catch silent corruption. Returns a fresh point-in-time BackupHealthReport; does not persist it. Throws ArgumentException when backupId is null or empty.

ILatticeBackupHealthStore

Persists per-backup health state - the latest BackupHealthReport and the per-backup BackupHealthConfig - in the reserved sys-backup-health ILattice tree keyed by backup id, so the periodic monitor that writes reports and the UI that reads them share one durable projection (no second external store). Registered by AddLatticeBackup.

  • Task SetReportAsync(BackupHealthReport report, CancellationToken cancellationToken = default) - persists (or replaces) the latest report for its BackupId. Throws ArgumentNullException when report is null.
  • Task<BackupHealthReport?> GetReportAsync(string backupId, CancellationToken cancellationToken = default) - reads the latest report, or null. Throws ArgumentException when backupId is null or empty.
  • IAsyncEnumerable<BackupHealthReport> ListReportsAsync(CancellationToken cancellationToken = default) - enumerates every stored report in backup-id order.
  • Task<bool> RemoveAsync(string backupId, CancellationToken cancellationToken = default) - removes the stored report and configuration for a backup; returns true when anything was removed. Throws ArgumentException when backupId is null or empty.
  • Task SetConfigAsync(string backupId, BackupHealthConfig config, CancellationToken cancellationToken = default) - persists (or replaces) the per-backup monitor configuration. Throws ArgumentException (backupId null/empty) and ArgumentNullException (config null).
  • Task<BackupHealthConfig?> GetConfigAsync(string backupId, CancellationToken cancellationToken = default) - reads the per-backup configuration, or null when the backup uses the configured defaults. Throws ArgumentException when backupId is null or empty.

The periodic monitor itself is an internal reminder-driven grain (mirroring the backup scheduler): once per sweep it enumerates the catalog and re-verifies each enrolled backup whose configured interval has elapsed, writing the result through ILatticeBackupHealthStore. It is inert unless the registered sink reports IsDurable and LatticeBackupHealthOptions.Enabled is true.

Extension seams (replication-aware backup)

These backup-package-local seams let the replication package layer replication awareness on top of the backup engine - an atomic multi-tree, multi-cluster restore, and a capture-side check that the sink is actually shared - without the backup package taking a dependency on replication. The dispatch, membership, and probe seams (IRestoreSagaDispatcher, IReplicatedTreeMembership, IBackupSinkSharingProbe) each have a default no-op registration installed by AddLatticeBackup, so a single-cluster host always takes the plain local restore path and never runs a cross-cluster probe; the replication package (or the host) supplies the real implementation. ILatticeCoordinatedRestoreEngine and ILatticeBackupSetResolver run the other way: AddLatticeBackup registers their real implementations (the restore engine itself, and a resolver that scans the catalog for set-stamped manifests), and the replication package consumes them.

IRestoreSagaDispatcher

The seam the restore path consults so a restore into a replicated tree can be promoted to an all-or-nothing coordinated restore across every cluster that replicates the target. The decision is a function of the target tree's current replication status, never of the backup's origin. RestoreAsync and RestoreSetAsync consult it only after they have authorized the restore, so the coordinated path never runs for a caller the restore gate refuses. The default registration never dispatches.

  • Task<LatticeRestoreResult?> TryDispatchAsync(LatticeRestoreRequest request, CancellationToken cancellationToken = default) - offers a single-tree restore to the coordinated path; returns the local cluster's result when the coordinated path handled it, or null to signal the caller should run the plain local restore. Throws ArgumentNullException when request is null.
  • Task<IReadOnlyList<LatticeRestoreResult>?> TryDispatchSetAsync(string setId, LatticeRestoreMode mode, CancellationToken cancellationToken = default) - offers a backup-set restore as one atomic unit over the union of the replicated members' peer sets; returns this cluster's per-member results, or null when no member is replicated (or the id is not a set id). Throws ArgumentException when setId is null or empty.

IReplicatedTreeMembership

The seam the startup sink guard consults to learn whether a tree participates in the cross-cluster replication set (a replicated tree must be backed by a shared external sink, not the default in-cluster sink). The default registration reports nothing replicated.

  • bool IsReplicated(string treeId) - reports whether the tree participates in the replication set. Throws ArgumentNullException when treeId is null.
  • IReadOnlyCollection<string> ReplicatedTrees { get; } - the ids of every replicated tree.

IBackupSinkSharingProbe

The capture-side analogue of IRestoreSagaDispatcher: the seam the startup sink guard and the periodic health monitor consult to learn whether the sink this cluster captures into is demonstrably the same store every peer cluster reads from. That is a deployment fact rather than a locally provable configuration property - two regions can hold identical-looking connection strings that resolve to different accounts - so the real (replication-supplied) implementation writes a tiny marker naming this cluster into its own sink and reads every peer's marker back out of that same sink. The default registration never probes and always reports NotApplicable.

  • BackupSinkSharingReport? LastReport { get; } - the most recent verdict, or null when the probe has never run. Read by the per-backup health path so annotating a report costs no I/O.
  • Task<BackupSinkSharingReport> ProbeAsync(CancellationToken cancellationToken = default) - runs the probe now and publishes the fresh verdict to LastReport. Inert (no sink or network I/O, verdict NotApplicable) when no tree is replicated or the deployment has no peers.

BackupSinkSharingReport

The outcome of one cross-cluster sharing probe. Serialized (alias olb.sh), immutable.

  • Constructor: BackupSinkSharingReport(BackupSinkSharingStatus status, string clusterId, int peerCount, IReadOnlyList<string> unconfirmedPeerClusterIds, DateTimeOffset probedAtUtc, string explanation). Throws ArgumentNullException (clusterId, unconfirmedPeerClusterIds, or explanation null) and ArgumentOutOfRangeException (peerCount negative).
  • BackupSinkSharingStatus Status - the verdict.
  • string ClusterId - the local cluster's id, as attested by the marker it wrote.
  • int PeerCount - the number of peer clusters considered.
  • IReadOnlyList<string> UnconfirmedPeerClusterIds - the peers whose marker could not be read back from this cluster's sink.
  • DateTimeOffset ProbedAtUtc - when the probe ran.
  • string Explanation - an operator-facing sentence naming the unconfirmed peers and the remediation.
  • bool IsRefuted - true only when Status is NotShared.

BackupSinkSharingStatus

Value Meaning
NotApplicable Nothing was measured: no replicated tree, no peers, no replication package, or the probe is disabled. The value a single-cluster deployment always reports.
Shared Every peer's marker was read back from this cluster's sink, so a coordinated restore can resolve the same backup fleet-wide.
Unverified At least one peer left no marker and was not reachable, so it may simply not be running yet. Undecided, not a fault.
NotShared At least one peer is reachable yet its marker is absent, so the sink is not shared and backups of a replicated tree are not restorable fleet-wide.

ILatticeCoordinatedRestoreEngine

Decomposes the atomic ShadowCutover restore into the separate phases a coordinated restore saga drives independently. The single ILatticeBackupRestoreService.RestoreAsync entry point composes these same phases for the local path, so both paths share one alias swap. Saga-unaware: it exposes the mechanism without any knowledge of the coordinator, write fence, or participant model.

  • Task<RestoreAdmissionReport> ProbeAdmissionAsync(LatticeRestoreRequest request, CancellationToken cancellationToken = default) - resolves the target's manifest, authorizes Restore over the effective restore scope (the same target-side check BuildShadowAsync makes), then walks the manifest chain and reports its size and topology without validating artifacts, fencing, or building, so an infeasible target is refused up front. Throws ArgumentNullException (request null), LatticeRestoreValidationException (the backup or a chain member is missing, or a requested sub-scope falls outside the captured scope), and LatticeAuthorizationDeniedException (unauthorized).
  • Task<LatticeRestoreResult> BuildShadowAsync(LatticeRestoreRequest request, CancellationToken cancellationToken = default) - builds the shadow tree from the manifest chain into a fresh physical tree without swapping the alias or fencing the live tree. Idempotent and resumable. Authorizes both trees exactly as RestoreAsync does. Registering the shadow takes the target tree's alias reservation, which a commit or a garbage-collect of the shadow releases. Throws ArgumentNullException (request null), ArgumentException (not a shadow-cutover request, or a reserved sys-backup-* target), LatticeRestoreValidationException (validation failure), LatticeAuthorizationDeniedException (unauthorized), and InvalidOperationException (the target tree is deleted, a delete of it is pending, or another alias change holds its reservation).
  • Task CommitShadowAsync(LatticeRestoreResult shadow, CancellationToken cancellationToken = default) - commits a built shadow by atomically swapping the registry alias, then refreshing routing and converging any covering tag index. The caller engages the write fence around this call. Idempotent. Authorizes Restore over the whole target tree. The physical tree ids carried on shadow are re-validated against registry provenance, so a result whose ShadowPhysicalTreeId or PreviousPhysicalTreeId names a tree that is neither the target, nor a shadow the engine built for that target, nor the target's current physical tree is refused. Releases the target tree's alias reservation once the cutover completes. Throws ArgumentNullException (shadow null), ArgumentException (not a shadow-cutover build result), LatticeRestoreValidationException (a named physical tree does not belong to the target), LatticeAuthorizationDeniedException (unauthorized), InvalidOperationException (the target tree is deleted, a delete of it is pending, or a different alias change holds its reservation), and LatticeTreeOwnershipDeniedException (the registered tree ownership guard refuses the alias swap).
  • Task DeleteShadowAsync(string shadowPhysicalTreeId, CancellationToken cancellationToken = default) - reliably garbage-collects an orphaned shadow so an aborted restore leaks no storage. Idempotent: a tree that was never registered is a no-op. Because it purges every shard of the named tree, it deletes only a tree the engine itself stamped as a restore shadow, and authorizes Restore over the logical tree that shadow was built for - taken from the shadow's own registry provenance, never from the argument. It also releases the alias reservation the shadow build took on that logical tree, so an aborted restore does not go on refusing the tree's delete or later alias changes. Throws ArgumentException when shadowPhysicalTreeId is null or empty, LatticeRestoreValidationException when the named tree is registered but is not a restore shadow, and LatticeAuthorizationDeniedException (unauthorized).
  • string ResolveShadowTreeId(LatticeRestoreRequest request) - deterministically resolves the shadow tree id a build of request would produce, without I/O, so an aborting participant can garbage-collect by id after losing its in-memory state. Throws ArgumentNullException (request null) and ArgumentException (no explicit target tree).

ILatticeBackupSetResolver

The saga-unaware read seam that expands a captured backup-set id into the per-tree member backups it references, so the restore path can restore every tree in a set as one unit.

  • Task<IReadOnlyList<BackupSetMember>> ResolveMembersAsync(string setId, CancellationToken cancellationToken = default) - resolves the set's member backups in tree-id order, or an empty list when the id is not a set id (for example a single-tree backup id). Throws ArgumentException when setId is null or empty.

BackupSetMember

One resolved member of a captured backup set: the member backup id and the tree it restores. Returned by ILatticeBackupSetResolver. An in-process value only (no serializer surface).

  • string BackupId - the content-addressed id of the member backup.
  • string TreeId - the tree the member backup restores.

Tenancy seam

The seam the backup engine consults to keep every capture and restore inside the active tenant's t/{tenantId}/{name} namespace and within the tenant's quota. It follows the same null-default pattern as the data-plane tenant gate: AddLatticeBackup installs an inert internal implementation (IsActive is false, every check a no-op), so a host without a tenancy add-on is unchanged; the tenancy add-on registers the active implementation in its place. The tenant is the ambient active tenant, not a parameter.

ILatticeBackupTenantScope

  • bool IsActive { get; } - true when a tenancy add-on has replaced the null default; every tenant check is gated on it.
  • void AuthorizeCapture(string treeId) - verifies the active tenant may capture treeId: a platform tree is left to the authorization gate, and a tenant-owned tree may be captured only by its owning tenant (a flow with no active tenant is likewise left to the authorization gate). Throws LatticeBackupTenantIsolationException when refused.
  • void AuthorizeRestoreTarget(string treeId) - applies the same ownership rule to a restore target, before any record is streamed. Throws LatticeBackupTenantIsolationException when refused.
  • ValueTask<IBackupRestoreAdmission> BeginRestoreAsync(string targetTreeId, CancellationToken cancellationToken = default) - opens a per-record admission controller for a restore into targetTreeId, resolving the active tenant's quota once.

IBackupRestoreAdmission

The per-restore admission controller the restore stream consults once per record. A refused record is dead-lettered (skipped), never silently written. Not required to be thread-safe.

  • long AdmittedCount { get; } - records admitted (written) so far.
  • long DeadLetteredCrossTenant { get; } - records dead-lettered because they were addressed outside the active tenant's namespace.
  • long DeadLetteredOverQuota { get; } - records dead-lettered because admitting them would exceed the active tenant's key quota.
  • BackupRestoreRecordDisposition Admit(string key) - decides whether the record may be written, updating the counters. Throws ArgumentNullException when key is null.

BackupRestoreRecordDisposition

Value Meaning
Admit Inside the active tenant's namespace and within quota; the record is written.
CrossTenant Addressed outside the active tenant's namespace; the record is dead-lettered.
OverQuota Would take the active tenant past its key quota; the record is dead-lettered.

An in-process control value only (no serializer surface).

Operation constants and helpers

BackupOperationKinds

Public constants for tracked backup operation kinds. Prefix is backup., and the concrete kinds are Capture (backup.capture), IncrementalCapture (backup.incremental-capture), SetCapture (backup.set-capture), Restore (backup.restore), and ColdRestore (backup.cold-restore).

BackupOperationPhases

Public constants for progress phases reported by tracked operations: Capturing, CapturingMembers, Cataloguing, Bootstrapping, Validating, Applying, and Replaying. A kind reports only the phases that apply to the work in hand.

BackupOperationUnits

Public constants for progress unit names: Entries (entries), Shards (shards), Members (members), and Manifests (manifests).

BackupOperationResultKeys

Public constants for the string result map carried by a succeeded tracked operation: backupId, setId, memberBackupIds, targetTreeId, mode, restoreOperationId, manifestChain, entriesApplied, shadowPhysicalTreeId, previousPhysicalTreeId, deadLetteredCrossTenant, and deadLetteredOverQuota.

BackupOperationResults

Helpers for reading operation result maps. TryReadRestoreResult(IReadOnlyDictionary<string, string> result, out LatticeRestoreResult? restore) reconstructs the full restore result when the map has the restore keys, and ReadMemberBackupIds(IReadOnlyDictionary<string, string> result) parses the comma-separated set-member backup ids.

Requests and results

LatticeBackupCaptureRequest

Full-capture request.

  • const int DefaultPageSize = 1024.
  • Constructor: LatticeBackupCaptureRequest(string name, BackupScopeSelector scope, int pageSize = DefaultPageSize). Throws ArgumentException (name null/empty), ArgumentNullException (scope null), ArgumentOutOfRangeException (pageSize not positive).
  • Properties: string Name, BackupScopeSelector Scope, int PageSize.

LatticeBackupIncrementalCaptureRequest

Incremental-capture request.

  • Constructor: LatticeBackupIncrementalCaptureRequest(string name, BackupScopeSelector scope, string baseBackupId, int pageSize = LatticeBackupCaptureRequest.DefaultPageSize). Throws ArgumentException (name or baseBackupId null/empty), ArgumentNullException (scope null), ArgumentOutOfRangeException (pageSize not positive).
  • Properties: string Name, BackupScopeSelector Scope, string BaseBackupId, int PageSize.

LatticeBackupCaptureResult

  • Constructor: LatticeBackupCaptureResult(string backupId, BackupManifest manifest). Throws ArgumentException (backupId null/empty) and ArgumentNullException (manifest null).
  • Properties: string BackupId, BackupManifest Manifest.

LatticeBackupSetCaptureRequest

Backup-set request.

  • Constructor: LatticeBackupSetCaptureRequest(string name, IReadOnlyList<BackupScopeSelector> scopes, bool crossTreeConsistent = false, int pageSize = LatticeBackupCaptureRequest.DefaultPageSize). Throws ArgumentException when name is null/empty, scopes is empty, or two scopes name the same tree; ArgumentNullException when scopes or a member is null; ArgumentOutOfRangeException when pageSize is not positive.
  • Properties: string Name, IReadOnlyList<BackupScopeSelector> Scopes, bool CrossTreeConsistent, int PageSize.

LatticeBackupSetCaptureResult

  • Constructor: LatticeBackupSetCaptureResult(BackupSetManifest setManifest, IReadOnlyList<LatticeBackupCaptureResult> members). Throws ArgumentNullException (either null) and ArgumentException (members empty).
  • Properties: BackupSetManifest SetManifest, IReadOnlyList<LatticeBackupCaptureResult> Members.

LatticeBackupScheduleRequest

A request to register a recurring backup schedule for a scope.

  • Constructor: LatticeBackupScheduleRequest(BackupScopeSelector scope, bool incremental, TimeSpan interval). Throws ArgumentNullException when scope is null; ArgumentOutOfRangeException when interval is not strictly positive.
  • Properties: BackupScopeSelector Scope, bool Incremental, TimeSpan Interval.

A runtime schedule registered from this request overrides the configured LatticeBackupScheduleOptions cadence for the chosen kind; the interval is clamped up to the scheduler minimum when smaller.

LatticeRestoreRequest

Restore request.

  • const int DefaultApplyBatchSize = 1024.
  • Constructor: LatticeRestoreRequest(string backupId, string? targetTreeId = null, BackupScopeSelector? scope = null, LatticeRestoreMode mode = LatticeRestoreMode.InPlace, string? operationId = null, int applyBatchSize = DefaultApplyBatchSize). Throws ArgumentException (backupId null/empty, or targetTreeId / operationId supplied but empty) and ArgumentOutOfRangeException (applyBatchSize not positive).
  • Properties: string BackupId, string? TargetTreeId, BackupScopeSelector? Scope, LatticeRestoreMode Mode, string? OperationId, int ApplyBatchSize.

LatticeRestoreResult

  • Constructor: LatticeRestoreResult(string backupId, string targetTreeId, LatticeRestoreMode mode, string operationId, IReadOnlyList<string> manifestChain, long entriesApplied, string? shadowPhysicalTreeId = null, string? previousPhysicalTreeId = null, long deadLetteredCrossTenant = 0, long deadLetteredOverQuota = 0). Throws ArgumentException (backupId, targetTreeId, or operationId null/empty), ArgumentNullException (manifestChain null), ArgumentOutOfRangeException (entriesApplied, deadLetteredCrossTenant, or deadLetteredOverQuota negative).
  • Properties: string BackupId, string TargetTreeId, LatticeRestoreMode Mode, string OperationId, IReadOnlyList<string> ManifestChain, long EntriesApplied, string? ShadowPhysicalTreeId, string? PreviousPhysicalTreeId, long DeadLetteredCrossTenant (records dead-lettered because they were addressed outside the active tenant's namespace), and long DeadLetteredOverQuota (records dead-lettered because admitting them would exceed the active tenant's key quota); both are zero when no tenancy add-on is active.

Scope

BackupScopeSelector

Names a region of a tree to back up.

  • Constructor: BackupScopeSelector(BackupScopeKind kind, string treeId, string? keyOrPrefix = null). Throws ArgumentException when treeId is null/empty, a WholeTree scope carries a key/prefix, or a Key / Prefix scope omits its key/prefix.
  • Properties: BackupScopeKind Kind, string TreeId, string? KeyOrPrefix.
  • Factories: static BackupScopeSelector WholeTree(string treeId), static BackupScopeSelector Prefix(string treeId, string prefix), static BackupScopeSelector Key(string treeId, string key).

BackupScopeKey

  • static string For(BackupScopeSelector scope) - the deterministic scope key used as the per-scope scheduler grain key and the named-options key. Two selectors covering the same region produce the same key. Throws ArgumentNullException when scope is null.

Manifests and descriptors

BackupManifest

The self-describing record of one backup.

  • Constructor: BackupManifest(string id, string name, DateTimeOffset createdAtUtc, BackupKind kind, BackupScopeSelector scope, BackupConsistencyCut consistencyCut, BackupTopologySnapshot topology, string structuralDigest, IReadOnlyList<BackupKeyDescriptor> keyDescriptors, IReadOnlyList<BackupContentDescriptor> contentDescriptors, IReadOnlyList<BackupOriginProvenance> provenance, string? baseBackupId = null, BackupCompressionDictionaryRef? compressionDictionary = null, string? capturingClusterId = null). Validates that id is non-empty and free of the reserved unit-separator (U+001F); that structuralDigest is non-empty; that an Incremental backup carries a non-empty baseBackupId and a Full backup carries none; and null-checks the reference-type members.
  • Properties: string Id, string Name, DateTimeOffset CreatedAtUtc, BackupKind Kind, BackupScopeSelector Scope, BackupConsistencyCut ConsistencyCut, BackupTopologySnapshot Topology, string StructuralDigest, IReadOnlyList<BackupKeyDescriptor> KeyDescriptors, IReadOnlyList<BackupContentDescriptor> ContentDescriptors, IReadOnlyList<BackupOriginProvenance> Provenance, string? BaseBackupId, BackupCompressionDictionaryRef? CompressionDictionary, string? SetId, string? SetName, DateTimeOffset? SetCreatedAtUtc, string? CapturingClusterId. SetId, SetName, and SetCreatedAtUtc are non-null only on a backup captured as a member of a multi-tree set: every member of one set shares the same values, so a catalog consumer can group the per-tree members into a single logical entry from a first-class fact rather than inferring it from the backup name, and the catalog index orders the members to one shared position. They are stamped when the set is captured; because re-registering a backup id replaces every field except the capture time (see ILatticeBackupCatalogStore.RegisterAsync), a later capture that reproduces a member's bytes - a standalone capture of the unchanged tree, or its capture into another set - re-registers that member with the later capture's set fields. A single-tree set leaves them null, so it lists as an ordinary backup. CapturingClusterId names the cluster that authored the capture (distinct from the per-entry origins in Provenance); it is stamped on every capture, an incremental inherits its base's value so a whole chain shares one stamp, and null marks a manifest captured before the stamp existed (read as the local cluster).

BackupConsistencyCut

The causal cut a backup was taken as of.

  • Constructor: BackupConsistencyCut(long walSequence, long hlcTimestamp, IReadOnlyDictionary<string, long>? perOriginFrontier = null, IReadOnlyDictionary<int, long>? walPartitionOffsets = null). Throws ArgumentOutOfRangeException when walSequence or hlcTimestamp is negative.
  • Properties: long WalSequence, long HlcTimestamp, IReadOnlyDictionary<string, long>? PerOriginFrontier, IReadOnlyDictionary<int, long>? WalPartitionOffsets (the per-partition resume offsets an incremental layers on). HlcTimestamp is the wall-clock component of the highest hybrid-logical-clock stamp the capture read - over the captured entries for a full backup, over the drained delta for an incremental (never below its base's) - and 0 only when the capture (with its whole chain) read nothing, or for a full backup captured by a build that predates this frontier. It is also the frontier an incremental pins the WAL at while it drains forward from its base.
  • What a capture records besides HlcTimestamp: a full capture sets WalSequence to the highest next-to-assign WAL offset across the shards and partitions its snapshot covered, and WalPartitionOffsets to the per-partition WAL heads read just before the snapshot opened. An incremental sets WalPartitionOffsets to the per-partition frontier its drain reached and WalSequence to the highest of those offsets. PerOriginFrontier is null when the captured entries name no origin.

BackupTopologySnapshot

  • Constructor: BackupTopologySnapshot(int shardCount, int virtualShardCount, IReadOnlyList<string> shardRootDigests). Throws ArgumentOutOfRangeException when shardCount or virtualShardCount is not positive, ArgumentNullException when shardRootDigests is null.
  • Properties: int ShardCount, int VirtualShardCount, IReadOnlyList<string> ShardRootDigests.
  • What a capture records: all three come from the tree's routing map at the capture. ShardCount is the number of physical shards the map names, VirtualShardCount is the map's slot count (4096 unless the tree was created with a declared virtual shard count), and ShardRootDigests holds one digest per physical shard in ascending shard-index order - the digest of the captured range on that shard, or nodigest-{index} when the tree does not maintain projection digests. The indices need not be contiguous: a shard consolidation that folds a shard away leaves a gap.

BackupKeyDescriptor

Per-key shape and merge mode.

  • Constructor: BackupKeyDescriptor(string key, BackupKeyMergeMode mergeMode, string? originId = null). Throws ArgumentException when key is null/empty.
  • Properties: string Key, BackupKeyMergeMode MergeMode, string? OriginId.

BackupContentDescriptor

Describes one stored artifact.

  • Constructor: BackupContentDescriptor(string artifactId, string contentHash, long byteLength, int chunkCount, BackupScopeSelector scope). Throws ArgumentException (artifactId or contentHash null/empty), ArgumentOutOfRangeException (byteLength or chunkCount negative), ArgumentNullException (scope null).
  • Properties: string ArtifactId, string ContentHash, long ByteLength, int ChunkCount, BackupScopeSelector Scope.

BackupOriginProvenance

Per-origin high-water mark.

  • Constructor: BackupOriginProvenance(string originId, long highWaterSequence). Throws ArgumentException (originId null/empty), ArgumentOutOfRangeException (highWaterSequence negative).
  • Properties: string OriginId, long HighWaterSequence.

BackupCompressionDictionaryRef

Reference to the compression dictionary a backup's artifacts were encoded against.

  • Constructor: BackupCompressionDictionaryRef(string dictionaryId, string digest). Throws ArgumentException when either is null/empty.
  • Properties: string DictionaryId, string Digest.

BackupSetManifest

The record grouping a backup set's members.

  • Constructor: BackupSetManifest(string? setId, string name, DateTimeOffset createdAtUtc, bool crossTreeConsistent, BackupSetFence? fence, IReadOnlyList<string> memberBackupIds). Throws ArgumentException (setId empty, name null/empty, memberBackupIds empty), ArgumentNullException (memberBackupIds null). A null setId is the absence of an id and is accepted; an empty one is a malformed id and is rejected.
  • Properties: string? SetId, string Name, DateTimeOffset CreatedAtUtc, bool CrossTreeConsistent, BackupSetFence? Fence, IReadOnlyList<string> MemberBackupIds.
  • SetId is non-null only for a set spanning two or more trees. Membership is durable only as the per-member BackupManifest.SetId stamp, and a single-member set is deliberately left unstamped, so it reports no id rather than one that matches no catalog row. A non-null SetId here therefore equals the SetId stamped on every member's catalogued manifest when the set was captured.

BackupSetFence

The selected cross-tree causal fence of a cross-tree-consistent set.

  • Constructor: BackupSetFence(long hlcTimestamp, int drainedInFlightCount, double drainWaitMilliseconds, int attempts). Throws ArgumentOutOfRangeException when hlcTimestamp, drainedInFlightCount, or drainWaitMilliseconds is negative, or attempts is not positive.
  • Properties: long HlcTimestamp (the wall-clock tick count at which the fence was selected), int DrainedInFlightCount, double DrainWaitMilliseconds, int Attempts.

Reports and status

BackupRetentionReport

  • Constructor: BackupRetentionReport(int retainedCount, IReadOnlyList<string> prunedBackupIds). Throws ArgumentOutOfRangeException (retainedCount negative), ArgumentNullException (prunedBackupIds null).
  • Properties: int RetainedCount, IReadOnlyList<string> PrunedBackupIds, int PrunedCount (equals PrunedBackupIds.Count).
  • static BackupRetentionReport Empty - a report that retained nothing and pruned nothing.

RestoreAdmissionReport

The self-describing size and topology of a restore, resolved from the target backup's manifest chain before any fence is engaged or shadow tree is built, so a coordinated restore can hard-refuse an infeasible target up front. Returned by ILatticeCoordinatedRestoreEngine.ProbeAdmissionAsync. An in-process value only (no serializer surface).

  • Constructor: RestoreAdmissionReport(string backupId, string targetTreeId, long totalByteLength, long totalChunkCount, int shardCount, IReadOnlyList<string> manifestChain). Throws ArgumentException (a required string null/empty), ArgumentNullException (manifestChain null), and ArgumentOutOfRangeException (totalByteLength/totalChunkCount negative, shardCount not positive).
  • Properties: string BackupId, string TargetTreeId, long TotalByteLength, long TotalChunkCount, int ShardCount, IReadOnlyList<string> ManifestChain (base-first order).

BackupSchedulerRuntimeStatus

A scope's schedule registration and last-run status.

  • Constructor: BackupSchedulerRuntimeStatus(bool fullScheduleRegistered, bool incrementalScheduleRegistered, DateTimeOffset? lastFullRunUtc, DateTimeOffset? lastFullSuccessUtc, DateTimeOffset? lastIncrementalRunUtc, DateTimeOffset? lastIncrementalSuccessUtc, BackupScopeRunOutcome lastRunOutcome, TimeSpan? runtimeFullBackupInterval = null, TimeSpan? runtimeIncrementalBackupInterval = null).
  • Properties mirror the constructor parameters: bool FullScheduleRegistered, bool IncrementalScheduleRegistered, DateTimeOffset? LastFullRunUtc, DateTimeOffset? LastFullSuccessUtc, DateTimeOffset? LastIncrementalRunUtc, DateTimeOffset? LastIncrementalSuccessUtc, BackupScopeRunOutcome LastRunOutcome, TimeSpan? RuntimeFullBackupInterval, TimeSpan? RuntimeIncrementalBackupInterval (the clamped interval of a runtime schedule registered through ScheduleRecurringBackupAsync, or null when none is recorded for that kind).

BackupCatalogRebuildReport

The outcome summary of ILatticeBackupCatalogRebuildService.RebuildFromSinkAsync. ScannedCount always equals RegisteredCount + ReconciledCount.

  • Constructor: BackupCatalogRebuildReport(long scannedCount, long registeredCount, long reconciledCount).
  • Properties: long ScannedCount (manifests enumerated from the sink), long RegisteredCount (absent from the catalog and freshly added), long ReconciledCount (already catalogued and reconciled in place).

BackupCatalogScrubReport

The outcome summary of ILatticeBackupCatalogScrubService.ScrubAsync. Non-destructive by default, so RemovedCount is zero and Pruned is false unless the caller opts in to pruning; a flag-only pass still reports every orphan.

  • Constructor: BackupCatalogScrubReport(long scannedCount, long orphanCount, long removedCount, bool pruned, IReadOnlyList<string> orphanBackupIds). Throws ArgumentNullException when orphanBackupIds is null.
  • Properties: long ScannedCount (catalog rows cross-checked against the sink), long OrphanCount (rows with no resolvable sink payload), long RemovedCount (orphan rows removed, zero on a non-destructive pass), bool Pruned (whether destructive pruning was requested for the pass - true even when no orphan was found), IReadOnlyList<string> OrphanBackupIds (the ids of the orphans found).

BackupSinkResolution

The read-only outcome of ILatticeBackupSink.ProbeAsync: whether a backup is resolvable from the sink alone.

  • Constructor: BackupSinkResolution(string backupId, bool manifestPresent, IReadOnlyList<string> missingArtifactIds). Throws ArgumentException (backupId null/empty) and ArgumentNullException (missingArtifactIds null).
  • Properties: string BackupId, bool ManifestPresent, IReadOnlyList<string> MissingArtifactIds (referenced artifacts absent, or present but not committed), and the computed bool IsResolvable (true only when the manifest is present and no artifact is missing).

BackupHealthReport

The result of verifying one backup's durable sink payload - presence plus content-hash consistency, and for a replicated tree whether every peer cluster can read the sink holding it - precise enough to drive a diagnostics dialog. Persisted per backup by ILatticeBackupHealthStore.

  • Constructor: BackupHealthReport(string backupId, BackupHealthStatus status, bool manifestPresent, IReadOnlyList<string> missingArtifactIds, IReadOnlyList<string> hashMismatchArtifactIds, DateTimeOffset checkedAtUtc, string explanation, BackupSinkSharingStatus peerVisibility = BackupSinkSharingStatus.NotApplicable, IReadOnlyList<string>? peerUnconfirmedClusterIds = null). The two sharing parameters are trailing and defaulted, so every pre-existing call site and every report persisted before the probe existed still means "no cross-cluster claim made". Throws ArgumentException (backupId null/empty) and ArgumentNullException (missingArtifactIds, hashMismatchArtifactIds, or explanation null).
  • Properties: string BackupId, BackupHealthStatus Status, bool ManifestPresent, IReadOnlyList<string> MissingArtifactIds (referenced artifacts absent or uncommitted), IReadOnlyList<string> HashMismatchArtifactIds (present artifacts whose content no longer matches the manifest's recorded hash), DateTimeOffset CheckedAtUtc, string Explanation (a precise human-readable summary naming the missing / mismatched artifacts and any peer that cannot see the sink), BackupSinkSharingStatus PeerVisibility, IReadOnlyList<string> PeerUnconfirmedClusterIds, and the computed bool IsHealthy (true only when Status is Healthy).

A backup of a replicated tree whose sink is positively refuted (PeerVisibility is NotShared) is reported as Warning even when it is locally intact, because a coordinated restore resolves the same manifest chain from every cluster's own sink and would abort. A non-replicated tree's report is unaffected.

BackupHealthConfig

The per-backup health-monitoring override: whether the periodic monitor verifies this backup, and how often. Every backup is auto-enrolled with the configured defaults; this record overrides that for a single backup. Persisted by ILatticeBackupHealthStore.

  • Constructor: BackupHealthConfig(bool monitoringEnabled, TimeSpan interval). Throws ArgumentOutOfRangeException when interval is not strictly positive.
  • Properties: bool MonitoringEnabled, TimeSpan Interval.

Catalog index

BackupCatalogIndexProjection

The ILatticeViewProjection behind the backup-catalog index materialised view (created when LatticeBackupOptions.EnableBackupCatalogIndexView is true). It lowers each catalog registration - a Set carrying a BackupManifest - into one compact BackupCatalogIndexRow, re-keyed so the index scans newest-first with the members of a backup set contiguous; deletes and range deletes project nothing, and the listing drops any index row whose backup no longer exists in the catalog.

  • const string Version - the projection's code-identity version (backup-catalog-index-v3).
  • string ProjectionVersion { get; } - returns Version.
  • IEnumerable<ViewWrite> Project(LatticeMutation mutation) - maps one catalog mutation to its index-row upsert.

BackupCatalogIndexRow

The compact row the index stores per catalogued backup - exactly the fields the listing filters and sorts on - so a filtered, created-descending, paged query is answered from the index and only the rows that land on the page read their full manifest. Serialized, immutable.

  • string BackupId, string Name, BackupKind Kind, string TreeId, DateTimeOffset CreatedAtUtc - the indexed backup's id, name, kind, scope tree, and capture time.
  • string? SetId, string? SetName - the backup set the backup belongs to, or null when it was captured standalone.
  • string? BaseBackupId - the base an incremental is layered on, or null for a full backup.
  • string DisplayName - SetName when the backup belongs to a set, otherwise Name.

Enums

BackupKind

Full = 0, Incremental = 1.

BackupScopeKind

WholeTree = 0, Prefix = 1, Key = 2.

BackupKeyMergeMode

LastWriterWins = 0, Crdt = 1.

LatticeRestoreMode

InPlace = 0 (a bottom-up bulk-load fast path when the target tree has never been registered and a single full whole-tree backup is restored with no narrower sub-scope, otherwise a last-writer-wins merge that converges with whatever the target already holds), ShadowCutover = 1 (build a fresh physical tree and atomically swap the registry alias).

BackupScopeRunOutcome

None = 0, Success = 1, Failure = 2, Denied = 3 (the last cycle was refused by the access gate, recorded apart from a generic Failure).

BackupHealthStatus

Unknown = 0 (never verified), Healthy = 1 (manifest and every artifact present, committed, and hash-matched), Warning = 2 (manifest present but at least one artifact missing, uncommitted, or hash-mismatched - or, for a replicated tree, the sink is not readable from a peer cluster), Missing = 3 (manifest itself absent - the catalog row is an orphan).

BackupSinkSharingEnforcement

Disabled = 0 (never probe), Warn = 1 (the default - probe, log loudly, annotate health, but start), FailFast = 2 (a positively refuted sink blocks silo start). See Configuration.

Options

LatticeBackupOptions, LatticeBackupScheduleOptions, and LatticeBackupHealthOptions are documented in full in Configuration. LatticeBackupHealthOptions configures the periodic health monitor cluster-wide: bool Enabled (default true - health monitoring is auto-enrolled) and TimeSpan DefaultInterval (default six hours), plus the static MinimumInterval (one minute) the sweep reminder clamps up to and the static DefaultSweepInterval (six hours) that DefaultInterval defaults to. The monitor stays inert against a non-durable sink regardless of these options.

Reserved-namespace guard

LatticeBackupReservedTrees

  • static string Prefix - the reserved tree-name prefix owned by the backup package (sys-backup-).
  • static bool IsReserved(string treeId) - true when treeId collides with the reserved namespace. Throws ArgumentNullException when treeId is null.
  • static void ThrowIfReserved(string treeId, string? paramName = null) - throws ArgumentException when treeId is null, empty, or reserved.

Content addressing

BackupContentHash

  • static string Compute(ReadOnlySpan<byte> content) - the 64-character lowercase hexadecimal SHA-256 of the bytes.
  • static string Compute(IEnumerable<ReadOnlyMemory<byte>> chunks) - the SHA-256 of an ordered chunk sequence, as if concatenated, without buffering the payload whole. Throws ArgumentNullException when chunks is null.

Metrics

BackupMetrics and LatticeBackupMetrics expose the meter, its instruments, tag/phase/reason constants, and the emission helpers. They are documented in full in Observability.

Exceptions

LatticeBackupCrossTreeFenceException : Exception

Thrown by CaptureSetAsync when a stable cross-tree fence cannot be established within the configured attempts or drain timeout. Constructors: (string message) and (string message, Exception innerException).

LatticeBackupTenantIsolationException : InvalidOperationException

Thrown by ILatticeBackupTenantScope.AuthorizeCapture / AuthorizeRestoreTarget when a capture or restore would cross the active tenant's isolation boundary. The operation is refused before any data is read or written. Constructors: (string message) and (string message, Exception innerException).

LatticeRestoreValidationException : InvalidOperationException

Thrown by RestoreAsync when a backup fails pre-apply validation (for example an artifact whose bytes do not match its recorded content digest), and by the other restore entry points - ColdRestoreAsync, RestoreSetAsync's per-member restores, RevertRestoreAsync (a named physical tree that does not belong to the target), and the ILatticeCoordinatedRestoreEngine seams. The replication package's coordinated restore path also throws it when a replicated restore is refused before it starts or aborts. Constructors: (string message) and (string message, Exception innerException).