Table of Contents

Extension seams and supporting types

This page is part of 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 extension-seams-and-supporting-types.md, and llms.txt lists every page.

Part of Lattice Public API Reference.

The sections above document the call surface an application drives. The tables below cover the rest of the public surface of the core package: the registration helpers, the host-replaceable seams, and the value types those seams exchange, each with a pointer to the page that documents it in depth. AddLattice registers the core default of each replaceable seam below - the access gate, write interceptor, value decoder, envelope codec, merge observer, membership and tenancy resolvers, tree placement resolver, tree ownership guard, merge-mode and origin-cluster resolvers, replication context, WAL record encoder, WAL provider catalog, cursor registry, and baseline WAL provider - with TryAdd, so an implementation registered before AddLattice is kept; an add-on package that owns a seam swaps its own implementation in.

Registration helpers

Helper Signature Purpose
AddLattice ISiloBuilder AddLattice(this ISiloBuilder builder, Action<ISiloBuilder, string> configureStorage) Registers Lattice and every core seam default; the callback registers the grain-storage provider under the supplied provider name, and that provider must enforce ETags on write. See Setup and The grain storage provider must enforce ETags.
ConfigureLattice ISiloBuilder ConfigureLattice(this ISiloBuilder builder, Action<LatticeOptions> configure) and ConfigureLattice(this ISiloBuilder builder, string treeName, Action<LatticeOptions> configure) Options for every tree, or overrides for one tree. See Configuration.
ConfigureLatticeGrainStorageFencing ISiloBuilder ConfigureLatticeGrainStorageFencing(this ISiloBuilder builder, Action<LatticeGrainStorageFencingOptions> configure) Configures the start-up probe that checks the grain storage provider enforces ETags: Mode (LatticeGrainStorageFencingMode.Warn by default, Reject, or Disabled) and ProbeTimeout (30 seconds). See The grain storage provider must enforce ETags.
ConfigureLatticeTagIndexReconciliation (this ISiloBuilder builder, Action<LatticeTagIndexReconciliationOptions> configure) and (this ISiloBuilder builder, string indexName, Action<LatticeTagIndexReconciliationOptions> configure) Reconciliation options for every tag index, or overrides for one index. See Background reconciliation.
AddWalStorage ISiloBuilder AddWalStorage(this ISiloBuilder builder, Func<IServiceProvider, IWalStorageProvider>? factory = null) Registers the baseline IWalStorageProvider. The no-factory form installs InMemoryWalStorageProvider only when no provider is registered yet; a factory replaces whatever is registered, so the host's choice is order-independent with respect to AddLattice. See WAL Storage Providers.
AddLatticeWalStorageProvider ISiloBuilder AddLatticeWalStorageProvider(this ISiloBuilder builder, string key, Func<IServiceProvider, IWalStorageProvider> factory) Registers a named provider in the silo's IWalStorageProviderCatalog so WAL partitions can be pinned to it. The reserved default key is rejected, every silo must register the same key set, and re-registering a key is last-call-wins. See Multi-account fan-out.
AddWalCursorRegistry ISiloBuilder AddWalCursorRegistry(this ISiloBuilder builder, Func<IServiceProvider, IWalCursorRegistry>? factory = null) See WAL consumer cursors.
AddLatticeWalGc ISiloBuilder AddLatticeWalGc(this ISiloBuilder builder, Func<IServiceProvider, ILatticeWalGc>? factory = null) Registers the WAL garbage collector (default LatticeWalGc) and the per-silo scheduler that runs it every LatticeOptions.WalGcInterval. Idempotent.
AddLatticeRetryPolicy ISiloBuilder AddLatticeRetryPolicy(this ISiloBuilder builder, Action<BoundedExponentialRetryPolicyOptions>? configure = null) See Idempotency keys and retry policy.
AddOrMapShape<TKey, TValue> ISiloBuilder AddOrMapShape<TKey, TValue>(this ISiloBuilder builder, string treeName) where TKey : notnull where TValue : ICrdt<TValue>, new() Registers the (TKey, TValue) shape an OR-Map tree's accessor and receiver-side applier resolve. Registering a different pair for the same tree is a configuration error.
AddLatticeGrainCallObservation ISiloBuilder AddLatticeGrainCallObservation(this ISiloBuilder builder) Opt-in silo-wide filter that records outstanding depth and duration for every outgoing grain call, tagged by target grain type. Idempotent. See Metrics.
AddLatticeViews / ConfigureLatticeView LatticeViewsServiceCollectionExtensions See Materialised views.
AddLatticeAtomicAction ISiloBuilder AddLatticeAtomicAction(this ISiloBuilder builder, Action<AtomicActionRegistrationBuilder>? configure = null) (LatticeAtomicActionServiceCollectionExtensions) Enables the generic atomic-action coordinator and allow-lists its custom handlers. See Atomic actions.

WAL storage seam (IWalStorageProvider)

Implement IWalStorageProvider to host the write-ahead log on a custom backend; WAL Storage Providers - Contract is the full contract. Members with a default implementation are optional overrides; every member takes a trailing CancellationToken.

Member Default implementation Purpose
Task AppendBatchAsync(string treeId, int shardIndex, IReadOnlyList<WalEntry> entries, CancellationToken) None (required) All-or-nothing append of dense, ascending, caller-assigned offsets.
Task AppendEncodedBatchAsync(string treeId, int shardIndex, ReadOnlyMemory<ArraySegment<byte>> encodedEntries, ReadOnlyMemory<long> offsets, IWalRecordEncoder encoder, CancellationToken) Decodes the segments and delegates to AppendBatchAsync Zero-copy append of pre-encoded payloads, with the same atomicity and offset rules.
IAsyncEnumerable<WalEntry> ReadAsync(string treeId, int shardIndex, long fromOffsetExclusive, int maxEntries, CancellationToken) None (required) Entries above fromOffsetExclusive in ascending offset order, at most maxEntries.
Task<WalShardEncodedPage> ReadEncodedAsync(string treeId, int shardIndex, long fromOffsetExclusive, int maxEntries, IWalRecordEncoder encoder, CancellationToken) Drains ReadAsync and re-encodes each entry The same entries as pre-encoded byte segments.
IAsyncEnumerable<WalEntry> ReadFilteredAsync(string treeId, int shardIndex, long fromOffsetExclusive, long toOffsetInclusive, int maxEntries, WalKeyFilter filter, CancellationToken) Drains ReadAsync over the window and applies the filter to the decoded entries - the same result, at the full decode cost Filtered replay read for a reader that owns filter. It examines at most maxEntries entries of the window (fromOffsetExclusive, toOffsetInclusive] and yields every entry the filter does not exclude, in full; when the last entry examined is excluded it is yielded routing-only (offset, Kind, and Key exact, every other field default) so the reader still advances past everything that was dropped. maxEntries below 1 throws ArgumentOutOfRangeException. Lets a provider drop foreign records before materialising their payloads. See Filtered replay read.
Task<long> GetHighestOffsetAsync(string treeId, int shardIndex, CancellationToken) None (required) The monotonic high-water mark of every offset ever assigned (-1 for a never-written shard). A trim must never lower it.
Task<long> GetLowestOffsetAsync(string treeId, int shardIndex, CancellationToken) None (required) The lowest still-stored offset, or -1 when the shard holds no entries.
Task TrimAsync(string treeId, int shardIndex, long throughOffsetInclusive, CancellationToken) None (required) Idempotently removes every entry at or below the offset; called by the WAL GC.
Task EvaluateCompactionAsync(string treeId, int shardIndex, CancellationToken) No-op Lets a log-structured backend reclaim dead bytes on a pass that trimmed nothing; must not change the shard's logical contents.
Task ReconcileAsync(string treeId, int shardIndex, CancellationToken) No-op Activation-time recovery hook for a backend with a multi-phase commit.
Task<long> GetRetainedByteSizeAsync(string treeId, int shardIndex, CancellationToken) Returns -1 (unsupported) Retained logical payload bytes, for storage-usage accounting and the byte-pressure retention policy.
Task<long> GetPhysicalByteSizeAsync(string treeId, int shardIndex, CancellationToken) Returns -1 (unsupported) Bytes physically occupied, including framing and trimmed-but-unreclaimed space.
Type Role
WalEntry readonly record struct (Offset, Mutation): the provider-boundary entry, a LatticeMutation tagged with its dense per-shard offset.
WalShardEncodedPage readonly record struct (EncodedEntries, Offsets, HighestOffsetInclusive) returned by ReadEncodedAsync. Transient and not an Orleans wire type.
WalKeyFilter readonly record struct: the keys a WAL reader owns - a half-open ordinal key range (LowKeyInclusive, HighKeyExclusive; null means unbounded on that side) intersected, optionally, with the virtual slots a ShardMap routes to one physical shard (VirtualShardCount, OwnedSlots). Built with WalKeyFilter(lowKeyInclusive, highKeyExclusive) or WalKeyFilter(lowKeyInclusive, highKeyExclusive, shardMap, shardIndex); a shard that owns every slot carries no shard constraint. Owns(key) tests ownership; Excludes(kind, key) is true only for a Set, Delete, or Tombstone record whose key it does not own (range deletes and saga terminals are never excluded); IsUnbounded and HasShardConstraint describe the shape, and the default filter owns every key.
IWalRecordEncoder / OrleansBinaryWalRecordEncoder The single-pass codec for WAL payload bytes (Encode(in WalRecord, IBufferWriter<byte>) plus three Decode overloads) and its default Orleans-binary implementation. See WAL.
InMemoryWalStorageProvider The default provider AddLattice registers: process-local, lost on restart, with native ReadFilteredAsync and byte accounting.
IWalStorageProviderCatalog The silo's named directory of providers: TryGet(key, out provider), Keys, and the reserved DefaultProviderKey (default) naming the baseline provider.

WAL garbage collection

Type Role
ILatticeWalGc / LatticeWalGc Task<LatticeWalGcReport> RunOnceAsync(string treeName, CancellationToken) and its default implementation, which trims each WAL partition through the largest prefix every retention bound allows. See WAL - Trim and GC.
LatticeWalGcReport Diagnostic result of one pass over TreeName: the bounds it evaluated (MinCursor, TtlCeilingHlc, CausalStable, BlockedFloor), ShardsScanned (the WAL partitions whose provider resolved and were visited; a partition whose pinned provider key does not resolve on this silo is skipped and not counted, so 0 means none could be visited rather than an empty WAL), EntriesTrimmed, the byte-pressure fields (ByteCeiling, RetainedBytesBefore / RetainedBytesAfter, LogicalRetainedBytes, BytePressureTriggered, BytePressureOverThreshold, CeilingUnsatisfiable), and why the cursor floor held (CursorFloorState, BlockingConsumerId, BlockingConsumerIds, RetainedBacklog).
WalGcCursorFloorState Whether the consumer-cursor trim branch was usable: Available, NoCursorReported, or BlockedByUnusablePin.
WalGcBlockingPinState The durable-pin state of a consumer the GC singled out as blocking: CheckpointedUncovered, NeverCheckpointed, NoDurableState, Unreadable, Orphaned, or CheckpointedCoverageUnknown. See Metrics.
TreeWalUsageReport The cheap WAL-only storage report (TreeId, WalRetainedBytes, WalPhysicalBytes, Partial, SampledAt) behind ILatticeAdmin.PollWalUsageAsync.

Access gate and write interception

Type Role
ILatticeAccessGate ValueTask<LatticeAccessDecision> AuthorizeAsync(in LatticeAccessRequest request, CancellationToken cancellationToken = default), consulted at the data-plane choke point before a read, write, delete, range, CRDT, or lifecycle operation. The core default allows everything. See Access-gate operation flags.
LatticeAccessRequest TreeId, Operation (a LatticeOperation), Subject (a LatticeSubject), and the optional Key, RangeStart, and RangeEnd.
LatticeAccessDecision Allow(), Deny(reason), or Filtered(predicate, reason?) - an allow with a per-key KeyFilter the enforcement point applies to prune keys the caller may not observe. Exposes Allowed, Reason, and KeyFilter.
LatticeSubject The resolved caller: SubjectId, the transitively expanded GroupIds, and optional Claims; Anonymous and System, whose ids are the AnonymousSubjectId (anonymous) and SystemSubjectId (system) constants, and IsAnonymous.
ILatticeMembershipContext Resolves the ambient LatticeCredential into a LatticeSubject (ResolveCurrentAsync, TryResolveCurrent). The core default is an anonymous fallback; the Membership package contributes the real implementation.
ILatticeWriteInterceptor ValueTask<LatticeWriteDecision> OnWriteAsync(in LatticeWriteRequest request, CancellationToken cancellationToken = default), consulted after the access gate authorizes a write and before the value is appended to the WAL. System-origin writes (replication apply, saga legs, view maintenance) bypass it unless InterceptsSystemOrigin returns true. The core default accepts everything.
LatticeWriteRequest TreeId, Key, Value, Operation, and the optional Ttl.
LatticeWriteDecision / LatticeWriteDecisionKind Accept(), AcceptTransformed(newValue), Reject(reason) (surfaced as LatticeWriteRejectedException), or DeadLetter(reason); read back through Kind, TransformedValue, and Reason.
LatticeKeyRange PrefixUpperBound(prefix): the exclusive upper bound of a prefix scan under ordinal comparison, or null when none exists (an empty prefix or one made only of U+FFFF).

Value envelopes and merge observation

Type Role
ILatticeValueDecoder Read-path seam that strips or upcasts a per-value envelope just before stored bytes are returned to a client (IsActive(treeId), DecodeAsync). The core default is inactive, so the read path is unchanged.
ILatticeEnvelopeCodec The merge-path complement: reports a stored value's schema-version tag (ReadVersion) and strips the version envelope from CRDT fold input (StripForFold) before it is deserialized, and never upcasts (IsActive(treeId)). The core default is inactive.
ILatticeMergeObserver Post-merge hook after a per-key CRDT or LWW merge completes (OnMergedAsync(in LatticeMergeContext ctx, CancellationToken ct)), returning a LatticeMergeOutcome. The core default always accepts.
LatticeMergeContext Key, TreeId, Mode, LocalValue, IncomingValue, MergedValue, LocalVersion, and IncomingVersion.
LatticeMergeOutcome / MergeOutcomeKind Accept(), AcceptTransformed(mergedValue) (LWW only), or AcceptWithEvent(reason), read back through Kind, TransformedValue, and EventReason. There is deliberately no reject: the merge has already been applied. See Schema.

Tenancy seams

The core package defines the tenancy seams so tenant-aware choke points need no dependency on the add-on. The core defaults are no-ops - the context resolver resolves the reserved default tenant and the placement resolver the default placement - so a cluster without Orleans.Lattice.Tenancy behaves as though tenancy did not exist.

Type Role
ITenantContextResolver Resolves the caller's active TenantId (ResolveCurrentAsync, TryResolveCurrent).
ITenantAdmissionController Admits or refuses a tenant-scoped operation or tree creation (IsActive, IsAdmittedAsync, IsReadAdmitted, IsTreeCreateAdmittedAsync).
ITenantEnumerationFilter Prunes a tree-id enumeration to the trees a tenant may observe (IsActive, Filter).
ITenantRegionVisibilityResolver Resolves a tenant's per-region standing as a TenantRegionVisibilityMap (IsActive, ResolveAsync).
ITreePlacementResolver Resolves a tree's TreePhysicalPlacement when it is first registered (TryResolveForRegistration, ResolveForRegistrationAsync).
TenantId A DNS-label-like tenant token (Value, MaxLength 63, Default / DefaultId default, IsDefault) with Parse and TryParse.
TreeOwnership A tree's ownership, always re-derived from its id and never stored (IsTenantOwned, IsPlatformOwned, Tenant; built with the static Platform or ForTenant(tenant)).
TreePhysicalPlacement WalProviderKey and the optional PlacementFilter seeded into a new tree's WAL placement; Default.
TenantRegionVisibility / TenantRegionVisibilityMap / TenantRegionResidencyStatus One tenant's standing in a region (IsAllowed, Status, and the derived IsResident and IsVisible), the immutable per-region map of it (Create, TryGet, Count, IsResolved, Empty, Unresolved), and the residency lifecycle (None, Provisioning, Backfilling, Online, Draining, Offline, Removed).
LatticeTenantTrees The reserved t/ structural namespace (SegmentPrefix): Compose, ComposePrefix, IsTenantScoped, TryGetTenant, LocalName, and GetOwner.
LatticeTenantExtensions ResolveEffectiveTreeIdAsync (an extension on ITenantContextResolver) and the two GetLatticeAsync overloads (on IServiceProvider, and on IGrainFactory with an explicit resolver) that resolve an ILattice from a tenant-local tree name. With the core no-op resolver - or with the tenancy add-on registered but no active tenant asserted, which resolves the default tenant - the bare name is returned unchanged, so behaviour is byte-for-byte as before. Under an asserted non-default tenant an unqualified name is scoped into that tenant's t/{tenant}/{name} namespace, and an already-qualified, well-formed t/ id or a _lattice_ system-tree name passes through unchanged (a well-formed foreign t/{other}/{name} is left to the tenancy access gate to adjudicate). The call fails closed with LatticeTenantAccessDeniedException when the asserted tenant fails validation against the caller's own membership, or when, under an asserted tenant and outside system origin, it names a sys- tree or a malformed t/ id that belongs to no tenant.
LatticeActiveTenantContext / LatticeActiveTenantAssertion The ambient active-tenant scope (Current, IsActive, With), and the helper that lifts a caller-asserted tenant off a transport header (default lattice-active-tenant) onto it (Stamp, Resolve, DefaultHeaderName).
LatticeTenantAdminScope / LatticeTenantAdminAuthorizer The platform-wide or delegated per-tenant administration scope (Platform, ForTenant, IsPlatformWide, Tenant, and the TreeScope built from the PlatformScopeId / TenantScopePrefix constants, with ToAdminRequest), and the authorizer that evaluates it as a whole-scope Admin request against the access gate, refusing a key-filtered grant (IsAuthorizedAsync, AuthorizeAsync).
LatticeTenantLabel The single source of the derived tenant metric dimension (TagTenant, ForTree, ForTenant, Resolve, PlatformMeasurement, and the pre-built Platform / Default tags; PlatformTenant _platform_ for platform-owned series and DefaultTenant default).

Tree ownership guard

The tree registry puts every alias assignment to this seam before it writes anything - a resize's swap, a shadow-cutover restore's cutover or its revert to a previous alias, a schema remediation's cutover, and an administrative set-alias, system-origin maintenance included; removing an alias does not consult it. The core default allows every alias, and the installable apps package replaces it with a guard over its tree ownership ledger. See Ownership-bounded aliasing for the full contract.

Type Role
ITreeOwnershipGuard ValueTask<TreeOwnershipDecision> AuthorizeAliasAsync(string logicalTreeId, string physicalTreeId, string? derivedFrom, CancellationToken cancellationToken = default). derivedFrom is the logical tree the target was created for, read from the registry rather than supplied by the caller (null for an independent tree). An in-process service, not a grain; a guard that throws fails the alias change without writing it.
TreeOwnershipDecision In-process readonly struct: Allow(), or Deny(reason) with a non-blank, caller-safe reason, read back through Allowed and Reason. The default value denies. A denial surfaces as LatticeTreeOwnershipDeniedException.

Replication-facing seams and ambient contexts

Type Role
ILatticeMergeModeResolver Resolves a tree's declared LatticeMergeMode at commit time (Resolve(treeId)); null means not replicated. See Replication modes.
ILatticeOriginClusterIdResolver Supplies the local cluster id stamped on a WAL record when the mutation carries no origin (Resolve(treeId)). See WAL - Origin cluster id stamping.
LatticeVectorClockContext / LatticeHlcOverrideContext Ambient scopes (Current, With) that stamp a VersionVector frontier, or a source HLC verbatim, onto the mutations the current logical call authors.
LatticeReplayAdmissionContext / LatticeReplayAdmissionClass Declares a call chain Bulk (BeginBulkScope()) rather than the default Interactive when it must queue for a WAL replay permit, so the per-silo replay queue can bound and prioritise bulk walks.
CrdtShapeRegistry The typed CRDT shapes a tree's writes and receiver-side applies resolve (Register, TryGet), with a global fallback for every closed-shape mode; only OR-Map needs per-tree registration (AddOrMapShape).

CRDT support types

Type Role
Typed delta records (LwwRegisterDelta, OrSetDelta / OrSetDeltaDot, PnCounterDelta, GCounterDelta, GSetDelta, VersionVectorDelta, MvRegisterDelta, OrMapDelta<TKey, TValue> / OrMapDeltaEntry / OrMapDeltaTombstone, RgaDelta / RgaDeltaNode, OrFlagDelta, RwFlagDelta, RwSetDelta, BoundedRegisterDelta) The System.Text.Json-encoded delta payloads a CRDT write carries - the deltaBytes of ApplyCrdtDeltaAsync, and the author delta on a mutation's Delta slot. See Typed CRDT delta records.
MvRegisterEntry, OrMapEntry<TValue>, RgaNode The dot-tagged entries inside MvRegister, OrMap<TKey, TValue>, and Rga. See State Primitives.
ICrdtProvenanceDecoder / CrdtProvenanceDecoderRegistry Turns a CRDT's stored state or author deltas into ordered element-level CrdtMemberChange events (DecodeDeltas, DecodeState) and its live members into CrdtMemberValues (DecodeCurrentValue); the registry resolves the decoder by LatticeMergeMode or shape tag (TryGet), and CrdtProvenanceDecoderRegistry.Default carries one built-in decoder per typed CRDT mode - OrSetProvenanceDecoder, PnCounterProvenanceDecoder, VersionVectorProvenanceDecoder, MvRegisterProvenanceDecoder, OrMapProvenanceDecoder, SequenceProvenanceDecoder, OrFlagProvenanceDecoder, RwFlagProvenanceDecoder, GCounterProvenanceDecoder, GSetProvenanceDecoder, RwSetProvenanceDecoder, MaxRegisterProvenanceDecoder, and MinRegisterProvenanceDecoder, each exposing a static Instance. See State API surfaces.
CrdtMemberChange / CrdtMemberChangeKind / CrdtMemberValue / CrdtProvenanceDelta A decoded Added or Removed event (Element, Kind, ReplicaId, the causal Ordinal, optional WallClock), a live member (Element, ReplicaId, Ordinal), and the in-process (Delta, WallClock) input pair a decoder consumes.

Change history, events, and diagnostics

Type Role
EntryRevision / EntryHistorySource One revision of a key's timeline in an EntryHistoryPage (Hlc, Kind, SourceKey, OriginClusterId, ValuePreview, ValueLength, ValueTruncated, ValueHash, Delta, Mode, RetentionShape, EndKey, VectorClock), and which substrate served the page (None, View, WalWindow). See Change history.
LatticeTreeEventKind / LatticeEventConstants The kinds a LatticeTreeEvent carries (Set, Delete, DeleteRange, SplitCommitted, CompactionTriggered, CompactionCompleted, TreeDeleted, TreeRecovered, TreePurged, SnapshotCompleted, ResizeCompleted, ReshardCompleted, AtomicWriteCompleted), and the StreamNamespace (orleans.lattice.events) events are published on. See Events.
ShardDiagnosticReport / RecentSplit The per-shard entries of a TreeDiagnosticReport (ShardIndex, Depth, RootIsLeaf, LiveKeys, Tombstones, TombstoneRatio, OpsPerSecond, Reads, Writes, HotnessWindow, SplitInProgress, BulkOperationPending, SampleFailed), and one recently committed adaptive split (ShardIndex, AtUtc). The report itself carries TreeId, ShardCount, VirtualShardCount, TotalLiveKeys, TotalTombstones, Shards, RecentSplits, SampledAt, and Deep. See Diagnostics.
LatticeStorageUsageMetrics The process-wide sink behind the storage-usage observable gauges (Publish, PublishWal, PublishOverThreshold, StalenessHorizon). AddLattice registers it.
LatticeMetrics.WalReplayPermitWaitScope / LatticeMetrics.LeafSplitCompletionScope Allocation-free disposable scopes returned by LatticeMetrics.EnterWalReplayPermitWait and EnterLeafSplitCompletion, which feed the in-flight observable gauges. See Metrics.

Grain storage

Type Role
ILatticeBinaryPersistedState / LatticeGrainStorageSerializer The marker for a persisted state type Lattice writes through the Orleans binary serializer instead of the JSON grain-storage serializer, and the serializer AddLattice installs to do it (WritesBinary(stateType), Fallback). Every other state type is delegated, unchanged, to the serializer registered before it, because the JSON path cannot write a large opaque payload without first materialising it as one contiguous string.

Distributed lock and atomic actions

Type Role
ILatticeLockGrain, LockAcquireRequest, LockLease, LockToken, LockStatus The FIFO-fair distributed lock (AcquireAsync, TryAcquireAsync, RenewAsync, ReleaseAsync, GetStatusAsync) and its request (LeaseDuration, MaxWait), lease (Token, ExpiresAt, LeaseDuration), fencing token (FencingToken), and status (IsHeld, CurrentFencingToken, LeaseExpiresAt, QueueDepth) values. See Distributed lock.
IAtomicActionGrain, AtomicActionPlanBuilder, AtomicActionTreeWriteBuilder, AtomicActionPlan, AtomicActionStep, AtomicActionStepKind, AtomicActionEntry, AtomicActionOutcome, AtomicActionStatus The saga coordinator (ExecuteAsync, TryGetOutcomeAsync), the fluent plan builder (Step, TreeWrite with Upsert / Delete, Build), the plan and step shapes (Custom or TreeWrite steps), and the terminal outcome (Committed, Compensated, or CompensationFailed, with FailedStepIndex and FailureMessage). See Atomic actions.
IAtomicActionHandler, IAtomicActionContext, AtomicActionRegistrationBuilder A named, versioned forward / compensate pair (HandlerId, VersionTag, ForwardAsync, CompensateAsync), the context each effect receives (OperationId, Args, GrainFactory, CancellationToken), and the AddHandler registration surface AddLatticeAtomicAction supplies.

Exception reference

Every public exception type the core package defines. Those deriving from a BCL exception subclass implement ILatticeDomainFault; LeafProjectionStaleException also implements ILatticeLeafUnavailable, the marker for "this leaf cannot be activated right now".

Exception Base Carries Raised when
LatticeAuthorizationDeniedException UnauthorizedAccessException TreeId, Operation, SubjectId, Reason The registered ILatticeAccessGate denies a write, delete, CRDT, atomic, range-delete, bulk-load, lifecycle, or whole-tree read call, or the backup / restore authorization seam. LatticeTenantAdminAuthorizer.AuthorizeAsync raises it when the gate denies a tenant-administration scope or allows only a key-filtered subset of it, and add-on facades (for example telemetry, tenant administration, and app installation) raise it for their own capability denials. With the auth add-on's internal-origin enforcement registered, a direct external-client call to one of a tree's internal shard or leaf grains is refused with it. Nothing is persisted. A denied point or range read reports absence or an empty result instead (see Reading an empty range read under a gate).
LatticeReservedTreeNamespaceException InvalidOperationException TreeId Any ILattice call is addressed to an internal system tree (the _lattice_ prefix). Outside system origin it is also raised when a data-mutation call (write, delete, CRDT apply, bulk load) names a tree in the sys- namespace, a t/ tenant id the caller's active tenant does not own, or the all-trees authorization sentinel *, and when a SnapshotAsync destination names a _lattice_, sys-, or another tenant's t/ tree. Reads of sys- and t/ trees are not refused. App trees (a/{app}/{tree}) are ordinary trees and are not reserved; the app registry's sys-app- trees fall under sys-.
LatticeTreeNotRegisteredException KeyNotFoundException TreeId A tree-registry verb that changes an existing tree's entry - a shard-map change, a split's shard-index allocation, a per-tree configuration override, the projection-digest latch, or a WAL placement change - names a tree with no registry entry (never created, or purged). Nothing is created. SetHistoryRetentionAsync and SetPublishEventsEnabledAsync raise it for a purged tree; a tree that was never created is registered by them instead. The API facades report it as not found. See Tree Registry.
LatticeTenantAccessDeniedException Exception - Resolving a tree name through LatticeTenantExtensions fails closed: the caller's asserted active tenant fails validation against the caller's own membership (an anonymous caller can never act as a tenant), or, under an asserted tenant and outside system origin, the name is a sys- tree or a malformed t/ id that belongs to no tenant. The ILattice read and write paths also raise it (the snapshot cursor per page) when an active ITenantAdmissionController returns false for the tenant's read or write; the tenancy add-on's own controller never returns false, and signals a breach with LatticeQuotaExceededException instead. Never raised by the core defaults: the no-op resolver always resolves the default tenant and the no-op admission controller is inactive.
LatticeTreeOwnershipDeniedException Exception Reason The registered ITreeOwnershipGuard refuses an alias assignment; the alias is not written and no alias-change notification fires. Every assignment consults the guard, system-origin maintenance included - a resize's swap, a shadow-cutover restore's cutover or its revert to a previous alias, a schema remediation's cutover, and an administrative set-alias - so each can meet it, except that a resize swaps its alias after ResizeAsync has returned, so a refused swap is retried and leaves the resize in progress rather than reaching that caller. The core default guard never refuses. Deleting through an alias to a copy this tree does not own raises InvalidOperationException, not this exception (see DeleteTreeAsync). Reason is the guard's caller-safe reason.
LatticeReplicationModeMismatchException InvalidOperationException TreeId, DeclaredMode, AttemptedMode A write would break the single-shape rule of a tree declared for cross-cluster replication.
LatticeCrdtShapeNotRegisteredException InvalidOperationException TreeId An OR-Map write targets a tree with no registered (TKey, TValue) shape. Also raised, at any CRDT mode and with an empty TreeId, when a CRDT write reaches a leaf its shard has not yet attached to a tree - a routing or lifecycle race to retry, not a missing registration.
LatticeIdempotencyKeyMismatchException InvalidOperationException OperationId An operationId is re-submitted with a different key set (cross-tree: tree set or key set).
LatticeStateWriteFailedException Exception GrainType, GrainKey, FaultType, Conflict An atomic-write saga, a cross-tree atomic-write coordinator, or a WAL materialiser pin shard fails to persist its own durable state. The storage provider's exception is summarised in FaultType and the message rather than carried as the inner exception, so a client that does not reference the provider can still load it; a fault that is not a conflict is translated only when its exception type comes from such a provider assembly, and a BCL or Lattice exception propagates unchanged. Conflict is true when the write lost an optimistic-concurrency (ETag) check - typically a storage SDK retry of a write that had already landed: the grain deactivates so the next call reloads its durable state, and retrying the same operation with the same operation id is safe and neither loses nor double-applies a commit or abort decision. Every ILattice SetManyAtomicAsync / SetManyAtomicWhereAsync overload, and the cross-tree SetManyAtomicAsync / LatticeAtomicWriteBuilder.CommitAsync, re-attaches up to three attempts (after 1 s and 2 s) before surfacing a persistent conflict.
LatticeWriteRejectedException InvalidOperationException TreeId, Operation, Key, Reason A registered ILatticeWriteInterceptor rejects the value before commit. A dead-letter decision on a single-key write does not raise it (the value is diverted and the call completes normally), but in an atomic batch a dead-letter aborts the whole batch and surfaces here too.
LatticeQuotaExceededException InvalidOperationException TreeId, Dimension, Current, Limit, TenantId See Admission back-pressure.
LatticeSaturatedException InvalidOperationException TreeId, SaturationSource See Saturation back-pressure.
LatticeShuttingDownException InvalidOperationException - See Shutdown back-pressure.
LatticeWriteFencedException InvalidOperationException TreeId, SagaId The tree is write-fenced for a cross-cluster saga such as a restore cutover. Transient.
LatticeWalQuiescingException InvalidOperationException - A WAL partition is quiesced for an administrative placement move. Transient.
LatticeWalProviderMissingException InvalidOperationException TreeId, Partition, ProviderKey A WAL partition's pinned provider key does not resolve on this silo; the partition fails closed rather than re-routing to the baseline provider. The ILatticeAdmin WAL move verbs also raise it, before touching any log, when a move target key (or, for ReclaimMovedWalSourceAsync, the source key) does not resolve on the serving silo.
LatticeCursorRegistryPinExhaustedException InvalidOperationException - Opening a point-in-time cursor would push a saga decision registry shard the snapshot touches past LatticeOptions.MaxPinnedSagaDecisions; the cursor is not opened, though a snapshot spanning several shards can leave its pin on the shards that accepted it (see Point-in-time cursors).
LatticeCursorSnapshotExpiredException InvalidOperationException - A point-in-time cursor's registry pin expired between steps; open a fresh cursor.
LatticeSnapshotReplayBudgetExceededException InvalidOperationException - Opening a snapshot cursor would materialise more than LatticeOptions.MaxSnapshotReplayEntries baseline rows on its deepest shard.
LatticeSnapshotExpiredException InvalidOperationException - A snapshot cursor's frozen baseline can no longer be loaded; open a fresh cursor.
LeafProjectionStaleException InvalidOperationException - A leaf cannot rebuild its projection at activation: the WAL has been trimmed past its persisted projection checkpoint and no snapshot covers the gap. Every ProjectionRebuildPolicy value surfaces it (no automatic recovery path is integrated), and the cost triggers - a replay gap over MaxLeafReplayEntries or a checkpoint older than LeafProjectionRetention - never raise it. Resolved by an explicit RebuildLeafProjectionAsync, never by a retry. See Projection Rebuild.
ScanPageStalledException TimeoutException TreeId, ShardIndex, Operation, Phase, LeavesVisited, TimeoutSeconds, LeafInFlight, ConsecutiveZeroProgressStalls, LeafStranded, StrandedRecoveryApplications One shard range-scan page fill exceeded LatticeOptions.MaxScanPageStallDuration with nothing it could bank: before any leaf contributed, or during a snapshot baseline capture or a bounded range delete, which never bank. A fill that had already accumulated rows returns them as a short page instead, and an aggregate walk (counts, diagnostics, storage usage) banks its partial page. The resilient read scans resume it within their stall budget; the resilient range-delete drain propagates it (see Enumeration).
ShardActivationTimeoutException TimeoutException TreeId, ShardIndex, TimeoutSeconds A shard root's activation-readiness seed exceeded LatticeOptions.ActivationReadyTimeout; entry points that wrap the seed retry up to three attempts before surfacing it.
LatticeTransactionOutcomeUnavailableException TimeoutException TreeId, Key, KeyCount, TransactionIds A read's result depends on an atomic-write saga whose outcome the transaction registry could not be reached to establish. A point read raises it for its key; a multi-key read (GetManyAsync, the counts, key and entry enumeration) raises it, with a null Key, only when a key it read carried a prepared mutation and only after its own MaxScanRetries retry is exhausted. A registry that answers that the outcome is no longer known makes the key read as absent instead, on every read path. Retryable; never a guessed value. See When the registry cannot be reached.
LatticeLockConflictException Exception LockName ILatticeLockGrain.RenewAsync is presented a token that no longer holds the lock.
CompensationFailedException Exception StepIndex An atomic action's compensating effect faulted after its retry budget. See Atomic actions.
AtomicActionHandlerNotRegisteredException Exception HandlerId An atomic-action plan names a custom handler id the silo never registered; handler resolution fails closed.