Cross-Cluster Authorization
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 README.md, and llms.txt lists every page.Looking for a minimal, single-silo introduction to the authorization layer? Start with the Authorization sample, which demonstrates users, groups, and nested groups on one silo. This sample adds a second cluster and cross-cluster policy convergence on top of that same layer.
What it shows
The Orleans.Lattice authorization layer end to end: membership (users and
groups), a default-deny policy, and per-tree / per-key / per-prefix rules
enforced on every operation. It stands up two independent in-process Orleans
clusters (site-a and site-b) wired together with Orleans.Lattice
replication over gRPC, and enrols the reserved membership and policy system
trees into that replication (the system-tree replication special case) so an
authorization change on one cluster converges onto the other.
The demo runs in four acts:
- Membership. Create the groups (
line-operators,auditors) on both sites and addaliceandbobto them (users are plain member ids - the directory keeps no user records - andcarolis in no group). - Rules and enforcement. Author three rules under a default-deny policy -
a prefix grant (operators read/write/delete/range the
station/subtree), a key grant (onlyalicemay read/write the singleconfig/thresholdkey), and a tree grant (auditors read the whole tree). Then exercise writes, a read, and deletes asaliceandboband watch the gate allow or deny each one. - Read visibility. A point read of a key the caller lacks read permission
for is soft-denied (returns absent, not an error), and a range read returns
only the keys the caller is authorized to read -
bob(auditor) sees all keys,alicesees only her two remaining stations (her config-key grant covers point reads and writes, not range reads), andcarol, who is in no group, sees nothing. - Cross-cluster convergence. Revoke
alice's config-key grant onsite-aonly and watch the revoke replicate ontosite-b's policy tree with no direct write tosite-b.
Every subject's operations run under an ambient credential
(LatticeCredentialContext.Use) that flows to the grains on the Orleans request
context; a small custom ILatticeCredentialAuthenticator maps a demo token to a
subject id, and the membership directory expands the subject's group
memberships. The groups, memberships, and rules are written through the
silo-side membership directory and policy store, which run under system origin
and never consult the gate, so they need no prior grant; the tree's data keys
are seeded as the configured bootstrap administrator, which the gate allows
before any rule exists.
Run it
dotnet run --project samples/CrossClusterAuthorization
Expected output
Starting two Orleans clusters (site-a, site-b) with the auth stack...
Both clusters ready and peered over gRPC.
== Act 1: create users and groups ==
alice in line-operators, bob in auditors, carol in no group.
== Act 2: author per-tree / per-key / per-prefix rules ==
As alice (line-operators):
write station/1/status -> allowed
write config/threshold -> allowed
write secret/recipe -> DENIED (no rule -> deny)
As bob (auditors, read-only):
read secret/recipe -> 'caramel'
write station/2/status -> DENIED (read-only -> deny)
delete station/2/status -> DENIED (read-only -> deny)
As alice (line-operators):
delete station/3/status -> allowed (prefix grant allows delete)
== Act 3: read visibility (point + range) ==
Point read of the secret recipe:
bob -> 'caramel' (auditor)
carol -> (hidden) (low-privilege: soft-denied)
Range read of the whole tree returns only authorized keys:
bob sees 4 keys (auditor: all)
alice sees 2 keys (her station keys)
carol sees 0 keys (nothing)
== Act 4: a revoke on site-a converges to site-b ==
Before: alice writing config/threshold on site-b -> allowed
Revoked 'alice-config' on site-a only. Waiting for site-b to converge...
site-b policy tree caught up: True (after 1s)
site-b live gate already denies alice: False
[OK] the revoke authored on site-a converged onto site-b via system-tree replication.
Convergence semantics
The policy tree is one of the reserved system trees enrolled into replication,
so the revoke propagates to site-b's policy tree within the shipping window
(about a second on loopback). That authoritative state convergence is what the
sample asserts on.
Each site keeps a compiled read-through snapshot of its policy tree that its
gate consults, and refreshes that snapshot when it observes the policy change.
The site-b live gate already denies alice line reports whether site-b's gate
has already rebuilt its snapshot from the converged tree at the moment we
poll; it may still read False immediately after convergence because the
snapshot refresh is decoupled from the tree write. The revoke is durably
converged either way - the rule is gone from site-b.
When to use
- Multi-cluster deployments that must apply a single authorization policy uniformly across regions, where a grant or revoke authored in one region must become effective in the others without a central policy service.
- Any deployment that needs per-tree, per-key, or per-prefix authorization with a default-deny posture and group-based subjects.
When not to use
- Single-cluster deployments that do not replicate - you still get the full
authorization layer from
AddLatticeMembership+AddLatticeAuth, without the replication and gRPC wiring this sample adds for the cross-cluster act. See the single-silo Authorization sample for that shape. - Deployments that do not need authorization at all. The layer is opt-in; a host
that never calls
AddLatticeAuthpays nothing on the data path.
Notes on this sample
- Uses plaintext HTTP/2 (h2c) on loopback - each site binds Kestrel to HTTP/2
with no certificate and grpc-dotnet speaks h2c by prior knowledge over an
http://address - and turns receiver authentication off, because this is a loopback demo with no secret material. Production must usehttps://peer endpoints and leave receiver authentication on. - The gRPC ports default to
17001/17002, and the two clusters also bind Orleans silo ports11111/11112and gateway ports30000/30001; change them inProgram.csif those are taken on your machine. - The bootstrap administrator (
root-admin) seeds the demo data before any rule grants access; the reserved membership and policy trees are written by the silo-side directory and policy store under system origin, not through it. Production should keep the bootstrap set as small as possible and grant everything else through rules.