Volter World

Create and revoke a local authentication session

Use the real Clerk backend SDK to create a user and session, verify the issued token's identity and revoke the session. Stop the World, resume it and read the same user and revoked session. No real Clerk account, secret key or Volter platform account is needed.

Use Node 22.6 or newer, npm and an empty app directory. Installation needs the normal package registry. This example uses admitted @volter/twin-clerk@1.0.2 and the exact published World dependencies below. Keep the manifest, lockfile and generated service source together. This is a backend example; it does not walk a browser sign-in screen.

Prepare the app

package.json
{
  "name": "world-auth-user",
  "private": true,
  "type": "module",
  "dependencies": {
    "@clerk/backend": "2.33.3"
  },
  "devDependencies": {
    "@volter/world": "3.0.158",
    "@volter/world-core": "3.0.148",
    "@volter/world-runtime": "3.0.156",
    "@volter/world-console": "3.0.149",
    "@volter/twin-clerk": "1.0.2"
  }
}

Declare the credential names your app reads. The World issues throwaway secret and publishable keys and its public token-verification key when it starts. Leave these values empty; do not copy keys from a real Clerk dashboard.

.env.example
CLERK_SECRET_KEY=
CLERK_PUBLISHABLE_KEY=
CLERK_JWT_KEY=
npm install --no-audit --no-fund

Create auth.mjs. The normal Clerk client uses its vendor API hostname; the World's injection supplies routing. The SDK's token verification uses the World's public key. The JWT itself and credential values are never printed.

auth.mjs
import { createClerkClient, verifyToken } from '@clerk/backend';

const clerk = createClerkClient({
  secretKey: process.env.CLERK_SECRET_KEY,
  publishableKey: process.env.CLERK_PUBLISHABLE_KEY,
  telemetry: { disabled: true },
});
const email = 'ada@example.test';

if (process.argv[2] === 'read') {
  const users = await clerk.users.getUserList({ emailAddress: [email] });
  for (const user of users.data) {
    const sessions = await clerk.sessions.getSessionList({ userId: user.id });
    console.log(JSON.stringify({
      user: { id: user.id, name: user.firstName, email: user.emailAddresses[0]?.emailAddress },
      sessions: sessions.data.map(session => ({ id: session.id, status: session.status })),
    }, null, 2));
  }
} else {
  const user = await clerk.users.createUser({
    emailAddress: [email], firstName: 'Ada', lastName: 'Lovelace',
    skipPasswordRequirement: true,
  });
  const session = await clerk.sessions.createSession({ userId: user.id });
  const token = await clerk.sessions.getToken(session.id);
  const claims = await verifyToken(token.jwt, { jwtKey: process.env.CLERK_JWT_KEY });
  console.log(JSON.stringify({
    user: { id: user.id, name: user.firstName, email: user.emailAddresses[0]?.emailAddress },
    session: { id: session.id, status: session.status },
    verifiedIdentity: { userId: claims.sub, sessionId: claims.sid },
  }, null, 2));
  const revoked = await clerk.sessions.revokeSession(session.id);
  const retained = await clerk.sessions.getSession(session.id);
  console.log(JSON.stringify({ revoked: revoked.status, retrieved: retained.status }, null, 2));
}

Select Clerk and run the backend workflow

./node_modules/.bin/volter world init --name auth-user --twins clerk --source clerk=@volter/twin-clerk

Review the detected vendor before booting: this app needs Clerk for user and session state. Keep only Clerk; the app has no other vendor dependency. Init writes the World configuration and reports the installed Clerk source.

./node_modules/.bin/volter world up
./node_modules/.bin/volter world clock set 2026-01-15T12:00:00Z
./node_modules/.bin/volter world run -- node auth.mjs

The client prints Ada's user ID, an active session and the verified token's matching user and session IDs. It then prints revoked for both the revocation response and a fresh session lookup. IDs are created for this World, so yours can differ.

This uses Clerk's backend session-creation operation for local app development, not a person's interactive sign-in. Signed-token verification establishes the token's claims; it is separate from reading whether the stored session is active or revoked. This example does not claim an already issued token becomes unverifiable after revocation.

./node_modules/.bin/volter world log

The log shows the user creation, session creation and revocation. Those are stored vendor operations, performed through the SDK rather than fabricated with a handler.

Resume the same user and session

./node_modules/.bin/volter world down
./node_modules/.bin/volter world up
./node_modules/.bin/volter world run -- node auth.mjs read
./node_modules/.bin/volter world down

The read-only client lists Ada and the same revoked session without creating another user. Down stops compute and retains state. Reset would discard the current branch's records and reload default data; use it deliberately before replaying the write path.

For your own application, keep its real SDK and normal credential names and use its usual command inside the World. Check the selected Clerk release for operation-specific gaps before depending on another flow. This walk covers backend users, sessions, signed-token verification, revocation and retained readback. Browser sign-in, organization permissions, MFA, passkeys and enterprise SSO were not exercised.

Continue with inspecting stored changes, using your existing app or recovering a failed call.

View Markdown source

On this page