Server Setup

Hosted and self-hosted database server configuration, app provisioning, and backend context setup.

Hosted database server

Your app ID namespaces your data for storage and sync. Click below to generate one on the Jazz hosted cloud — you'll also get the secrets you need to deploy your schema and permissions later. Generated apps are unclaimed until you claim them in the dashboard, and unclaimed apps are automatically deleted after 14 days.

Or from the command line (AI agents: use this to provision your own app):

curl -X POST https://v2.dashboard.jazz.tools/api/apps/generate

Jazz Cloud sync URL: https://v2.sync.jazz.tools/

Self-hosted database server

Terminal
export JAZZ_APP_ID="replace-with-your-app-id"
export JAZZ_ADMIN_SECRET="replace-with-admin-secret"
​
npx jazz-tools@alpha server "$JAZZ_APP_ID" \
--port 1625 \
--data-dir ./data \
--admin-secret "$JAZZ_ADMIN_SECRET"

jazz-tools@alpha server <APP_ID> currently supports:

OptionPurposeEnvironment variableDefault
<APP_ID> (positional)App namespace identifier (required)--
-p, --port <PORT>Listen port-1625
-d, --data-dir <DATA_DIR>Persistent storage directory-./data
--in-memoryUse in-memory storage instead of files; data is lost when the process exits-off
--jwks-url <JWKS_URL>JWKS endpoint for external JWT validationJAZZ_JWKS_URLunset
--jwt-public-key <JWT_PUBLIC_KEY>Single JWK JSON object or PEM public key for external JWT validation. Accepts inline contents or a path to a key file.JAZZ_JWT_PUBLIC_KEYunset
--auth-cookie-name <AUTH_COOKIE_NAME>Cookie name to read for browser authentication during WebSocket upgradesJAZZ_AUTH_COOKIE_NAMEunset
--allow-local-first-authAllow local-first auth (Authorization: Bearer <self-signed Jazz JWT>)JAZZ_ALLOW_LOCAL_FIRST_AUTHsee NODE_ENV note below
--backend-secret <BACKEND_SECRET>Enable backend session impersonationJAZZ_BACKEND_SECRETunset
--admin-secret <ADMIN_SECRET>Required for deploy and schema catalogue reads. In development mode, structural schema auto-sync works without it.JAZZ_ADMIN_SECRETunset
--shutdown-timeout-secs <SECONDS>Graceful shutdown network-drain timeout in secondsJAZZ_SHUTDOWN_TIMEOUT_SECS30

Local-first auth is enabled by default in development and requires --allow-local-first-auth in production. External JWT auth requires either --jwks-url or --jwt-public-key, but not both. The server runs Core directly. It owns the account registry, permission checks, and durable write confirmation. Browser workers and native local relays remain client-side persistence components.

Cookie-based WebSocket auth is enabled with --auth-cookie-name or JAZZ_AUTH_COOKIE_NAME. When no explicit auth credential is supplied, the sync server reads that named cookie and validates the JWT it contains. If your app uses a separate application session cookie, resolve it in your own app server and obtain an admitted account handle from its JWT. Pass that handle to await client.forAccount(account); forRequest accepts bearer headers and does not parse application session cookies.

If you prefer to start the database server programmatically, you can use startLocalJazzServer from jazz-tools/dev. It expects similar arguments as the CLI.

Backend context setup

A TypeScript backend creates one createJazzSession owner from jazz-tools/backend, with appId, app, permissions, driver, and serverUrl configured once. Select backend authority with initial: { backendSecret } or later with await session.becomeBackend({ backendSecret }). For authentication and account linking, see Authentication.

Backend admission requires a reachable Jazz server: the Node host sends POST /apps/{app}/backend/admit with X-Jazz-Backend-Secret, and proceeds only after that server validates it. Core validates the credential and owns account registration and linking. This also applies to a memory driver; backend initialization is not an offline privilege grant. Browser and React Native hosts reject backend selection.

main.ts
const session = await createJazzSession({
appId,
app: schemaApp,
permissions,
driver: { type: "persistent", dataPath: dbPath },
serverUrl,
initial: { backendSecret },
jwksUrl,
jwtPublicKey,
allowLocalFirstAuth,
env: "dev",
});
const snapshot = session.getSnapshot();
if (snapshot.status !== "ready" || !snapshot.client) {
throw snapshot.error ?? new Error("Backend session is not ready");
}
const client = snapshot.client;
const db = client.db;

Backend identity pattern

The ready snapshot exposes client.db for backend-owned work. Backend accounts use the reserved nil account UUID and identity { issuer: "urn:jazz:system", subject: nodeUUID }, where nodeUUID is the actual originating native node. Copying that identity does not grant authority. The secret stays in private handle material and is never serialized into snapshots or account preferences.

  • client.db has backend permissions and records SYSTEM node provenance.
  • await client.forRequest(req) verifies the caller's bearer and active core account assignment, then returns an immutable database scope with that user's policies and authorship.
  • await client.forAccount(account) performs the same verification from an opaque admitted user account handle.
  • await client.withAttribution(account) and await client.withAttributionForRequest(req) keep backend permissions while recording the verified user's authorship. Use them after the application authorizes the operation.

Request scopes do not change the shared session's selected account, so concurrent requests retain separate policy contexts. Do not switch the shared owner to serve individual requests. When the application deliberately transitions the owner from backend to a user account, the old client shuts down and the replacement opens an ordinary native runtime without backend privilege. The shared lifecycle handles detach, sync, shutdown, retry, and logout; logout invalidates every issued handle. Backend selection is ephemeral across process restarts, while retained local-first signing roots remain available.

forRequest reads standard HTTP headers from Express, Hono, Fastify, or a Web Fetch API Request. Configure jwksUrl or jwtPublicKey on createJazzSession(...) for external IdP tokens, but not both. Without either, it accepts only Jazz local-first tokens; allowLocalFirstAuth: false disables those too. Application cookies must first be resolved to an admitted account by your auth integration.

The TypeScript backend's jwksUrl must use HTTPS, except for development HTTP with a WHATWG-canonical hostname of localhost, [::1], or an IPv4 address in 127.0.0.0/8. For example, http://localhost:3000/api/auth/jwks is allowed; http://localhost.:3000/api/auth/jwks (the trailing-dot spelling) and http://keys.example/api/auth/jwks are rejected before fetching keys. Every redirect is rejected, including redirects to HTTPS. Use the provider's final JWKS URL directly. This policy applies to TypeScript request authentication, not the separate Rust server's --jwks-url verifier.

Attribution without impersonation

Use await client.withAttribution(account) or await client.withAttributionForRequest(req) to retain backend access while recording verified user provenance. Raw session objects and issuer/subject strings are not public authority inputs. See Sessions > Attribution without impersonation.

Per-request user-scoped client

Pass req to run queries as the authenticated user, with all permission policies applied.

request-context.ts
export async function listTodosForRequester(req: Request, res: Response): Promise<void> {
try {
const requester = await client.forRequest(req);
const rows = await requester.all(schemaApp.todos.where({ done: true }));
res.json(rows);
} catch {
sendQueryError(res);
}
}