Appearance
Users
A user in HiveCast is represented internally as a principal. This page covers what a principal is, how it gets created, and which fields are operator-relevant.
Internal terminology
The code says "principal." The product UI says "user" or "you." This docs page mostly uses "principal" because it is precise — a principal is an identified subject, not a session or a device. The product surface should never expose the term to end users.
The principal record
Source: projects/matrix-3/packages/system-auth/src/host-auth.ts:29-39.
ts
export interface IHostPrincipalRecord {
readonly id: string; // p_xxx
displayName?: string;
email?: string | null;
avatarUrl?: string;
workspaceRealm?: string | null;
addressRoots: Array<{ root: string }>;
readonly status: 'active' | 'suspended';
readonly createdAt: string;
updatedAt: string;
}Key fields:
id— opaque, stable principal id (p_<random>). Used everywhere internally. Never displayed in user URLs.displayName— the user's display name from their OIDC ID token (nameclaim).email— from OIDCemailclaim. Used to derive the initial address root and as a contact handle. Email can change (a user updates Google's primary address); the principal stays.addressRoots— list of bus authority roots claimed for this principal. Today the first entry is derived from the email domain at first login. Later, additional roots can be added via Space creation.workspaceRealm— legacy field. UseaddressRoots[0].rootin new code.status—activeorsuspended. Suspended principals fail session validation.
The principal record is created on first OIDC login and persisted in HostAuthStateStore. Subsequent logins find the existing record by (issuer, subject) — never by email.
OIDC identity is (issuer, subject), not email
This is critical and easy to get wrong. Per ARCHITECTURE-PLATFORM-TOPOLOGY.md §3:
Identity is
(issuer, subject)per OIDC — NOT email. Email can change; identity is permanent.
Examples:
| Provider | Issuer | Subject |
|---|---|---|
accounts.google.com | 107348194... | |
| GitHub (target) | github.com | 12345678 |
| Okta (target) | acme.okta.com | 00u1a2b3c4 |
The findOrCreate op (HostPrincipalStore.findOrCreate(issuer, subject, claims)) does the lookup. This is how a user who changes their email keeps the same principal.
How a principal is created
Source: projects/matrix-3/packages/system-auth/src/google-oidc.ts:151-190 and index.ts:1216-1244.
The Google OIDC flow:
- User clicks "Sign in with Google" on
/apps/web/. - Browser is redirected to
https://accounts.google.com/o/oauth2/v2/authwith PKCE. - Google redirects back to
/api/auth/callback/googlewith code + state. auth.google.callbackop exchanges code for tokens (exchangeCodeForTokens).verifyGoogleIdTokenchecks signature against Google JWKS, validatesiss/aud/exp.principalStore.findOrCreate(claims.iss, claims.sub, { email, displayName, provider: 'google' }).sessionService.createSession(principal.id, ...)mints anmx_sessionJWT (HMAC-SHA256, 7-day expiry).Set-Cookie: mx_session=...; HttpOnly; SameSite=Lax.- Browser redirects to the page captured in the OAuth state's
redirect.
If principal.id was not in the existing-set before, isNewUser: true is returned. The cloud Host can use this to drive a first-time setup flow.
Listing principals
bash
matrix invoke system.auth auth.principal.list '{}' \
--home /tmp/matrix-homeReturns every principal known to the local Host. On the platform Host this is every signed-in user. On a local Device it is exactly one principal — the Device's owner — assuming pairing has happened.
Looking up by id
bash
matrix invoke system.auth auth.principal.get \
'{"principalId":"p_xxx"}'Returns the principal record or { ok: false, error: "Principal not found" }.
Address-root derivation
The first address root is derived from the email domain at first login. Per ARCHITECTURE-PLATFORM-TOPOLOGY.md §4:
richard@nimbletec.com -> COM.NIMBLETEC.RICHARD-SANTOMAURO
richard@gmail.com -> COM.GMAIL.RICHARD-SANTOMAURO
alice@gmail.com -> COM.GMAIL.ALICE-TESTUSER
jane@acme.com -> COM.ACME.ENG.JANEAddress roots are immutable once assigned. Renaming an email account does not rename the root. (For new Spaces, the v2 contract prefers SPACE.<spaceId> derivation — see Spaces.)
A common misconfiguration is to derive gmail.com to COM.GOOGLE instead of COM.GMAIL. Per the auto-memory ledger ("Root derivation"), this must be COM.GMAIL. Mismatches break Device discovery.
Setting workspace realm (legacy)
bash
matrix invoke system.auth auth.principal.setWorkspaceRealm \
'{"principalId":"p_xxx","realm":"COM.GMAIL.NEW-REALM"}'workspaceRealm predates addressRoots[]. New code should write address roots via Space creation. The op exists for migration tooling.
Authority root resolution
For any principal, auth.principal.authorityRoot returns:
- The principal's default Space's
authorityRoot, if a default Space is set. - Otherwise the first registered address root.
- Otherwise the legacy
workspaceRealm. - Otherwise an error.
This op is what system-gateway-http's /api/bootstrap handler calls to compute the addressRoot returned to a freshly-loaded browser tab.
Lifecycle: suspending a principal
Today the status: 'suspended' field exists in the schema but is not surfaced via an op. Suspending requires direct state-file edit on the platform Host. This is target state — a auth.principal.suspend / unsuspend op pair is needed for the admin dashboard.
Lifecycle: deleting a principal
Not implemented. Principal deletion has cascading implications for Spaces, Host Links, and stored credentials. The recommended workaround is suspension. Target state.
Target state
- Multi-provider login. Today only Google. GitHub OIDC and email/passkey are in
ARCHITECTURE-PLATFORM-TOPOLOGY.md§3. - Enterprise SSO via home-realm discovery. Schema-ready (
workspaceRealm); discovery flow not implemented. auth.principal.suspend/auth.principal.unsuspendops.auth.principal.deleteop with cascade rules.
See also
- Organizations — multi-principal owner type.
- Spaces — what principals own.
- Ownership — owner-type semantics.
- Security: Authentication — full auth flow.
- Reference: Actor surfaces —
system.authop list.
Source:
projects/matrix-3/packages/system-auth/src/host-auth.ts:29-39for the schema.projects/matrix-3/packages/system-auth/src/index.ts:450-528forauth.principal.*ops.projects/matrix-3/packages/system-auth/src/google-oidc.tsfor the OIDC implementation.