Auth model
Cairn authenticates callers with an opaque session cookie and resolves every request into a principal — a user, their active workspace, and their role in that workspace — before your handler runs.
Sessions
Section titled “Sessions”When a sign-in flow succeeds, Cairn mints a session and sets
an opaque cairn_session cookie. The cookie holds a 32-byte random token; only its
SHA-256 hash is stored in the sessions table, so a database leak does not expose
usable tokens. The cookie is set HttpOnly, Secure, and SameSite=Lax.
On each request, the auth interceptor resolves the cookie into the caller’s principal — their user id, active workspace, and role — and makes it available to your handler. Two RPCs work directly with the session:
WhoAmIreports the resolved identity for the current session.Logoutrevokes the session server-side and clears the cookie.
Workspaces & roles
Section titled “Workspaces & roles”Cairn is multi-tenant by way of workspaces. cairn.v1.WorkspaceService lets an
authenticated caller manage them:
- Create a workspace — the creator becomes its
owner. - List workspaces and roles — every workspace the caller belongs to.
- Switch the active workspace —
SetActiveWorkspacechanges which workspace subsequent requests are scoped to. - Invite an existing user by email into a workspace.
- Change a member’s role.
Roles are hierarchical: owner ≥ admin ≥ member. Cairn resolves the caller’s
role in the active workspace into the request context, so you gate a handler by
requiring a minimum role:
if err := cairn.RequireRole(ctx, "admin"); err != nil { return nil, err // caller is below admin in the active workspace}Because the hierarchy is inclusive, RequireRole(ctx, "admin") is satisfied by both
admins and owners.
Disabling members
Section titled “Disabling members”An admin can revoke a member’s access to the active workspace without removing them.
DisableMember(user_id) marks the membership disabled; EnableMember(user_id)
restores it. A disabled member keeps their membership row and role — ListMembers
still returns them, flagged disabled: true — but the change takes effect at
resolution time: on every request, a disabled membership resolves to no active
workspace and no role.
That resolution-time enforcement is what makes disabling cheap for your application.
The member stays authenticated — WhoAmI still shows them signed in — but
cairn.ActiveWorkspace(ctx) returns false, so every workspace-scoped surface (Cairn’s
own RPCs, file transfer, and any handler you gate on the active workspace) denies the
request automatically. You write no extra code.
Re-enabling is instant and reversible. EnableMember clears the flag, and the
member’s same existing sessions and API keys regain access on their next request —
no re-login and no re-issued credentials. Both actions are audited fail-closed
(member.disabled / member.enabled).
Related
Section titled “Related”- Sign-in — the flows that mint sessions: magic link, OAuth, and passkeys.
- MFA — adding a second factor on top of the session.
- Data model — the tables behind users, sessions, workspaces, and memberships.