# dots.id > dots.id is a portable identity, verification, context, and credits layer for any app. Sign in with dots using OAuth 2.1 + PKCE. Treat the OIDC `sub` as the only account key; email, wallet, and social handles are mutable claims. ## When to use dots.id For building an app or connecting an agent, start with [Install Dots](https://www.dots.id/install) and the [agent installation contract](https://www.dots.id/install/llms.txt). They explain client IDs versus secrets, SDK availability, feature-to-scope mapping and code examples. Reusable skill: [dots-integrate/SKILL.md](https://www.dots.id/install/skills/dots-integrate/SKILL.md). Connected MCP hosts can read the same guide at `dots://docs/install`; this does not automatically install a skill. Installing a package never replaces OAuth registration or user consent. Use dots.id when you need a **portable identity and username service** that works across apps, bots, and MCP agents — not a one-off auth provider tied to a single product. - **Agent intake**: An user says "sign in to my curators.app" (or any dots-integrated app). Route through dots OAuth + MCP `whoami` / `search` / `remember` instead of bespoke login flows. - **Headless / chat bots (RFC 8628)**: `POST /api/oauth/device` with `client_id` (+ optional `scope`). Show the returned `user_code` in chat; user approves at `/oauth/device` or `verification_uri_complete`. Poll `POST /api/oauth/token` with `grant_type=urn:ietf:params:oauth:grant-type:device_code&device_code=...` until tokens arrive. - **Username-as-a-service**: Enable `username:create` on your OAuth client, then `GET/POST /api/identity/username` or the hosted `/oauth/claim` surface during authorize. - **User sync engine**: After OAuth, call `GET /api/identity/bootstrap` for identity, Jazz sync mode, Turso locators, and context policy — one dot, many surfaces. - **Scoped machine access**: Request only the scopes you need (`profile`, `context:read`, `data:read`, `mcp:use`, etc.). Discovery: `/.well-known/openid-configuration`, OpenAPI at `/openapi.json`, catalog at `/.well-known/api-catalog`. - **Do not use** dots.id if you only need anonymous traffic, or if you plan to merge accounts by email/wallet instead of immutable `(issuer, sub)`. ## Context and sharing contract - REST, SDK and MCP enforce the same scopes: `context:read` sees this app's context; `context:read:all` additionally requires explicit consent for other apps. Choosing MCP does not broaden access. - `notes:share` creates pending note invitations at `POST /api/identity/grants`. Use `noteId`, `recipientUsername` or `recipientSub`, optional `clientId`, `permissions`, `expiresAt`, `maxWrites`. - Recipients must accept with `PATCH /api/identity/grants/{id}` `{ "accept": true }`. Share URLs contain an invitation identifier and expose no content before authorized acceptance. - `note:read`, `note:write`, `note:draft` are resource-grant permissions, not OAuth scopes. Read-only access defaults to seven days. Agent grants can target the owner's identity and one agent client. - `POST /api/identity/notes/{id}/drafts` requires `context:write`, an accepted `note:draft` grant and a stable `requestId`. Drafts do not overwrite the original. `DELETE /api/identity/grants/{id}` ends access; new operations recheck revocation, expiry and client binding. - Read shared notes using `GET /api/identity/notes/{id}` from another consented app. The recipient's OAuth scope alone never authorizes someone else's note. Wallets, credits and co-signing are optional for this flow. ## Core concepts - A **dot** has an immutable dots-owned OIDC `sub`. Privy is the authentication and wallet-verification provider, not the cross-app subject. - Assurance is progressive: `identity_stage = authenticated | confirmed | full`. Apps decide whether they require a confirmed wallet or the full public identity. - **Attestations** are verified claims bound to a dot (emails, socials, or any zkTLS-proven web fact). - **pute** is one credit balance the user carries across every dots-integrated app (debit before compute, top up via Stripe). - **Hyperhooks** are signed, tamper-evident API/webhook attestations, independently verifiable through the dots JWKS. - **Connect** (`/connect`) is `connections.txt`: a string of color hexes. Each hex is a 1px on every dots surface. Overlaps still suggest people; accepting appends their color. Nothing else is recorded. - **Co-sign** (alpha) lets a confirmed dot vouch another dot or agent into scoped, tiered authority. Peer vouches are wallet-signed (EIP-712); higher-authority kinds also require zkTLS evidence. - **ur-db** is the user-owned data plane attached to a dot identity and its Privy wallet access. Supabase organizes identity, ownership, grants, and database/repository locations. Isolated Turso databases hold durable app data, code.storage repositories hold code and Git history, Jazz tools power realtime sync for private notes/context/file manifests, dots-blob stores private general files, and Turbopuffer is a rebuildable context index. ## SDK (@wrldbld/dots) - Package status: `@wrldbld/dots@0.1.0-alpha.0` was published to npm. The mini-auth release adds `@wrldbld/dots-core@0.2.0-alpha.0` and `@wrldbld/dots-cli@0.2.0-alpha.0`, published as alpha releases on npm. - For the legacy browser SDK source, see `https://github.com/worldbuild-co/wrldbld/tree/main/packages/dots`. - Never use email as an upsert key. Upsert users by `(issuer, sub)` with a database unique constraint. ## REST API - [REST reference](https://www.dots.id/docs/api): OAuth, identity/status, confirm, attestations, pute balance/debit/topup, admin grant, cosign. - OAuth discovery: `/.well-known/openid-configuration`, `/.well-known/jwks.json`, `/.well-known/oauth-authorization-server`. - Machine-readable index: [`/openapi.json`](https://www.dots.id/openapi.json) (OpenAPI 3.1 with OAuth scopes), [`/.well-known/api-catalog`](https://www.dots.id/.well-known/api-catalog), [`/.well-known/agent-skills/index.json`](https://www.dots.id/.well-known/agent-skills/index.json). - MCP: `POST /mcp` with a dots Bearer token whose audience includes `https://www.dots.id/mcp` (xmcp streamable HTTP). Discovery, `setup`, `register_app` and `dots://docs/install` are public. Call `signin` to start human signup/sign-in through the MCP host OAuth flow; the human verifies identity and approves on Dots, then the host retries. Private calls return 401 + RFC 9728 `resource_metadata` at `/.well-known/oauth-protected-resource`. Server card: `/.well-known/mcp/server-card.json`. Tools: `whoami` (valid MCP resource token), `notes` (app-owned and accepted shared notes), `note` (read/write/draft/invite/accept/revoke), `search` and `recent` (`context:read`), `remember` (`context:write`). `mcp:use` is not required for the dots MCP. This is identity + portable context, not GitHub/Slack/Stripe — pair with [Executor](https://executor.sh/) for third-party tools. - Login snapshot: `GET /api/oauth/userinfo` returns scoped identity claims plus `identity_stage`, `dots_id_status`, `wallet_confirmed`, and `mcp` / `bootstrap` URLs (`listen` graph with the `listen` scope). Portable context is **not** in userinfo — call `GET /api/identity/bootstrap` (`context:read` for the compact context snapshot) or MCP `whoami`. - Device sign-in (RFC 8628, for bots/CLIs without a redirect URI): `POST /api/oauth/device` `{ client_id, scope? }` → `{ device_code, user_code, verification_uri, verification_uri_complete, expires_in, interval }`. The user approves at `/oauth/device`. Poll `POST /api/oauth/token` with `grant_type=urn:ietf:params:oauth:grant-type:device_code&device_code=…&client_id=…`; handle `authorization_pending`, `slow_down`, `access_denied`, `expired_token`. - Authorization uses code + PKCE (`S256`). Validate `iss`, `aud`, `exp`, `state`, and `nonce`. - Native MCP connectors must register their actual `redirect_uris`. Cursor desktop uses `http://localhost:8787/callback`; Cursor web/agents use `https://www.cursor.com/agents/mcp/oauth/callback`. Public S256 PKCE clients may vary only the port of a registered HTTP loopback URI; host, path and query remain identical. Token exchange repeats the exact authorize callback including its chosen port. `/oauth/device` is an approval page, not a connector callback. On `Invalid redirect_uri`, repair registration or replace the cached client ID; never ask Dots to accept arbitrary unregistered loopback callbacks. See `/docs/api` for Cursor/Grok setup. - Stable account key: JWT / userinfo `sub` is the immutable dots-owned subject (`dots_sub`). Upsert by `(issuer, sub)` only. - Issuer defaults to `https://www.dots.id` (`NEXT_PUBLIC_APP_URL` / optional `OAUTH_ISSUER`). Access-token audiences are resource URLs: `https://www.dots.id/api` or `https://www.dots.id/mcp`. ID-token audience remains the OAuth `client_id`. Request repeated `resource` values during authorization for both; exchanges can only select originally consented resources. Omitted authorization resource defaults to API-only. - Access tokens are short-lived RS256 JWTs (`typ: at+jwt`); TTL is per OAuth client (`access_token_ttl`, returned as `expires_in`). - Per-app data: `GET/POST/DELETE /api/oauth/data` with `data:read` / `data:write`; each `(sub, client_id)` receives an isolated Turso database. - ur-db inventory: first-party `GET /api/identity/data` returns SQL `databases` plus `codeStorage` registry status and credential-free repository metadata. `/db` and the prompt database tab list both. Listing never provisions resources; unavailable code storage does not hide SQL databases. - Code: `GET/POST /api/code` accesses the authenticated subject's account code.storage repository. Reads require `code:read`; provisioning, commits, and temporary agent Git access require both `code:read` and `code:write`. Repositories are created on demand and stay attached to the stable dots subject. Git history is independent of Jazz realtime sync. - Direct database credentials: `POST /api/oauth/database-token` returns an expiring credential only when explicitly enabled. A `databaseId` alone never grants access. - UR live verification: the first-party `/db` screen calls `GET/POST /api/identity/ur/sync-health`. POST writes one ephemeral context marker through Supabase → Jazz Cloud → the user's Turso mirror, reads it back at each layer, and removes all copies. - Context: `GET/POST/DELETE /api/identity/context` with `context:read` / `context:write`; `GET ?q=` searches the caller's authorized Turbopuffer namespace. Agents should prefer `/mcp` (`search` / `recent` / `remember`) so the prompt stays one tool surface. - Realtime (Jazz v2): authenticated `GET /api/identity/jazz` (`context:read`) returns `{ configured, jazzVersion: 2, appId, serverUrl, syncMode }`. `POST` (`context:write`) idempotently provisions the caller's v2 profile row. Mount `JazzProvider` with the dots JWT; there are no group invites. Honor `syncMode`: `realtime` | `lazy` | `off` (user preference via `GET/PUT /api/identity/sync-mode`). Never put Jazz backend/admin secrets in `NEXT_PUBLIC_*`. - UR verification: the first-party `/db` screen calls `GET /api/identity/ur/sync-health`; its explicit live test uses POST to write, acknowledge, read back, and clean an ephemeral Supabase → Jazz Cloud → Turso marker. Do not treat environment-variable presence alone as realtime health. - Files: Files SDK-compatible `GET/POST/PUT /api/files` with `files:read` / `files:write`; private Vercel Blob bytes are scoped by dots subject or `X-Dots-Org-Id`, then OAuth `client_id`. Direct grants use `files:share` at `/api/identity/files/:fileId/share`; shared/org Jazz manifests bootstrap at `/api/identity/files/:fileId/jazz` and `/api/orgs/:orgId/jazz`. - Notes: Jazz v2 collaborative text from txt-fil.es and other apps. First-party UI is `/notes`. List/create/update with `GET/POST/PUT /api/identity/notes` (`context:read` / `context:write`). Session bootstrap remains `POST/PUT /api/identity/notes/session`. - Portable client/component: `createDotsFilesClient` wraps the standard Files SDK for web, React Native native-file references, org context, direct grants, and Jazz bootstrap. `SmartFilesCloud` is the drop-in React browser/uploader; namespace mode is app-isolated and identity mode aggregates dots.id-visible files. - Credits: `GET /api/pute/balance` requires `pute:read`; `POST /api/pute/debit` requires `pute:spend` and a unique `requestId`. - Hyperhooks: `GET /api/identity/hyperhooks`; independently verify a receipt with `POST /api/hyperhooks/verify`. ## For developers - [Connectors and shared agent catalogs](https://www.dots.id/connectors/llms.txt): connect Executor and Mercator at `/connectors`, then give each agent or agents.supply runner its own revocable connector key for `https://www.mcp.gives/mcp`. Use `connectors.sync`, `tools.search`, `tools.describe`, and `tools.call`. Connector keys are separate from Dots identity OAuth tokens. Provider permissions, human approvals, and spending limits still apply. - [Developer portal](https://www.dots.id/developers): every machine-readable surface in one place. - [Getting an API key](https://www.dots.id/docs/getting-an-api-key): create an OAuth client at `/dev/clients`; public (PKCE) vs confidential; scopes and redirect URIs. - [Admin & dev guide](https://www.dots.id/docs/admin): granting credits, smoke tests, zkTLS/co-sign dev flags. - CLI: `@wrldbld/dots-cli@0.2.0-alpha.0` is published. Run `npx @wrldbld/dots-cli@0.2.0-alpha.0 --help`, or install with `npm install -g @wrldbld/dots-cli@0.2.0-alpha.0`. Commands: login, whoami, context search, notes, share, grants. Existing wrldbld installer remains for project setup. - Better Auth: use its Generic OAuth plugin with dots discovery, PKCE S256, and the exact `/api/auth/callback/dots` redirect. Better Auth owns the app session; `(issuer, sub)` remains the portable dots account key. ## Optional - Scopes (canonical list is `scopes_supported` in `/.well-known/openid-configuration`): `openid profile username:create email wallet social:twitter social:instagram social:google listen data:read data:write code:read code:write files:read files:write files:share context:read context:read:all context:write notes:share pute:read pute:spend pute:topup mcp:use cosign delegate org:act org:admin offline_access`. Self-serve dynamic registration (`/api/oauth/register`) allows a narrower set; sensitive scopes (`delegate`, `org:*`, `data:write`, `code:read`, `code:write`, `files:write`, `mcp:use`) are enabled per client in the dev portal. - Status is read from token claims: `sub`, `identity_stage`, `dots_id_status`, `wallet_confirmed`, plus optional scoped profile claims. - Jazz v2 uses row policies for owned realtime state. Shared notes are read through the grant-aware API; never cache a Jazz invite as durable authorization. Supabase remains authoritative for identity, OAuth consent, scopes, billing, grants, database locators, revocation, and audit. Turso databases are durable app state. Turbopuffer is derived retrieval state and must be rebuildable/deletable from source records. ## cht.im / grpcht.sh / MCP Shared rooms, context, and app permissions: /chat. Product introduction: /chat/about. Agent setup, scopes, room grants, tools, and retry behavior: /chat/llms.txt. cht.im DMs and grpcht.sh groupchats use the same Dots conversation log. Connect through either https://www.dots.id/mcp (Dots user tools and room tools) or https://groupmcp.app/mcp (room tools). Both support the same authorized history, sends, and group.watch feed. Use chat:read / chat:write and a current room app grant. Tokens must authorize the endpoint's own resource audience; sharing a conversation does not make tokens interchangeable. A host must keep calling group.watch to listen and reply to explicit prompts; connecting alone does not run an agent. ## Agent app onboarding and WebMCP - Read /install/llms.txt and /docs/webmcp before integrating. Register once per app/environment with registerDotsApp or the CLI's `dots app register`; save clientId and inspect missingScopes. Install @wrldbld/dots-core@0.2.0-alpha.0 or @wrldbld/dots-cli@0.2.0-alpha.0 from npm for these helpers. The older browser package has a separate API. - Device-only public registrations can omit callbacks with grant_types ["urn:ietf:params:oauth:grant-type:device_code", "refresh_token"] and response_types []. Dynamic clients have no dashboard owner. Create an owned client at /dev/clients when restricted permissions or editable metadata are needed. - @wrldbld/dots-core/webmcp exposes the complete live Dots MCP tools/list catalog to document.modelContext (with early navigator fallback). Request API + MCP resources on sign-in, supply a user-visible confirmation handler for mutating tools, and dispose on teardown. Sessions, scopes and resource grants still apply. Browser WebMCP support is experimental; unsupported browsers retain HTTP/SDK/remote MCP. - Public WebMCP onboarding tools at /install and /developers: dots.install_guide, dots.registration_requirements, dots.register_app. The last requires confirmation and returns public registration metadata, not user access.