Sessions across a node's cells
The question every architect asks about a multi-cell platform is some version of this: if I sign in against one cell and my next request lands on another, what happens? The unsatisfying answers are a sticky load balancer, which turns a cell into a single point of failure, or a shared session cache, which turns one box into the thing the whole estate depends on.
System9 answers it in the data model instead. A session is node state, not cell state. It converges across the cells of its node, so any cell of that node can validate it locally, with no session-affinity routing and no shared cache to lose.
What converges, and how far
Session material is not one undifferentiated blob. Different artifacts have different blast radii, and each is declared separately:
| Artifact | Converges | Scope | Why that scope |
|---|---|---|---|
| Live sessions | Yes | Across the node's cells | Any cell of the home node validates locally, so a request can land anywhere |
| Session revocations | Yes | Across the node's cells | A revoked session must not validate on a peer that has not heard yet |
| Account-recovery tokens | Yes | Across the node's cells | Recovery survives a home-cell failure, and the consumed state converges so the token cannot be replayed on a second cell |
| OAuth authorization codes | Yes | Across the node's cells | A code issued on one cell is exchanged on another; consumed state converges so it is genuinely single-use |
| OAuth device codes | Yes | Across the node's cells | Mint on one cell, approve on a second screen served by another, poll from a third |
| OAuth consents | Yes | Across the node's cells | An approval granted once is honoured on whichever cell serves the next authorize call |
| OAuth token revocations | Yes | Across the node's cells | Offline token verification is stateless, so a converged denylist is the only revocation signal there is |
| Password credentials | Yes | Across the organization's nodes | The stronger guarantee: any cell of any node can authenticate the user through a cell or a whole-node failure |
| Auth event stream | No | Stays on its cell | Each cell owns its own hash-chained audit stream; merging chains across cells would break the chain invariant |
Two rows deserve the emphasis.
Credentials converge further than sessions. A session is scoped to the node that issued it, but the password credential converges across every node in the organization, because the user record it belongs to is already organization-wide. That is deliberately the stronger resilience guarantee: losing a node costs the sessions it issued, but never the ability to sign in again. The stored hash is organization-private and never crosses an organization boundary.
Single-use really is single-use. For recovery tokens, authorization codes and device codes, it is not the token that must converge so much as the fact that it has been spent. Replicating the consumed state is what stops the classic multi-cell replay: redeem on cell A, redeem the same code again on cell B before anything catches up.
Revocation is the load-bearing direction
Convergence for availability is the easy half. The half that has to be right is the one that takes authority away, because a delay there is a window in which a revoked session still works.
Revocations therefore travel by two paths at once:
- Convergence, as a denylist that broadcasts to every cell of the node, the same way a certificate revocation list works. This is the durable path and it is the one that is eventually authoritative.
- A revocation event on the bus, published by a revocation worker and applied by a consumer on each cell. This is the fast path, and it exists so the practical latency of a logout is not the replication interval.
Neither path is trusted to be instant, which is why the fallback below is written to fail closed against revocation rather than against availability.
The convergence window, and the fallback that closes it
Replication is fast, not instantaneous. There is a narrow window between a login on one cell and that session being present on its peers. A cross-cell session validator closes it.
When a cell is handed a session it does not have locally, it may ask its peers:
Figure I4: the cross-cell session fallback. The first gate is the one that matters. Before a cell will ask anyone about an unknown session, it checks its own converged revocation store, and a hit there ends the request without a single peer being contacted. That ordering is the whole design: a peer that has not yet converged a revocation must never be able to hand back a session this cell already knows is dead. Peers are only ever queried within the node, because that is the boundary sessions are scoped to.
Table equivalent
| Step | Behaviour |
|---|---|
| Local revocation check | Runs first. A known revocation refuses immediately and skips the peer query entirely |
| Peer selection | Same node only, capped at a small number of peers |
| Timeout | Short, per peer, so an unreachable peer cannot stall a request |
| On success | The session is imported locally and briefly cached, so the next request needs no fan-out |
| On failure | Treated as no session. There is no partial or optimistic grant |
The fallback is explicitly a fallback for the convergence window and should rarely fire in steady state. If it is firing constantly, the thing to investigate is replication health, not session handling. It is also configurable and can be switched off entirely, in which case a session is simply unavailable until it converges.
How a peer verifies a token without a shared secret
A session token has to be verifiable by whichever cell receives it, and the naive way to arrange that is to give every cell the same signing secret. That makes the compromise of any one cell the compromise of all of them.
System9 splits the token by who needs to verify it:
| Token | Signature | How it is verified |
|---|---|---|
| Same-cell | Symmetric (HS256) | The issuing cell verifies with its own key, which never leaves it |
| Cross-cell | Asymmetric (EdDSA), signed with the node's sign identity | The peer resolves the public half from the issuer's published key set, matched by key ID |
Figure I5: cross-cell token verification. No shared secret crosses the boundary. The issuer signs with a private key it keeps; the verifier fetches only public material from a standard key set endpoint and selects the right key by its ID, which is what makes key rotation a non-event. Verification is fail-closed: anything wrong with the token, the issuer, or the key lookup yields no identity at all rather than a partially trusted one.
Identity is stamped only from the verified claims of a genuinely signed token. A claimed identity in a header is not an identity.
Beyond the node: sessions do not cross, verification does
A session converges across the cells of its node and stops there. That boundary is intentional, and it is the same distinction the platform draws everywhere:
State that must converge is replicated. Everything else is verified where it is presented.
Across nodes, authentication is served by signed-token verification against published keys, not by copying live session rows into every node in the organization. Sessions are high-churn state, and broadcasting them organization-wide would multiply the most volatile rows in the estate to answer a question that public-key verification already answers.
What does converge organization-wide is the credential, so a user whose home node is gone can still authenticate somewhere else and obtain a new session there.
What a session carries
A session is more than an identifier, which is what lets authorization be re-derived per request rather than trusted from the token:
| Recorded | Used for |
|---|---|
| Entity and user | The principal the request acts as |
| Session type: user, agent, service, or API key | How the session was established, which is distinct from what kind of principal owns it |
| Entity kind | The owning principal's kind, drawn from the same canonical identity vocabulary the rest of the directory uses |
| Issuing node, and the originating node and cell | Provenance, and the scope the session is valid across |
| Federation provider, when the session came from one | So disabling or revoking a provider can drain exactly the sessions it issued |
| User agent, address, last seen, expiry | Expiry enforcement and anomaly detection |
The provider field is the one that is easy to miss and expensive to lack. When an upstream identity provider is disconnected, the sessions it vouched for are identifiable as a set and can be drained, instead of surviving until they happen to expire.
"How the session was established" and "what kind of principal owns it" are separate questions and are stored separately. An agent session and an agent entity are not the same statement, and conflating them is how an API-key session quietly acquires a human's authority. The platform keeps one vocabulary for principals and a different one for auth flows.
Anomaly detection on live sessions
Sessions are watched while they are alive, not only checked at creation. Detected anomalies include a surge in concurrent sessions, a device switch within a session, a binding violation where a session is presented against material it was not bound to, and rapid regeneration. These feed the same governance and activity record as any other security signal.
Related topics
- How a principal proves identity in the first place: Authentication.
- What a validated session is then permitted to do, and how authority is re-derived per request: Authorization and delegation.
- The per-table replication model these policies are declared under: Platform replication.
- Obtaining a credential for programmatic access instead of a session: Programmatic authentication.
- The key custody behind the node sign identity: Signing and cryptography.