Packages

Org controls

WorkOS SSO sign-in, a Clerk session-verification driver, owner-gated multi-user membership, and the cross-tenant admin-write RLS layer the operator control plane mutates through.

@caisson-sh/org-controls is the org and operator-controls module, carved out of the base substrate so the core stays small: WorkOS SSO, a Clerk session-verification driver, the MANAGE half of the multi-user account model, and the admin-write RLS layer an operator control plane mutates through. Session resolution stays in the open @caisson-sh/auth; tenant isolation stays in the open @caisson-sh/tenancy-rls.

What it does

  • WorkOS SSO (createWorkosSsoProvider), a framework-agnostic AuthKit/SSO transport seam: builds the authorization URL, exchanges the callback code for the user's id + email. Config is injected, never read from env by the package.
  • Clerk session verification (createClerkSessionVerifier), verifies a Clerk session token against Clerk's JWKS via @clerk/backend, then maps the claims onto the kernel's SessionContext. Networkless when you supply jwtKey.
  • Owner-gated membership (listAccountMembers / addAccountMember / removeAccountMember / assertCanManageMembers), invite and remove seats on a shared account, owner-only.
  • Admin-write RLS (withAdminWrite + the policy builders), the cross-tenant write role an operator control plane mutates through, DB-separated from the tenant-scoped app role.
  • A feature-gate predicate (holdsOrgControls), a fail-closed check your app can use to gate access to this module's surfaces by whatever grant/entitlement model you run.

Install

bun add @caisson-sh/org-controls

Quickstart, WorkOS SSO

import { createWorkosSsoProvider } from "@caisson-sh/org-controls";

const sso = createWorkosSsoProvider({
  clientId: process.env.WORKOS_CLIENT_ID!,
  apiKey: process.env.WORKOS_API_KEY!,
  redirectUri: "https://app.example.com/auth/callback",
});

const url = sso.authorizationUrl(state); // redirect the user here
const { userId, email } = await sso.exchangeCode(code); // on the callback

Clerk session verification

import { createClerkSessionVerifier } from "@caisson-sh/org-controls";

const verifier = createClerkSessionVerifier({
  jwtKey: process.env.CLERK_JWT_KEY!, // networkless — no per-call JWKS fetch
  authorizedParties: ["https://app.example.com"],
});

const session = await verifier.verifySession(clerkToken); // -> SessionContext

An active Clerk Organization maps to accountId/role; with no Organization active, the session falls back to the personal-account convention (accountId === userId, role "owner"). This mapping is stateless, route the verified userId through @caisson-sh/auth's resolveUserAccounts/selectActiveAccount when you need the DB-authoritative multi-account resolution instead.

Membership management

import { addAccountMember, listAccountMembers } from "@caisson-sh/org-controls";

// actorRole comes from the caller's resolved session; only "owner" may manage members.
await addAccountMember(db, actorRole, accountId, newUserId); // default role "seat"
const members = await listAccountMembers(db, accountId);

Admin-write RLS

import {
  withAdminWrite,
  buildAdminWritePolicySql,
} from "@caisson-sh/org-controls";

// At DEPLOY, alongside the table's existing tenant-isolation policy:
const sql = buildAdminWritePolicySql("account");

// At call time, in the operator control plane only:
await withAdminWrite(db, async (tx) => {
  await tx.query(`UPDATE account SET ... WHERE id = $1`, [accountId]);
});

withAdminWrite refuses a SUPERUSER/BYPASSRLS role before it ever assumes it, a misconfigured role fails closed rather than silently widening access.

Feature-gate predicate

import { holdsOrgControls } from "@caisson-sh/org-controls";

if (!holdsOrgControls(activeEntitlementIds)) {
  throw new AuthzError("org-controls access required");
}

activeEntitlementIds must already be filtered to active grants, the predicate never reads the database itself, and an empty set denies.

Composing with the base

org-controls composes DOWN onto @caisson-sh/auth, @caisson-sh/tenancy-rls, and @caisson-sh/kernel: session resolution and tenant isolation are never reimplemented here, only extended: the owner-only MANAGE surface and the cross-tenant admin-write role sit beside those open primitives, never in place of them.

org-controls is a standalone module (also included in the Everything family). Gate access to its surfaces with holdsOrgControls.