Table of Contents

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:

  1. Membership. Create the groups (line-operators, auditors) on both sites and add alice and bob to them (users are plain member ids - the directory keeps no user records - and carol is in no group).
  2. 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 (only alice may read/write the single config/threshold key), and a tree grant (auditors read the whole tree). Then exercise writes, a read, and deletes as alice and bob and watch the gate allow or deny each one.
  3. 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, alice sees only her two remaining stations (her config-key grant covers point reads and writes, not range reads), and carol, who is in no group, sees nothing.
  4. Cross-cluster convergence. Revoke alice's config-key grant on site-a only and watch the revoke replicate onto site-b's policy tree with no direct write to site-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 AddLatticeAuth pays 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 use https:// peer endpoints and leave receiver authentication on.
  • The gRPC ports default to 17001 / 17002, and the two clusters also bind Orleans silo ports 11111 / 11112 and gateway ports 30000 / 30001; change them in Program.cs if 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.

Feature docs