DomainRuntimedocs
Auth and permissions
PlannedWhat works todayReviewed 2026-09-22

Users

Sign your app's users in to an environment, and know who they are in queries and actions.

The mechanism runs today; the helpers are planned. Minting user tokens works in production through the environment route shown under Today. env.users.mintToken, db.auth.signIn and runtime.auth are the planned helpers.

Your app keeps its own login — email, Google, Clerk, anything. After your backend has verified who the user is, it asks DomainRuntime for a user token for that environment. The browser signs in with it.

Browser ──login──▶ your backend ──verifies──▶ your auth provider
                         │
                         └── mintToken({ id, email, claims }) ──▶ DomainRuntime
Browser ◀── user token ──┘
Browser ── db.auth.signIn(token) ──▶ <env>.domainruntime.cloud

Mint a token on your backend

app/api/session/route.ts
import { init } from "@domainruntime/platform";

const platform = init({ serviceKey: process.env.DOMAIN_SERVICE_KEY! });
const env = platform.environments.get(process.env.DOMAIN_ENV!);   // e.g. "prod-acme-main"

export async function POST(request: Request) {
  const user = await requireSignedInUser(request);   // your auth provider's session check
  const { token } = await env.users.mintToken({
    id: user.id,
    email: user.email,
    claims: { role: user.role },
  });
  return Response.json({ token });
}
  • id must be a UUID and stable for the person. Use the same id for the same person in every environment. If your provider's ids are not UUIDs (Clerk user_…, Auth0 auth0|…), derive a stable UUID from them, for example UUIDv5 over the provider id.
  • claims are yours: whatever your actions and rules need, such as a role or a tenant. Up to 1 KB.
  • A token is valid for one environment.
Today

Call the environment directly from your backend with its service credential:

curl -X POST "https://<handle>.domainruntime.cloud/admin/refresh_tokens" \
  -H "authorization: Bearer $DOMAIN_SERVICE_KEY" -H "content-type: application/json" \
  -d '{"id": "3f0c…", "email": "ada@acme.com", "claims": {"role": "manager"}}'
# { "user": { "id": "…", "email": "…", "refresh_token": "dru_…" }, "expiresAt": "…" }

In the browser, sign the InstantDB client in with db.auth.signInWithToken(refresh_token); see Reading data.

Sign in from the browser

const { token } = await fetch("/api/session", { method: "POST" }).then((r) => r.json());
await db.auth.signIn(token);

db.auth.user;       // { id, email, claims } or null
await db.auth.signOut();

Who is calling, on the server

In an action, runtime.auth.user is the signed-in user or null. In permission rules, the same user is auth.

Revoking

Signing out, or revoking a user from your backend, rejects that token's next request. Work the user already started — a running workflow — continues with the identity it was started with.

The $users entity

Signed-in users appear in $users; each user can see only their own row. Link your entities to it to model ownership:

orders_orderOwner: {
  forward: { on: "orders_order", has: "one", label: "owner" },
  reverse: { on: "$users", has: "many", label: "orders" },
},

On this page