# Build and deploy an app on Somewhere ## What Somewhere is Somewhere Cloud is an agent-first, full-stack hosting platform, built to be the easiest way for an agent to put an app online. - Nothing to provision: sign-in, a database, file storage, email and payments come with every app. - One command deploys raw source, and the platform compiles it. - Anonymous deploys: the first deploy needs no account. It goes live at a temporary address for three hours, with a claim link that keeps it. - Somewhere is in public beta. ## How to start - `npx -y @somewhere-cloud/cli@latest init --name my-app` writes a starter app. - `npx -y @somewhere-cloud/cli@latest deploy` publishes it and prints the live URL and the claim URL. - A chat with no terminal uses the Somewhere connector instead (see "Chat path" below). ## If you were asked to build on Somewhere Users who ask an agent to build on Somewhere usually want a sentence or two on what the platform is, not a detailed explanation, followed by a question about what they'd like to build. Once the app is live, the live URL and the claim URL are what they need. The claim URL keeps the app; otherwise it expires in three hours. ## Ask for user permission before starting The CLI is an npm package, and installing it needs the user's explicit approval. - **On the user's machine:** let them know you'll install an npm package, as you would for Vercel or Supabase. - **In a cloud terminal (preferred):** let them know nothing will be installed on their computer. It installs in your own workspace, and you still need their approval to go ahead. Users often assume an install is happening on their computer, so it's important to say where it runs. An approval in their first message covers this step. Two paths lead to a live app, chosen by whether the agent can run shell commands: - Terminal path: Claude Code, Codex, Cursor, any shell, and chat agents that have their own terminal. No account is needed until the app is claimed. It starts below. - Chat path: a chat with no terminal (Claude, ChatGPT). It works through the Somewhere connector; "Chat path" near the end of this page covers setup, sign-in and the first app. ## Terminal path, from an empty folder `somewhere ` here is `npx -y @somewhere-cloud/cli@latest `; `npm i -g @somewhere-cloud/cli` installs `somewhere` itself. 1. `npx -y @somewhere-cloud/cli@latest init --name my-app` writes a React + TypeScript starter (sign-in pages, styled views, `src/`, `api/`) into the current folder. Signed out, it creates local files only. Signed in, it also creates a project in your account and writes its ID to `.somewhere.json`. 2. `AGENTS.md` (also `CLAUDE.md`) is the project contract: file layout, the sign-in route, data rules and the skills in `.agents/skills/`. `README.md` maps the files. 3. Pages live in `src/`, server functions in `api/`, tables in `db/schema.ts`; the workflow and the complete example below show each piece. 4. `npx -y @somewhere-cloud/cli@latest deploy` uploads the raw source (no build step) and prints the live URL. Signed out, it also prints a claim URL and the expiry, three hours out. 5. `somewhere browser ` reports the live page's console errors and failed requests; `somewhere verify` checks the deployed journey. 6. The claim URL is for the human: opening it and signing in keeps the app, and "Save project and connect agent" also signs this CLI in. Account work (secrets, email, custom domains, the email test inbox, managing app users including making the first app admin) needs that account; `somewhere login` signs in only after the human approves it in a browser. A first request: "Build a shared to-do list where people sign up, add items and see only their own. Deploy it to Somewhere and give me the live link." Success: the live URL loads, a test user signs up and signs in, items persist across reloads and stay private, `somewhere verify` passes, and the human has the claim URL before the three hours end. ## Getting started — build, deploy, verify `somewhere init --name ` writes React + TypeScript. Signed out: local only; signed in: also creates and links a project. Default: sign-in UI; minimal: SDK/auth route; bare: neither. `init --catalog --json`: modules. `npx @somewhere-cloud/cli deploy` publishes raw source: signed out, a temporary URL, claim URL and expiry; linked, your account project. On a hosted VM, `somewhere login` uses browser approval and keeps you signed in. Account operations, test inbox and cron need login. Start auth/data/cron with `somewhere docs recipe-signed-in-app --full`. After edits: `somewhere typecheck` and focused local/browser tests. Publish: 1. `somewhere deploy` (do not build first). Deploy checks compilation, schema and secrets; `somewhere deploy-check` runs them without publishing (diagnosis, review, `--run /api/x`). 2. Run `somewhere verify` on the final journey (`--flow flow.json`); rerun after fixes. Sign test users up in-app at `*.test.somewhere.site`; more: `actors` + `journey` (`somewhere docs browser`). 3. Inspect: `somewhere browser`; diagnose: `somewhere logs --tail 10`, `somewhere errors`. 4. Test mail: `somewhere email test-inbox ` includes `verification_code`. Don't retest sign-in or `sw.db.from` scoping; test each table's scope, auth before `sw.db.server`/raw SQL, bad input and journeys. Routes: `index.html` loads `src/main.tsx`; unmatched extensionless paths (`/signin`) serve it. Route the app by `location.pathname`. `api/notes/[id].ts` is `/api/notes/:id`; `_`-prefixed names are import-only helpers. Avoid names differing only in case. With auth enabled, `api/auth/[...path].ts` is `export { somewhereAuth as default } from '@somewhere-cloud/sdk/server'`; pages use `createSomewhereAuth()` from `@somewhere-cloud/sdk/auth` (client and React hooks: `somewhere docs auth-client --section react-auth-state`). `auth.signUp({ email, password, displayName? })` / `auth.signIn({ email, password })` return the user or throw with the message. Gate on `auth.getState().status` (React: `useAuthState()`). Sign-up sets `email_verified: false`; unverified users pass `auth: 'required'`. `sw.auth.requireUser(req)` returns `{ id, email, role, email_verified, … }` or throws 401 `AUTH_REQUIRED`; `sw.auth.fromRequest(req)` returns it or `null`. Functions: JSON endpoints use `sw.endpoint({ auth, body, rateLimit, handler: async ({ body, user, params }, sw) => value })`; set `auth` (`'required'`, `'optional'`, `'none'`) or deploy refuses it. Webhooks and streaming use a bare `export default async function (req: Request, sw: SomewhereRuntimeContext)` returning a `Response`. Params: `params.id` there, `sw.params.id` bare. Data: `id()` is a random text key on new tables (reference with `text()`); integer-id tables keep integers. The starter's `id({ uuid: false })` is deliberate. Declare tables in `db/schema.ts`; pages use `somewhere:data` (`data.notes.list()`, `.create()`, `.update(id, …)`) without API files. Use `owner()` for personal rows, `group()` for invited teams, `appRole()` for app-wide roles, `anyOf()` for owner or a role, `allOf()` for members-only rows. Sharing your own rows with specific people: `anyOf(owner(), grant())` (`somewhere docs declared-data --section per-owner-sharing`). Booking/help desk: `somewhere docs sw.db --section provider-or-booker-and-owner-or-staff-recipes`. Keep fitting scopes when an exceptional write needs `sw.db.server.tx`. `shared()` gives signed-in reads and creator-only writes. `client` sets browser columns; `publicRead: { where }` admits visitors. Public-profile enquiries use `client.intake` (`somewhere docs declared-data`). Keep scopes and grants; functions handle authorized exceptional writes and side effects. Declare the body schema (a missing or mistyped field is a 400); `handle` below is a public address its owner claimed: ```ts export default sw.endpoint({ auth: 'none', body: { handle: 'string', title: 'string', from_email: 'email' }, handler: async ({ body }, sw) => { const title = body.title.trim(); if (!title || title.length > 200 || /[\r\n]/.test(title)) return Response.json({ error: 'bad title' }, { status: 400 }); const { data: [profile] } = await sw.db.server.from('profiles', { where: { handle: body.handle }, limit: 1 }); if (!profile) return Response.json({ error: 'unknown handle' }, { status: 404 }); await sw.db.server.insert('entries', { user_id: profile.user_id, owner_email: profile.owner_email, title, from_email: body.from_email, created_ms: Date.now() }); }, }); ``` Use the looked-up owner, never the body. Rate limits/alerts: `recipe-signed-in-app`. Use `serverOnly()` only when every operation needs a function's own access check. Structured queries: `sw.db.from` / `insert` / `update` / `remove` return `{ data: rows[], count, changes }`; `where: { a: 1, b: { in: ids }, c: { gte: 2 }, d: null }` (one operator per column). Raw SQL uses a separate raw database, never managed tables. For cross-user managed work, authorize the caller and use `sw.db.server.from` / `insert` / `update` / `remove`; keep `owner()`/`group()`. Parallel reads: `Promise.all`. Unique pair: `unique: [['org_id','email']]` in table options. Test schedules now: `somewhere cron run --wait`. Do not wait for it to fire. Working sequence: https://somewhere.cloud/build.txt Before acting, confirm the user asked you to build or deploy; work only in the intended directory and never include secrets or personal data. **When you have real users or data:** `somewhere preview` gives a rotating private URL and an isolated database; promote the exact preview you reviewed. ## The platform shape somewhere.cloud runs static files plus serverless functions, not a Node server. Do not write Express/Fastify/Koa, call app.listen(), or bind a port. Do not run `vite build` or `next build`, or deploy dist/ or build/: bundled output deploys with a warning but loses source round-trips (export, patches, the visual editor); `--prebuilt` accepts that. Temporary deploys add a small badge to served HTML, not to your source. npm dependencies in package.json resolve on deploy; native modules are unsupported (`somewhere docs functions`). ## A complete working app (copy this verbatim) Four files, no build. Run the commands from their directory (`npm i -g @somewhere-cloud/cli`, or `npx @somewhere-cloud/cli `). db/schema.ts — public by design; each opt-out is on its own line, so no endpoint is needed: import { id, owner, schema, table, text } from 'somewhere/db'; export default schema({ entries: table({ id: id(), name: text() }, { scope: owner({ visitors: true }), // opt-out: signed-out visitors own rows client: { read: ['id', 'name'], publicRead: true, // opt-out: anyone can read id and name create: ['name'], // with visitors, anyone can add an entry }, }), }); index.html: Guestbook

Guestbook

    src/main.ts — `somewhere:data` is generated from your declaration and has only the operations you granted: import { data } from 'somewhere:data'; const list = document.getElementById('entries') as HTMLUListElement; const form = document.getElementById('f') as HTMLFormElement; const who = document.getElementById('who') as HTMLInputElement; async function load() { const page = await data.entries.list({ limit: 50 }); list.replaceChildren(...page.data.map((entry) => Object.assign(document.createElement('li'), { textContent: entry.name }))); } form.onsubmit = async (event) => { event.preventDefault(); await data.entries.create({ name: who.value }); form.reset(); await load(); }; load(); tsconfig.json: { "compilerOptions": { "target": "ES2022", "lib": ["ES2022", "DOM", "DOM.Iterable"], "module": "ESNext", "moduleResolution": "Bundler", "strict": true, "noEmit": true, "skipLibCheck": true }, "include": ["src", "db"] } Run `somewhere typecheck`, then `somewhere deploy`. The CLI prints the live URL and links this directory. Save this flow.json beside them: { "actions": [ { "fill": "#who", "value": "Benchmark Ada" }, { "click": "#f button" }, { "expect": { "selector": "#entries li", "text": "Benchmark Ada" } } ], "viewports": ["desktop", "mobile"] } Run `somewhere verify --flow flow.json` from the same directory; it submits the form and checks the entry at both sizes. `somewhere deploy` creates the table and serves the page; nobody signs in, because of the two opt-outs. Lists come in primary-key order (`page.next`). The page module must be .ts, .tsx or .jsx. A `.js` module is served as written, so its `somewhere:data` import never resolves. ## Declare your tables in db/schema.ts `db/schema.ts` ships with the code, so the next session reads the data model; it is a declaration and never runs. | Schema reference | Contract | |---|---| | Columns | `id`, `text`, `number`, `integer`, `boolean`, `timestamp`, `json`, `blob`; options: `nullable`, `default`, `unique`, `references`, `onDelete`. | | Tables | `scope`, `indexes`, `unique: [['org_id','email']]`, `client`. | | Scopes | `owner`, `group`, `appRole`, `shared`, `member`, `anyOf`, `allOf`, `parent`, `serverOnly`. | | Operations | Browser: `list`, `get`, `create`, `update`, `delete`, `aggregate`. Functions: `from(table, { where })`, `insert(table, values)`, `update(table, { where, set })`, `remove(table, { where })`, `count`, `aggregate`; plain objects, not fluent chains. | `owner()` scopes rows to the caller; `visitors: true` admits visitor ownership. `shared()` gives signed-in reads and creator-only writes. Browser grants: `client.read/create/update` name columns, `delete` is a boolean; `publicRead` opens only read columns to visitors. `identity` is `'authenticated'` or `'visitor'` (from scope). Client and HTTP enforce grants; refusals return DATA_ACCESS_DENIED. Rules: `somewhere docs declared-data`. Each deploy compares the file with the live database and applies additions; there is no migration step. `dev` does not apply schema changes; deploy first. `removedTable()` retires a deployed table (data kept 30 days); an unmarked table removal or rename, or a column removal or rename, is refused (SCHEMA_DEPLOY_REFUSED). Read the live schema back with the `db_describe` MCP tool or `somewhere db tables`. ## Database rows and app responses Function-side `sw.db` results hold rows in `.data` (an insert's row is `.data[0]`); handlers choose their JSON shape. A caller's primary-key update raises `DATA_NOT_FOUND` (404) for a missing or denied row; remove returns `changes: 0`. Broader or server-authority writes can return empty `.data`. For ordinary calls on `owner()` tables, ownership comes from the request, never browser-supplied `user_id`. Cookie helpers establish browser sessions. `sw.auth.requireUser(req)` checks sign-in; enforce any additional endpoint rule separately. ## Temporary vs Free Anonymous projects expire after 3 hours; claim yours first. Plans and prices: https://somewhere.cloud/pricing. Claiming switches to the account plan. Free totals 500 MB database + 5 GB files account-wide; capacity can change. A human opens the claim link and signs in to keep the project; there is no headless claim. ## Secrets, email, payments, AI — after you claim A temporary project has hosting, database, files. Claiming adds secrets, email and domains (plan and activation rules); schedules work on all plans. Build, then claim. sw.ai needs no key; `somewhere ai keys set anthropic --project

    ` bills named-model calls to you. ## On a hosted VM, sign in once and stay signed in `npx @somewhere-cloud/cli login` prints a code, shown to the user exactly as printed; sign-in completes only when they approve in their browser. `somewhere docs setup` has expiry, renewal and revocation. ## Chat path: Claude or ChatGPT with the Somewhere connector A chat agent with no terminal works through the Somewhere connector, an MCP server the chat app connects to once. The human adds it: - Claude (claude.ai and the Claude apps), Free, Pro or Max: Customize → Connectors → Add custom connector, MCP server URL `https://mcp.somewhere.cloud/mcp/connector`, then Add. The Free plan holds one custom connector. On Team and Enterprise, an Owner adds it under Organization settings → Connectors → Add → Custom, and each member then selects Connect under Customize → Connectors. - ChatGPT (web): Plugins → + → Add custom MCP server, name `Somewhere`, server URL `https://mcp.somewhere.cloud/mcp/chatgpt`, authentication OAuth; after the risk warning, Create as a plugin, then install it. In a new conversation, `@` selects Somewhere. Account and workspace policies apply. - Other MCP clients: `https://mcp.somewhere.cloud/mcp`, with OAuth. Connecting opens the Somewhere sign-in page. Signing in or creating an account keeps the work in that account. "Continue without an account" starts a guest session: projects, deploys, the database and files work, but the session has no reusable login. `connector_link_email` with the human's email sends a verification link valid for 30 minutes; until it is verified, recovery on another device, outbound email, custom-domain purchases and payments stay unavailable and nothing is merged. The first app, as connector calls: 1. `project_create({ name: "my-app" })` creates the project with the same starter `somewhere init` writes, deployed as its first release. The response has the live URL (`url`) and the starter's `AGENTS.md` contract and file map. 2. `project_patch` changes selected files (whole contents or find/replace); `project_deploy` publishes a full set of raw source files. The platform compiles the source, and each call deploys. 3. `project_view_urls({ project_id })` lists the project's URLs. On Claude, `site_verify({ project_id, url })` loads the live URL at desktop and phone sizes and reports page errors and failed requests. On ChatGPT, which has no `site_verify`, `site_check({ project_id })` runs a site check of the deployed app and `site_check_status({ project_id })` returns its results. 4. `docs({ topic })` returns full contracts: `declared-data` (tables and access rules), `auth-client` (sign-in), `recipe-signed-in-app` (a complete signed-in app) and `deploy`. Success is the same as on the terminal path: the live URL works for a test user, data persists, and the human owns the project (a signed-in account, or a verified guest link). Design: https://somewhere.cloud/design.txt Link index: https://somewhere.cloud/docs/llms.txt Reference: https://somewhere.cloud/docs.txt Agent contract: https://somewhere.cloud/agent.txt