Table of Contents

Orleans.Lattice.Api.TenantAdmin

This page documents Orleans.Lattice.Api.TenantAdmin 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 README.md, and llms.txt lists every page.

Transport-agnostic tenant administration and region-residency control facades for Orleans.Lattice multi-tenancy: one coherent, discoverable, authorized surface for the tenant lifecycle and each tenant's region residency, plus a tenant-scoped tree-administration facade. Every transport binding (the gRPC service, the MCP tool group) is a thin adapter over these surfaces, so the control semantics are written and tested once and no transport concern leaks into the control logic.

What is it?

This package is the control plane for the Orleans.Lattice.Tenancy companion. It mirrors the TreeAdmin packaging convention exactly: the contracts live in Orleans.Lattice.Api.Abstractions (under TenantAdmin/) - except ILatticeTenantScopedTreeAdmin and its TenantScopeRequiredException, which this package declares - the implementations here, the gRPC binding in a sibling package, and an MCP TenantAdmin tool group. It composes the existing tree-administration facade (ILatticeTreeAdmin) and the schema engine's in-process admin (ILatticeSchemaAdmin) rather than reimplementing them.

The facades exposed are:

  • ILatticeTenantAdmin - the tenant lifecycle: create, suspend, resume, delete, and author per-tenant resource quotas.
  • ILatticeTenantRegionAdmin - per-tenant region residency: authorize the allowed region set, set the residency set within it, read per-region status.
  • ILatticeTenantSelfService - the read-only self-awareness counterpart: which tenant the caller is operating as, which tenants it may see, and one tenant's lifecycle and per-region residency. It holds no lifecycle authority at all, so it is safe to expose wherever tenancy is enabled without granting an administrative capability.
  • ILatticeTenantScopedTreeAdmin - tree administration executed inside a single tenant's namespace (create/check/delete/recover/purge trees and manage per-tree schema policy), so a delegated tenant admin drives tree lifecycle without reaching outside its tenant.
  • ILatticeTenantAccessAdmin - tenant access administration: list, add, and remove a tenant's tenant-admin subjects, so membership can change after creation rather than being frozen at the create-time seed.
  • ILatticeTenantGrantAdmin - cross-tenant grant administration: the two-step agreement by which one tenant exposes a scope of its data to another (the granting tenant offers, the grantee approves or rejects, either party may revoke), plus a listing of a tenant's issued and received grants.
  • ILatticeTenantQuotaUsage - the read-only usage-against-quota report: per dimension, a tenant's consumption next to its steady-state and burst-adjusted ceilings.

The gRPC binding serves every facade above except ILatticeTenantScopedTreeAdmin, which no transport binding exposes. The MCP binding exposes the self-service reads and, behind its control opt-in, only the lifecycle, quota, and region-residency verbs (see the tenancy guide).

Core properties

  • Fail-closed authorization. Every lifecycle, region-residency, access, grant, and quota-usage operation authorizes the caller before it changes anything. An unauthenticated caller, or one the gate denies, is refused with a LatticeAuthorizationDeniedException (unified into TenantNotFoundException on the quota-usage read) and no change is made. The read-only self-service surface scopes its answers to the caller instead of refusing, and the tenant-scoped tree facade relies on the facades it wraps (see ILatticeTenantScopedTreeAdmin). The bindings additionally gate every lifecycle, region-residency, access, grant, and quota-usage call they serve behind an explicit opt-in (the gRPC binding's default-deny authorizer, the MCP binding's control-tool switch), so a cluster that does not enable it exposes none of them; the read-only self-service calls are deliberately left outside that opt-in.
  • Two-tier governance. Tenant lifecycle and allowed-region authorization are platform-operator actions (cluster-wide Admin on the reserved auth policy tree, which the gate's control-plane isolation grants only to a platform operator). Setting residency, reading status, tenant access administration, cross-tenant grant administration, and the quota-usage read are tenant-admin actions, authorized when the caller is that operator or a live admin subject on the tenant record (for a grant step, the record of the tenant whose side of the agreement the step belongs to). Both tiers are independent of the data-plane DefaultEffect, so an unmatched request always resolves to deny even under DefaultEffect = Allow.
  • Reserved default tenant. The well-known legacy-adoption default tenant can never be suspended, deleted, or given quotas, have its admin-subject set changed, or be named on either side of a cross-tenant grant offer; each fails closed with a ReservedTenantOperationException, because it names the cluster's own legacy state. Resuming it is allowed and is an active no-op, since it can never be suspended in the first place.
  • Idempotent lifecycle. Suspend/resume report whether they changed anything; create is not an idempotent upsert (a duplicate id fails closed with TenantAlreadyExistsException), so it can never reset or reuse another tenant's definition.
  • Create seeds admin subjects. Tenant visibility on the read-only ILatticeTenantSelfService surface resolves from the tenant record's admin-subject set, so a tenant created with none is mutable but invisible - even to the operator who just created it. CreateTenantAsync therefore takes an optional adminSubjects set and seeds it onto the new record. Omit it (or pass an empty set) and the calling subject is seeded, so the creator can always see what it created; supply a non-empty one and it is used verbatim (the caller is not added on top), which is how you hand a tenant to its delegated admins in a single call. Every entry must be a non-blank subject id, a blank or null entry fails closed with an ArgumentException, and duplicates collapse. A caller that cannot be resolved to a subject (an anonymous or system-origin create) seeds nothing rather than inventing an owner; grant access explicitly in that case. When a real identity directory provider is registered (anything but the default NullIdentityDirectory) and LatticeIdentityDirectoryOptions.ValidationRequired is set, every id in an explicitly supplied set must resolve - checked after authorization and before the write, exactly as ILatticeTenantAccessAdmin.AddAdminSubjectAsync checks an added subject - and an id the directory resolves to nothing fails the whole create with a LatticeDirectoryValidationException (an ArgumentException), so create cannot be used to grant what add would refuse. The caller-seeded default is not checked, since it is the authenticated caller's own resolved subject. The seeded set is echoed back on TenantCreationResult.AdminSubjects.
  • Authorize, then validate, then write. Every ILatticeTenantAdmin lifecycle verb first parses the tenant id (a purely syntactic step over the caller's own argument, which on create also rejects an id shadowing the sys- or _lattice_ reserved namespaces with an ArgumentException), then authorizes through the fail-closed gate, and only then inspects its remaining arguments or touches the registry. The region, access, and grant verbs also check their other arguments' syntax (a region set, a subject id, a grant scope and operation set) before the gate; their gate reads the tenant record to evaluate admin-subject membership, but reports a missing tenant to a non-operator as a denial, and no reserved-tenant check runs until it admits the call. So a denied caller learns nothing from the admin-subject list it supplied, from whether the tenant already exists, or from whether it is the reserved default tenant: every one of those checks sits behind the gate and cannot be used as an oracle.
  • Cascading delete. Deleting a tenant first suspends it, so no new tenant-scoped admission can race the delete, then cascades the delete to every tree the tenant owns (each t/{tenantId}/* tree is soft-deleted) before the registry record is removed. An interrupted delete therefore leaves a suspended, retriable record rather than orphaned trees.
  • Quota authoring. SetTenantQuotasAsync replaces a tenant's resource quotas and burst allowance in one platform-operator action. Each ceiling (MaxBytes, MaxKeys, MaxMemoryBytes, MaxTreeCount, MaxOpsPerSecond) is null for unbounded on that dimension; passing TenantQuotasDescriptor.Unbounded lifts every cap again. A bounded ceiling must be non-negative, and so must BurstPercent, the transient headroom above the bounded ceilings: a negative value on any of them fails closed with an ArgumentException and writes nothing. The reserved default tenant can never be given quotas. The quotas now in effect come back on TenantQuotasUpdateResult.Quotas, and stay readable on ILatticeTenantSelfService.GetTenantAsync (TenantStatusReport.Quotas) for any caller that can see the tenant.
  • Write contention. Every mutating verb commits through the tenancy registry's optimistic read-merge-write, so under sustained contention on one tenant's record it can fail with the tenancy package's TenantRegistryConcurrencyException (see Store write contention) instead of its domain outcome; the write that exhausted its retries is not applied, and the call can be retried.

Registration

Register the facade on the silo (it requires the Orleans.Lattice.Tenancy package):

  • AddLatticeTenantAdminApi(this ISiloBuilder builder, Action<LatticeApiTenantAdminOptions>? configure = null) - registers ILatticeTenantAdmin, ILatticeTenantRegionAdmin, ILatticeTenantAccessAdmin, ILatticeTenantGrantAdmin, and the read-only ILatticeTenantSelfService and ILatticeTenantQuotaUsage, together with the fail-closed authorizers they consult, and a residency listener that completes the drain of the silo's own region: a region dropped by SetResidencyAsync moves Draining -> Offline -> Removed on its own. A region it adds stays at Provisioning until an operator advances it with TenantRecord.TryPromoteRegionStatus, because nothing backfills the tenant's existing data into it (see Lifecycle states).
  • AddLatticeTenantScopedTreeAdminApi(this ISiloBuilder builder) - registers ILatticeTenantScopedTreeAdmin.

Each call is order-guarded at registration time: a misordered call throws an InvalidOperationException with an actionable message rather than failing obscurely at silo start.

Call Must run after Because
AddLatticeTenantAdminApi AddLatticeTenancy() The facade operates on the tenancy engine's tenant registry, so it would otherwise have no lifecycle store to act on.
AddLatticeTenantScopedTreeAdminApi AddLatticeTreeAdminApi() It delegates the whole-tree lifecycle verbs to that facade.
AddLatticeTenantScopedTreeAdminApi AddLatticeSchemaEnforcement() It delegates the per-tree schema-policy verbs to the schema engine's in-process ILatticeSchemaAdmin, which that call registers.

Each is idempotent: repeating the call layers any supplied configuration delegate but performs the structural wiring only once.

LatticeApiTenantAdminOptions currently exposes no settings - it is the reserved per-facade options seam, mirroring the sibling control facades - so the configure delegate can be omitted. The knobs that shape tenancy behaviour live on the Orleans.Lattice.Tenancy options instead.

Facade method signatures

ILatticeTenantAdmin

The tenant lifecycle surface (published in Orleans.Lattice.Api.Abstractions, namespace Orleans.Lattice.Api.TenantAdmin). Each method corresponds to one gRPC RPC in the binding.

Method Signature
CreateTenantAsync Task<TenantCreationResult> CreateTenantAsync(string tenantId, IReadOnlyCollection<string>? adminSubjects = null, CancellationToken cancellationToken = default)
SuspendTenantAsync Task<TenantStatusChangeResult> SuspendTenantAsync(string tenantId, CancellationToken cancellationToken = default)
ResumeTenantAsync Task<TenantStatusChangeResult> ResumeTenantAsync(string tenantId, CancellationToken cancellationToken = default)
DeleteTenantAsync Task<TenantDeletionResult> DeleteTenantAsync(string tenantId, CancellationToken cancellationToken = default)
SetTenantQuotasAsync Task<TenantQuotasUpdateResult> SetTenantQuotasAsync(string tenantId, TenantQuotasDescriptor quotas, CancellationToken cancellationToken = default)

ILatticeTenantRegionAdmin

The per-tenant region-residency surface. Of the region sets it authors the operator-owned allowed set and the tenant-owned resident set, leaving the physical topology to the deployment. Residency is always a subset of the allowed set; the last resident region can never be removed.

Method Signature
AuthorizeAllowedRegionsAsync Task<TenantRegionAuthorizationResult> AuthorizeAllowedRegionsAsync(string tenantId, IReadOnlyCollection<string> allowedRegions, CancellationToken cancellationToken = default)
SetResidencyAsync Task<TenantResidencyChangeResult> SetResidencyAsync(string tenantId, IReadOnlyCollection<string> residencyRegions, CancellationToken cancellationToken = default)
GetTenantRegionStatusAsync Task<TenantRegionStatusReport> GetTenantRegionStatusAsync(string tenantId, CancellationToken cancellationToken = default)

Each authored set is a replacement, not a delta: the supplied collection becomes the whole set, so a currently-allowed or currently-resident region absent from it is revoked or drained.

Concurrent residency changes

The tenant record is CRDT-merged, and its per-region status is a map keyed by region id, so two removals of different regions do not conflict - the join keeps both. A guard that only checked before writing would therefore let two concurrent callers each drop a different region and leave the tenant resident nowhere. SetResidencyAsync closes that by checking the invariant twice: once before the write, and again on the record the registry commits. Because the pre-write check has already refused the single-writer case, a merged record with no resident region can only mean a concurrent removal, so the call repairs the regions it drained (restoring their prior status at a strictly later stamp), leaves the other caller's removal standing, and refuses with TenantLastRegionException. Only the caller whose commit observes the emptied set is refused; the other caller's removal, committed while a region was still resident, succeeds and stands. Either way the tenant keeps at least one resident region, and retrying the refused call afterwards meets the ordinary pre-write guard.

For the same reason both write operations report the merged record rather than the caller's pre-write view, so a concurrent change from another writer is present in the returned region set instead of silently absent.

Authorization tiers

Operation Tier Who may call it
AuthorizeAllowedRegionsAsync Operator only Cluster-wide Admin on the reserved auth policy tree. A tenant admin is denied - the allowed set is the operator's containment boundary and a tenant must not be able to widen it.
SetResidencyAsync Operator or tenant admin That operator, or a live admin subject on the tenant record.
GetTenantRegionStatusAsync Operator or tenant admin Same as above. Read-only.

Both tiers are independent of the data-plane DefaultEffect, so an unmatched request resolves to deny even under DefaultEffect = Allow. Every transport binding inherits this gate rather than re-implementing it, so neither tier can be widened by reaching the facade over the wire.

Domain exceptions

Each failure mode is a distinct exception type so a transport binding can map it to a specific status rather than an opaque fault:

Exception Raised when
TenantNotFoundException The tenant is not registered - reported to a platform operator. A non-operator caller naming an unknown tenant on a tenant-admin-tier verb gets LatticeAuthorizationDeniedException instead, so it cannot probe for a tenant's existence.
TenantRegionNotAllowedException Residency was set to a region outside the allowed set, or an allowed region a tenant is still resident in was revoked.
TenantLastRegionException The change would remove the tenant's last resident region - either as submitted, or once merged with a concurrent removal (see Concurrent residency changes).
LatticeAuthorizationDeniedException The caller does not hold the required tier.

ILatticeTenantSelfService

The read-only tenant self-awareness surface (published in Orleans.Lattice.Api.Abstractions, namespace Orleans.Lattice.Api.TenantAdmin). It never creates, suspends, resumes, or deletes a tenant.

Method Signature
GetCurrentTenantAsync Task<TenantDescriptor> GetCurrentTenantAsync(CancellationToken cancellationToken = default)
ListAccessibleTenantsAsync Task<IReadOnlyList<TenantDescriptor>> ListAccessibleTenantsAsync(CancellationToken cancellationToken = default)
GetTenantAsync Task<TenantStatusReport> GetTenantAsync(string tenantId, CancellationToken cancellationToken = default)

GetCurrentTenantAsync needs no administrative tier because it reports only the caller's own context, and a caller with no tenant in context resolves to the reserved default tenant. That is not the same as being ungated: GetCurrentTenantAsync and ListAccessibleTenantsAsync each re-run the fail-closed tenant resolution first, so a caller whose asserted active tenant was refused gets a LatticeTenantAccessDeniedException instead of a report for a tenant it does not hold.

ListAccessibleTenantsAsync returns, in ascending ordinal tenant-id order, the tenants the caller is a registered administrator of plus its own current tenant when that is non-default, so an anonymous or non-privileged caller under the default tenant gets an empty list. GetTenantAsync deliberately unifies "no such tenant" and "you may not see this tenant" into a single TenantNotFoundException, so no caller can probe for the existence of a tenant outside its authority.

ILatticeTenantScopedTreeAdmin

Tree administration executed inside one tenant's namespace (namespace Orleans.Lattice.Api.TenantAdmin). Names are the tenant's unqualified tree names; the facade injects the tenant segment.

Method Signature
CreateTreeAsync Task<TreeCreationResult> CreateTreeAsync(string name, int? shardCount = null, int? maxLeafKeys = null, int? maxInternalChildren = null, CancellationToken cancellationToken = default)
CheckTreeExistsAsync Task<TreeExistenceResult> CheckTreeExistsAsync(string name, CancellationToken cancellationToken = default)
DeleteTreeAsync Task<TreeDeletionStatus> DeleteTreeAsync(string name, CancellationToken cancellationToken = default)
RecoverTreeAsync Task<TreeDeletionStatus> RecoverTreeAsync(string name, CancellationToken cancellationToken = default)
PurgeTreeAsync Task<TreeDeletionStatus> PurgeTreeAsync(string name, bool confirm, CancellationToken cancellationToken = default)
GetTreeDeletionStatusAsync Task<TreeDeletionStatus> GetTreeDeletionStatusAsync(string name, CancellationToken cancellationToken = default)
SetSchemaPolicyAsync Task SetSchemaPolicyAsync(string name, LatticeSchemaPolicy policy, CancellationToken cancellationToken = default)
ClearSchemaPolicyAsync Task<bool> ClearSchemaPolicyAsync(string name, CancellationToken cancellationToken = default)
GetSchemaPolicyAsync Task<LatticeSchemaPolicy?> GetSchemaPolicyAsync(string name, CancellationToken cancellationToken = default)

Every method on this facade requires an active tenant. With none in scope the call fails closed with a TenantScopeRequiredException (declared in this package, namespace Orleans.Lattice.Api.TenantAdmin) rather than silently operating on the cluster-global namespace.

The facade composes the target id from the ambient active-tenant assertion as supplied and does not use the two-tier gate above. Its tree verbs delegate to ILatticeTreeAdmin, which resolves and authorizes the composed id through the access gate as it would for any caller - whole-tree Admin to create (also checked here before any quota accounting), TreeLifecycle to delete, recover, or purge, and Read to check existence or deletion status - so the caller's membership validation and tenancy's isolation apply there. Its schema-policy verbs delegate to the in-process ILatticeSchemaAdmin, which performs no authorization of its own (see Capability gate), and the facade adds no check for them.

ILatticeTenantAccessAdmin

The tenant access-administration surface (published in Orleans.Lattice.Api.Abstractions, namespace Orleans.Lattice.Api.TenantAdmin). Every operation is a tenant-admin action - authorized for the platform operator or a live admin subject of the target tenant - and a caller holding neither is told denied, never not found, so the surface cannot be used to enumerate tenants.

Method Signature
ListAdminSubjectsAsync Task<TenantAdminSubjectReport> ListAdminSubjectsAsync(string tenantId, CancellationToken cancellationToken = default)
AddAdminSubjectAsync Task<TenantAdminSubjectChangeResult> AddAdminSubjectAsync(string tenantId, string subjectId, CancellationToken cancellationToken = default)
RemoveAdminSubjectAsync Task<TenantAdminSubjectChangeResult> RemoveAdminSubjectAsync(string tenantId, string subjectId, CancellationToken cancellationToken = default)

Add and remove are idempotent (Changed reports whether the set moved). When a real identity directory provider is registered (anything but the default NullIdentityDirectory) and LatticeIdentityDirectoryOptions.ValidationRequired is set, an add of a subject that is not already a member - checked after authorization and the reserved-tenant check - requires the id to resolve: an id the directory resolves to nothing is refused with a LatticeDirectoryValidationException (an ArgumentException) before the write. Resolution is the only directory check. Unlike the UpsertGroupAsync and AddMemberAsync paths of the authorization-admin facade, the principal's kind is not checked, so an id that resolves to a group is accepted. Admin-subject membership is matched against the caller's own subject id with no group expansion, so such an entry never authorizes the group's members. Removing a tenant's last admin subject is refused with TenantLastAdminSubjectException - including when two concurrent removals of different subjects would together empty the set, which is detected on the merged record and repaired before the refusal - and the reserved default tenant's membership can never be changed.

ILatticeTenantGrantAdmin

The cross-tenant grant surface. A grant is a two-step agreement: an offer creates it Pending and authorizes nothing; only the grantee's approval makes it Active.

Method Signature
ListGrantsAsync Task<TenantGrantReport> ListGrantsAsync(string tenantId, CancellationToken cancellationToken = default)
OfferGrantAsync Task<TenantGrantChangeResult> OfferGrantAsync(string granterTenantId, string granteeTenantId, string scope, TenantGrantAccess operations, CancellationToken cancellationToken = default)
ApproveGrantAsync Task<TenantGrantChangeResult> ApproveGrantAsync(string granterTenantId, string granteeTenantId, string scope, CancellationToken cancellationToken = default)
RejectGrantAsync Task<TenantGrantChangeResult> RejectGrantAsync(string granterTenantId, string granteeTenantId, string scope, CancellationToken cancellationToken = default)
RevokeGrantAsync Task<TenantGrantChangeResult> RevokeGrantAsync(string granterTenantId, string granteeTenantId, string scope, CancellationToken cancellationToken = default)

Each step is authorized for the platform operator or a live admin subject of one specific tenant: the granting tenant offers, the grantee tenant approves or rejects, either party may revoke, and a listing is the listed tenant's own. scope names the granting tenant's data the grant covers. It is stored as supplied and matched against the full id of the tree a request names, so write it in the granting tenant's composed form - a tree id such as t/acme/orders, or a prefix of one that ends on a / segment boundary, such as t/acme/ - because an unqualified name such as orders covers no tree. operations must not be TenantGrantAccess.None, and the two tenants must differ: a blank scope, an empty operation set, or the same tenant on both sides fails with an ArgumentException. An offer never requires the grantee to exist and may not name the reserved default tenant on either side. A grant that was never offered - or whose granting tenant is not registered - is reported identically as TenantGrantNotFoundException; asking for the state a grant is already in is an idempotent no-op, and a transition the lifecycle forbids (for example approving a rejected or revoked grant) raises TenantGrantTransitionException before any write. A step that races the other party's concurrent transition and loses the merge is refused with the same exception, carrying the state that won.

The access gate honours an active grant's Write operation, but the data plane refuses a user-origin write that names another tenant's t/ tree before the gate is consulted, so through the data surface only a grant's read operations take effect.

ILatticeTenantQuotaUsage

The read-only usage-against-quota surface.

Method Signature
GetQuotaUsageAsync Task<TenantQuotaUsageReport> GetQuotaUsageAsync(string tenantId, CancellationToken cancellationToken = default)

The report carries, per dimension (Bytes, Keys, MemoryBytes, TreeCount, OpsPerSecond), the consumption, the steady-state ceiling, the burst-adjusted ceiling, the live overage, and the accrued metered overage, together with the EnforcementScope the figures were read under. OpsPerSecond carries only its two ceilings: the engine never samples a sustained operation rate, so its usage is always null (not measured). A platform operator may read any tenant and a live tenant admin only its own; an unauthorized tenant and an absent one are unified into a single TenantNotFoundException, so the call cannot probe for tenant existence. Authoring quotas remains the operator-only SetTenantQuotasAsync.

Authorization seams

The two fail-closed authorizers the facades consult are public types of this package (namespace Orleans.Lattice.Api.TenantAdmin). Both honour a system-origin bypass for trusted co-hosted infrastructure and are independent of the data-plane DefaultEffect:

  • TenantAdminAccessAuthorizer - the platform-operator gate for the lifecycle verbs. AuthorizeTenantAdminAsync throws LatticeAuthorizationDeniedException unless the caller holds whole-scope Admin on PlatformOperatorScope (the reserved authorization policy tree), refusing a key-filtered allow; IsTenantAdminAuthorizedAsync is its non-throwing probe.
  • TenantRegionResidencyAuthorizer - the two-tier gate for the tenant-tier verbs. AuthorizeOperatorAsync is the operator tier. AuthorizeTenantAdminAsync admits the platform operator or a live admin subject on the tenant record and returns that record, reporting an unknown tenant as TenantNotFoundException to the operator and as a denial to anyone else. TryAuthorizeTenantAdminAsync returns null instead of throwing - for a missing tenant as well as a denial - so a verb either of two tenants may perform can consider both sides.

Public model types

Results and exceptions live in Orleans.Lattice.Api.Abstractions under TenantAdmin/Model/.

Type Kind Purpose
TenantCreationResult result The newly created tenant, with the admin subjects seeded onto it.
TenantDescriptor model One tenant's identity and lifecycle status, as reported by the read-only self-service surface.
TenantStatusReport result One tenant's read-only lifecycle status, authored Quotas, and per-region residency rows.
TenantStatusChangeResult result Suspend/resume outcome; Changed reports whether state moved.
TenantDeletionResult result Deletion outcome, including the count of trees cascaded.
TenantQuotasDescriptor model A tenant's per-dimension resource ceilings (null = unbounded) and BurstPercent; Unbounded sentinel and IsUnbounded predicate.
TenantQuotasUpdateResult result The tenant id and the quotas now in effect after authoring.
TenantLifecycleStatus enum Active / Suspended.
TenantRegionAuthorizationResult result The resulting allowed region set.
TenantResidencyChangeResult result The regions this call began adding (now Provisioning) and removing (now Draining), and the resulting per-region status rows.
TenantRegionStatusReport result Per-region rows (TenantRegionStatusDescriptor), ordered by region id.
TenantRegionStatusDescriptor model One region's allowed flag and lifecycle status.
TenantRegionLifecycleStatus enum None / Provisioning / Backfilling / Online / Draining / Offline / Removed.
TenantNotFoundException exception No tenant with that id is registered.
TenantAlreadyExistsException exception A tenant with the same id is already registered.
ReservedTenantOperationException exception Attempted suspend, delete, set-quotas, an admin-subject add / remove, or a cross-tenant grant offer on the reserved default tenant.
TenantRegionNotAllowedException exception A residency region is not in the allowed set (or a revoked region is still resident).
TenantLastRegionException exception The change would remove the last resident region, as submitted or once merged with a concurrent removal.
TenantAdminSubjectReport result A tenant's live admin-subject set, in ordinal order.
TenantAdminSubjectChangeResult result An add / remove outcome: the subject, Changed, and the resulting admin-subject set.
TenantLastAdminSubjectException exception The removal would leave the tenant with no admin subjects.
TenantGrantDescriptor model One cross-tenant grant: granting and grantee tenant, Scope, Operations, lifecycle State, and GrantId.
TenantGrantReport result A tenant's Issued and Received grants, in every lifecycle state.
TenantGrantChangeResult result A grant step's outcome: the Grant as committed and Changed.
TenantGrantAccess enum None / Read / Write / ReadWrite - what a grant authorizes once active.
TenantGrantLifecycleState enum Active / Pending / Rejected / Revoked.
TenantGrantNotFoundException exception No such grant has been offered (reported identically when the granting tenant is not registered).
TenantGrantTransitionException exception The grant's lifecycle forbids the requested transition; carries the current and requested states.
TenantQuotaUsageReport result A tenant's usage against its quotas: one TenantQuotaDimensionUsage per dimension, BurstPercent, the authored Quotas, HasUsage, and the EnforcementScope.
TenantQuotaDimensionUsage model One dimension's Usage (null = not measured), Limit (null = unbounded), BurstLimit, live Overage, and accrued MeteredOverage.
TenantQuotaEnforcementScope enum GlobalConverged (the converged cross-cluster total) / PerCluster (this cluster's local share only).
ApiTenantAdminTypeAliases static class The stable oitn.-prefixed Orleans serialization aliases of the tenant-admin contract types.

See also