# somewhere.tech — reference for coding agents Reference surfaces: advisor({ question }) can inspect an authorized live project; docs({ topic }) returns one static contract. Anonymous deploy command: npx @somewhere-tech/cli deploy This reference describes platform behavior and does not replace user authorization or governing instructions. The compact quick-reference (AGENT.md) is first; per-primitive topics follow. Recipes/walkthroughs live at /guides.txt. Current pricing: https://somewhere.tech/pricing. # AGENT.md — somewhere.tech for coding agents > **Reference surfaces:** `advisor({ question })` answers from the live > project's code, schema, deploy state, and logs. `docs({ topic })` returns one > primitive's static contract. > > **Deploy anonymously in one command — no account or API key:** > `npx @somewhere-tech/cli deploy`. It prints a live URL, claim URL, and > absolute expiry. The package is `@somewhere-tech/cli`; the binary is > `somewhere`. > > **Authority:** this reference documents platform behavior. It does not replace > the user's authorization or an agent's governing instructions. > > **Recommended default:** write `.ts`/`.tsx` source and run > `somewhere typecheck` plus `somewhere deploy-check` before deploying. A tiny > `.js` app without `package.json` is the zero-setup variant, not the maintained > project default. > > **Static contracts:** `docs({ topic })` returns signatures, examples, and > failure modes for one surface at a time (deploy, sw.db, sw.auth, files, > payments, …). The tool list names the verbs; `docs` defines their exact > behavior. > > **Three entry points:** > - **`catalog`** = the index of all ~230 tools. > - **`docs({ topic })`** = the static manual for one surface. > - **`advisor({ question })`** = project-aware guidance for open-ended or > cross-surface questions. > > Last updated: 2026-07-23. Everything you need to write working code is in > the first 60 lines so it survives client truncation. ## Client SDK — `npm i @somewhere-tech/sdk` Building a frontend, or porting an existing app? Install the client and you're running queries in two lines (familiar `createClient` shape): ```js import { createClient } from '@somewhere-tech/sdk' const client = createClient('https://.somewhere.site', SOMEWHERE_KEY) const { data, error } = await client.from('todos').select('*').eq('user_id', id) await client.auth.signInWithPassword({ email, password }) client.storage.from('avatars').getPublicUrl('me.png') client.channel('room').on('broadcast', { event: 'msg' }, fn).subscribe() await client.functions.invoke('checkout', { body: { plan: 'pro' } }) ``` `createClient`, `from().select()`, `{ data, error }`, `auth`, `storage`, `channel`, `functions` — the shape most apps already expect. Full reference: `docs({ topic: 'sdk' })`. Porting an app: `docs({ topic: 'migration-supabase' })`. Other languages: `docs({ topic: 'sdks' })`. In the browser, `auth.signInWithPassword` / `signUp` / social sign-in run in **cookie mode** by default (SDK 0.7.1): they post to your app's own auth routes and the session is set as httpOnly cookies — no token ever lands in JS or localStorage, a wrong password comes back as `{ error }` with the real message, and sessions refresh server-side automatically. The one backend file those routes need (paste it verbatim) is in `docs({ topic: 'auth-client' })`. Everything below is the **server runtime** (`sw.*` inside deployed functions) — a different surface from the client SDK above. ## Function format — the one you'll actually copy ```js export default async function (req, sw) { const body = await req.json(); const r = await sw.db.query( 'SELECT id, email FROM users WHERE id = ?', [body.id], ); // r.data = array of row objects (NOT .rows, NOT .results) // r.count = number of rows returned // r.changes = rows affected by INSERT / UPDATE / DELETE return Response.json({ user: r.data[0] ?? null }); } ``` That's the function signature. `req` is a standard `Request`; you return a `Response`; `sw` is the project-scoped platform namespace. `sw.endpoint({ auth, body, rateLimit, handler })` exists as a thin wrapper around the above (handles auth + zod validation + rate limit for you). It's optional, not required. The bare `export default async function(req, sw)` form always works and is what you should reach for when in doubt. ## The sw object ``` sw.db.query(sql, params) → { data, count, changes } sw.auth.signup / login / me / fromRequest(req) sw.email.send({ to, subject, text|html }) sw.ai.chat / ai.embed / ai.transcribe / ai.tts / ai.generate_image sw.agent.run / agent.start / agent.status / agent.cancel sw.fs.read / fs.write / fs.list / fs.delete / fs.public_url sw.env.YOUR_KEY (env var; server-side, never returned by an API — EXCEPT VITE_*/REACT_APP_* names, which are compiled into PUBLIC browser JS. See Secrets.) sw.jobs.create / cron.create / queue.send sw.calendar.hold / confirm / release (atomic booking holds) sw.payments.checkout / quote (Stripe Connect; per-user onboarding) sw.billing.has(userId, feature) (gate YOUR app's plans/features — definePlans / entitlements) sw.fetch(url, opts) → { data, error, response } (server-side outbound HTTP) ``` Detail per area: `docs({ topic: 'sw.db' | 'sw.auth' | … })`. **Building client-side login (browser session + Google)?** The happy path is httpOnly **cookie sessions**: one pasteable backend file, zero auth code in the browser, nothing in localStorage, and expected failures (wrong password, duplicate email) surface as structured 4xx with the real message. `docs({ topic: 'auth-client' })` has the exact code — works with the SDK's cookie mode or plain `fetch`, nothing to install. Do NOT hand-roll a token/session layer; that's where every auth bug lives. ### npm imports work inside functions `import { z } from 'zod'` and friends resolve automatically at deploy. Versions in `package.json` become pins (`zod@^3.22` pins to `3.22`); unpinned imports get the latest. `node:*` standard-library imports and the platform runtime built-ins pass through as externals. No `npm install` step — you deploy raw source and the platform resolves your imports for you. ### CORS is handled by the platform `/api/*` requests get a free CORS layer in front of every function: OPTIONS preflights return 204 with the right headers, and every function response is auto-decorated with `Access-Control-Allow-Origin` (echoes request Origin), `-Credentials: true`, and the common method/ header allow-lists. Don't write CORS boilerplate in your handlers. ### Outbound requests from a function — `sw.fetch` Call other APIs from inside a deployed function with `sw.fetch(url, opts)` — same call shape as browser `fetch`, but returns `{ data, error, response }` instead of throwing: `data` is the parsed JSON (or text, or `null` for an empty/non-JSON body), `error` is set on a non-2xx status or a transport failure, `response` is the raw `Response`. Idempotent requests (GET/HEAD, or any method with `idempotent: true`) retry automatically on a transient failure or a 429/502/503/504; pass `retry: false` to disable. `timeout_ms` caps the call (default 10s, max 60s); `max_redirects` caps redirect hops (default/max 5). Private, loopback, link-local, and cloud-metadata hosts are blocked — this is for calling third-party APIs, not your own internal network. This is a server-runtime helper for deployed functions, not a browser API — client-side code still uses the browser's own `fetch`. ### Every live deploy is automatically analyzed After every successful live `project_deploy` / `project_patch`, the platform refreshes the project analysis for that live version. A rollback restores the analysis already stored with the selected live version. The available analysis surfaces are: - **LLM security review** (premium, Builder+ tier) — an LLM pass over your deployed source. Returns structured Markdown findings against 9 risk categories (auth bypass, raw SQL, unsafe payment metadata, env leakage, privilege escalation, RCE, email spoofing, conversation hijack, CSRF). Run on-demand after deploy: → `security_review({ project_id, focus? })` or `POST /v1/security/review`. - `project_screenshots({ project_id })` or `GET /v1/deploy/screenshots` — desktop + mobile PNGs of the live homepage per version (every tier). - `project_docs({ project_id })` or `GET /v1/project-docs` — deploy-keyed readable docs plus a Mermaid project map. - `project_description({ project_id })` or `GET /v1/deploy/description` — 2-3 sentence plain-English summary ("Restaurant booking app with AI chatbot. 89 users browse 24 restaurants and book tables."). Screenshots, architecture, and description refresh automatically in post-live-deploy work. The LLM security review is on-demand today. Separately, every deploy ALSO runs a quick regex scanner (`scanFunctionGuardrails`) during compile. Catches obvious footgun patterns and surfaces them as advisory `warnings` in the deploy response. Not the LLM review — it's the cheap synchronous gate. Use these before editing: pull the architecture endpoint to know what exists; read the description to remember what an unfamiliar project does; call `security_review` after a meaningful change. Full reference: `docs({ topic: 'deploy-intelligence' })`. ## The five rules 1. **Deploy raw source — the platform compiles.** Ship `.jsx` / `.tsx` / `.ts` / `.html` directly. Never run `npm run build`, `vite build`, `esbuild`, `tsc`, or similar first. `/v1/deploy` HARD-REJECTS pre-bundled output with `BUNDLED_DEPLOY_REJECTED` (400) — this includes pre-bundled `api/*.mjs` functions, not just `dist/` static output. Escape hatch: `somewhere deploy --prebuilt`, the project setting, or `allow_bundled: true`. 2. **Use the CLI for deploys, MCP for reads.** `somewhere deploy` reads files from disk in one trip; `project_deploy` over MCP re-serializes the same files to JSON (~750× more tokens). Use MCP for `db_query`, `project_export`, `fs_read`, `project_logs`, etc. 3. **Functions use the format above.** `export default async function(req, sw)`, never `curl` with a `smt_` bearer from inside a deployed function — the bundle injects the key automatically through `sw.*`. 4. **`project_patch` for edits, not full re-deploys.** Sending `{find, replace}` is ~200 bytes on the wire; resending a whole project burns the conversation. See `## Building an app`. 5. **`/v1/fs/*` for files.** `database`, `auth`, `email`, `payments` are built in — never reach for a separate database, auth, or payments service. Before generating frontend code, read the design directive: `https://somewhere.tech/design.txt` — opinionated aesthetic rules (tone, fonts, color hierarchy, layout-breaking, motion, atmosphere, variance) so your UI looks designed and distinct, not default-generic. Platform mechanics (live deploy verification, CSS-var theming) live in `docs({ topic: 'design-system' })`. ## Get connected (pick the path that fits your environment) ### A. Local terminal — install CLI, get MCP for free ```sh npm i -g @somewhere-tech/cli somewhere auth login # browser flow; writes ~/.somewhere/config.json ``` CLI and MCP share `~/.somewhere/config.json` — one login, both work. Use the CLI from this point on; reach for MCP for in-context reads. ### B. Ephemeral environment (Claude Code Web, sandboxes, container) If the user is in Claude Code Web, a sandboxed container, or any environment without a persistent home directory, the browser-based `somewhere auth login` won't outlive the session. Pair the CLI to your already-authenticated MCP session: ```text 1. npm i -g @somewhere-tech/cli # 5 seconds 2. Call MCP tool auth_cli_pair() # returns { key, expires_at } 3. somewhere auth set # 24h-TTL token 4. somewhere whoami # confirm identity 5. somewhere deploy # ready to go ``` The minted key is `kind='cli_pair'`, expires in 24h, and is revocable in isolation if leaked. If the agent doesn't have shell access at all (pure MCP — e.g. Claude.ai connector), skip the CLI entirely and use the MCP tools directly: `project_deploy` for writes, `fs_read` / `db_query` / `project_logs` for reads. ### C. Connector / no shell at all Pure MCP. Every CLI command has an MCP equivalent — `project_deploy`, `project_patch`, `project_export`, `db_query`, `fs_*`, `project_logs`. Slower per write than the CLI (files round-trip as JSON) but identical behavior. Skip steps that require the shell. ## What is somewhere.tech A platform for building and deploying apps. One login, one MCP connection. Database, auth, storage, email, AI, jobs, cron, domains — all built in. **Reference app:** [emailsomewhere.com](https://emailsomewhere.com) — a complete email product built end-to-end on the same `sw.*` primitives you have access to. 15 API routes, 12 tables, AI inbox classification, realtime UI. Worth a look if you want a worked example of what a real app looks like on this platform. ## Deploy `src/`, never `dist/` or `build/` **Do not run `npm run build`, `vite build`, `next build`, or any other build step before deploying.** The platform compiles JSX / TSX at deploy time. Ship raw source — `.jsx`, `.tsx`, `.html`, `.css`, your images, your `api/` functions. Why this matters: every "edit live code" feature on the platform breaks on bundled output. `project_export` returns minified artifacts you can't meaningfully edit. `project_design_tokens` extracts mangled variable names. `project_patch find/replace` substrings won't match across content-hashed filenames. The visual annotator has no source-map back to your real component file. `somewhere pull` is for round-tripping the source you shipped — if you shipped a build artifact, that's what comes back. **`/v1/deploy` rejects bundled output with HTTP 400 `BUNDLED_DEPLOY_REJECTED` unless `--prebuilt`, the project setting, or `allow_bundled: true` opts out.** Triggers: files under `dist/` / `build/` / `.next/` / `out/`, content-hashed assets like `assets/index-.js`, source-map files at deploy root, large minified JS, OR pre-bundled function files (`api/*.mjs` containing esbuild/webpack helper banners like `__toCommonJS`, `__defProp`, `__toESM`). Escape hatches (use only when you genuinely need pre-built output — a native binary or a specialized prebuilt pipeline): - CLI, one-off deploy: `somewhere deploy --prebuilt`. - CLI, whole project default: turn on "Allow prebuilt deploys" in the project's Settings. - REST/MCP equivalent: `{ "allow_bundled": true }` in the deploy body. - Intentionally shipping already-built static assets: `somewhere deploy --scope static --prebuilt`. For functions specifically: do not run `esbuild` / `tsc` on `api/**/*.ts` before deploying. The platform compiles each `.ts` / `.tsx` function at deploy time and resolves cross-function imports (`import { foo } from './webhook'`) correctly — pre-bundling breaks that because each function becomes its own entry with no shared module graph. ## Authentication Run `somewhere auth login`. The browser opens, the user approves, the session is saved to `~/.somewhere/config.json`, and Claude Code's MCP config is wired automatically. The CLI and the MCP bridge both read that file — no API key juggling. Human authentication uses the browser flow and never requires sharing an API key, setting `SMT_API_KEY`, or pasting a token into a config. The CLI handles auth the same way `gh auth login` does. The `smt_` developer key still exists for CI/CD, server-to-server jobs, and webhooks. It is never part of a human setup flow. Developer keys can be scope-limited at mint time: `POST /v1/keys` with `{ "scopes": ["ai:complete", "db"] }` (a scope is the `/v1/…` path with `:` separators, max 32 per key) returns a key that gets 403 outside those paths. Keys minted without `scopes` have full access. ## Two ways to use the platform FROM OUTSIDE (MCP tools + CLI / SDK / REST API): Call tools like `db_query` from Claude Code via MCP for reads. For deploys, prefer the CLI: `somewhere deploy`. It authenticates from the same session as `somewhere auth login`. Generic API escape hatch: on the full `/mcp` surface, `api({ calls, confirm? })` can call platform `/v1/...` endpoints that do not yet have a dedicated MCP tool. Use `catalog` and `docs({ topic })` first when a dedicated tool or documented shape exists. Safety model: `api` only accepts platform paths, never hosts or full URLs, forwards the caller's own auth, and the same permission checks as dedicated tools still apply. It rejects path traversal, URL-like paths, encoded path separators, whitespace/control characters, `/v1/admin*`, and `/v1/internal*` after normalizing the path. `GET` and `HEAD` calls run without confirmation; every `POST`/`PUT`/`PATCH`/`DELETE` call requires `confirm:true` (fully fail-safe — no write runs unconfirmed through this tool). Max batch size: 10 independent calls. The curated connector surface does not expose this tool. FROM INSIDE (deployed functions): Your server-side code runs ON the platform. Functions use `sw.db`, `sw.fs`, `sw.email`, `sw.ai`, `sw.auth` directly with no HTTP call or developer key inside functions. ```js export default async function(req, sw) { // Throws 401 if not signed in. Use sw.auth.fromRequest(req) instead // (returns null) when the route should also work for anonymous users. const user = await sw.auth.requireUser(req) // Pass { user } as the 3rd arg and the platform automatically rewrites // the SQL to AND owner_id = ?. Works for single-table SELECT / UPDATE / // DELETE / INSERT against any user-scoped table — no SCOPE_VIOLATION, // no need to write the WHERE yourself. const posts = await sw.db.query( 'SELECT * FROM posts ORDER BY created_at DESC', [], { user } ) return Response.json(posts) } ``` Database size boundaries are distinct: SQL text is limited to 100,000 bytes per statement. Treat 2,000,000 bytes as the documented portable maximum for a single string, binary value, or complete resulting row. The database may accept some larger values depending on the encoded row, but behavior above that supported maximum is not guaranteed; a rejection returns `DATABASE_VALUE_TOO_LARGE`. SQL-text failures return `STATEMENT_TOO_LARGE`. Store large content in `sw.fs` and keep its path or URL in the row. Database usage reporting is evidence-based and does not turn accounting into a write blocker. Successful writes through the public database API and deployed functions refresh the byte snapshot when the database returns post-write size metadata. Some internal maintenance paths do not refresh usage in the same operation; a complete paginated database inventory reconciles every attached project database and remains authoritative. If an accounting refresh fails, the already-committed write still succeeds. ### User-scoped tables `POST /v1/db/scopes` (developer key) declares a table as user-owned: ```json { "project_id": "abc", "table": "notes", "owner_column": "user_id", "sensitive_columns": ["body", "email"] } ``` Inside a deployed function, pass the current user to `sw.db.query`. The platform rewrites a simple single-table statement to add the owner constraint. ```js const rows = await sw.db.query('SELECT * FROM notes', [], { user }) const made = await sw.db.query('INSERT INTO notes (title) VALUES (?)', ['x'], { user }) const fixed = await sw.db.query('UPDATE notes SET title = ? WHERE id = ?', ['y', 5], { user }) ``` This automatic path covers SELECT, INSERT, UPDATE, and DELETE against one scoped table. JOINs, comma-joins, CTEs, subqueries, UNIONs, and other multi-table shapes are refused with `SCOPE_VIOLATION`; query each scoped table separately and combine the results in function code. Trusted server functions can state that a query is intentionally cross-user (for example an admin report) with: `sw.db.query('SELECT COUNT(*) FROM notes', [], { unscoped: true })`. That is an explicit authority choice, not an app-user escape hatch. The boundary fails closed: - `{ user }` against a table without declared `scoped` intent throws `SCOPE_NOT_DECLARED`. - A scoped-table query that omits both `{ user }` and `{ unscoped: true }` throws `SCOPE_VIOLATION`. - `sw.db.batch` does not accept `{ user }`; use separate simple scoped statements or a trusted `{ unscoped: true }` transaction. - Browser app-user database access is structured table access only. Raw SQL at `/v1/db/query` and `/v1/db/batch` is refused. - There is no grace interval: once a scope is active in the serving bundle, violations are blocked. To mark a table intentionally shared, call `db_scope_set` with `intent: "shared"`. To remove the declaration entirely, use `DELETE /v1/db/scopes/:table?project_id=…` with developer authority. **`sensitive_columns`** is a dashboard-side privacy nudge, not a worker-enforced wall. The dashboard's database browser redacts those column values with a click-to-reveal control; each reveal is logged to `scope_access_log` so casual browsing leaves a paper trail. The developer's own SQL queries return full values — this is friction-as-security for incidental dashboard browsing, not a hard block. List recent reveals via `GET /v1/db/scopes/reveals?project_id=…` (developer key only). ### Change webhooks Register an HTTPS endpoint to be notified after every successful INSERT / UPDATE / UPSERT / DELETE through `/v1/db/query`. One webhook per project; no row contents leak through — payload is just `{ project_id, table, op, rows_affected, ts }`. ```js // In a deployed function: const { secret } = await sw.db.onchange.set('https://example.com/db-hook', { events: ['insert', 'update', 'delete'], // optional; default is all three }); // Read or remove the registration later: await sw.db.onchange.get(); await sw.db.onchange.delete(); ``` REST equivalents (developer key only): - `PUT /v1/db/webhook` — `{ project_id, url, events? }` returns the registration including `secret`. - `GET /v1/db/webhook?project_id=…` — read current registration (or `{ webhook: null }`). - `DELETE /v1/db/webhook?project_id=…` — remove the registration. Every POST carries `X-Somewhere-Signature: t={ms},v1={hex}` where `hex = HMAC-SHA256(secret, "{ms}." + rawBody)`. Verify with a constant-time compare and reject requests older than your chosen tolerance (5 minutes is typical) to block replays. The webhook fires from `c.executionCtx.waitUntil` after the write commits, so your handler's latency never blocks the developer's query. Failed deliveries are recorded as `last_status: "failed"` / `last_error: …` on the registration — there is no automatic retry, so make your endpoint idempotent and fast. ### Exporting your data (no lock-in) `sw.db.dump()` (inside a function) and `db_dump` (MCP) and `POST /v1/db/dump` (REST) all return the project's database as a single `.sql` file — schema + every row as `INSERT` statements. It's a standard, portable SQL file: restore it into any standard SQL database, or convert it to Postgres. One call, you have everything, take it anywhere. Per-table cap is 1,000,000 rows (in-worker memory bound); larger tables get a truncation comment at the bottom of the dump and a streaming export available on request. For a single table as CSV, use `db_export` / `sw.db.export()` instead. **For demos and production, pin a premium frontier model** such as `provider: 'anthropic', model: 'claude-sonnet-4-6'`. When you omit both provider and model, the platform uses the free default `deepseek-v4-flash`: no activation, no user billing, rate-limited and capped. Explicit `provider: 'workers-ai'` remains available for included provider-specific models. Call `ai_catalog` for the live list of available models and their per-model pricing. ## Building an app — typical flow 1. `project_create` → creates the project 2. `db_migrate` or `db_migrate_to` → set up your database 3. `project_deploy` with files + functions → deploy frontend + API 4. Live at `https://{subdomain}.somewhere.site` **Schema changes always go through step 2** — `db_migrate` / `db_migrate_to`, never an inline `CREATE TABLE` in a function handler and not through `db_query`. Runtime DDL runs on every request instead of once, is un-versioned (no migration history, nothing to diff or roll back), and a new table with an `owner_id`/`user_id` column silently misses the platform's per-user data protections — cross-user readable until it's declared scoped. (`db_query` flags DDL with a `schema_advisory` nudge if you slip.) Migrate once, developer-side; handlers read/write rows with `sw.db.query`. For incremental edits after the first deploy, use `project_patch`. One file per call. Two modes: send `find` + `replace` for surgical tweaks (the token-optimal path), or send `content` to rewrite the whole file. **Find / replace (cheapest, preferred for small edits):** ```json { "project_id": "my-app", "path": "index.html", "find": "

Pricing

", "replace": "

Plans & pricing

" } ``` ~200 bytes on the wire. Replaces every occurrence. If `find` doesn't match, you get `FIND_NOT_FOUND` with a snippet of the current file so you can retry with the right substring. **Full content (when you're rewriting most of the file):** ```json { "project_id": "my-app", "path": "api/hello.ts", "content": "..." } ``` Static files go live in ~1s; function changes in 2–4s. Paths are auto-routed (anything under `api/` / `_lib/` or matching a root `[id].ts`-style route is a function; everything else is static). Binary assets (images, fonts) go through the binary write surface, not `content` or `find`/`replace`. **Use `project_deploy` only for the first deploy of a brand-new project** or when you actually want to replace the whole tree. For every other edit, reach for `project_patch`. **JSX / TSX compile on deploy.** Files ending in `.jsx` or `.tsx` are compiled to JS automatically when you deploy or patch them. You ship raw `.jsx` source; the platform stores the source on its own internal path (round-tripped by `project_export`) and serves the compiled JS to the browser. There is no build step, no `npm run build`, no Vite config. Functions can also `import` `.json` files directly (`import data from '../data/stations.json'`) — no need to bake JSON into code. **Patch responses verify what's actually served.** After a patch that rebuilds the app, the platform fetches the live page and checks every referenced bundle really landed. If something didn't, the response carries `status: "served_incomplete"` plus a `served_incomplete` array naming the missing assets — treat that as "the page may be broken right now, re-run the patch or roll back," not as success. **Full bundling.** When the deploy includes an entry like `src/main.jsx` or `src/main.tsx` referenced from `index.html`, the platform follows every relative import (`./Header`, `../lib/auth`) and bundles them into one (or more, with code-splitting) hashed chunk. Bare `import React from 'react'` and similar npm-style imports are resolved automatically at deploy — no `node_modules`, no `npm install`, no `package.json` required. If your project does ship a `package.json`, the platform uses the `dependencies` versions to pin your imports — that's how you upgrade to React 19, ship a specific lodash version, etc. Your `index.html` is rewritten at deploy time so its ` ``` `src/main.tsx` — React app (raw TSX, deployed as-is): ```tsx import { createRoot } from 'react-dom/client'; import { useEffect, useState } from 'react'; function App() { const [msg, setMsg] = useState('loading...'); useEffect(() => { fetch('/api/hello').then(r => r.json()).then(d => setMsg(d.message)); }, []); return

{msg}

; } createRoot(document.getElementById('root')!).render(); ``` `api/hello.ts` — server function (raw TS, deployed as-is): ```ts export default async function(req, sw) { return Response.json({ message: 'hello from a function' }); } ``` Now deploy: ```bash somewhere deploy ``` The CLI uploads the source files. The platform's compiler bundles `src/main.tsx` into a hashed JS chunk under `/_compiled/`, rewrites `index.html` to load it, and compiles `api/hello.ts` into a function the runtime can execute. The public URL serves that live deploy. (For just-a-static-page, you can ship a single `index.html` with plain HTML — the JSX/TSX compiler only runs on files that need it.) ## Common follow-up commands ```bash somewhere logs # tail recent function logs somewhere deploy # redeploy current directory somewhere mcp stdio # raw stdio MCP bridge (for hosts you wire by hand) somewhere auth status # show current login ``` For everything else (custom domains, project rename, env vars, billing) go through the dashboard at https://somewhere.tech or call the matching MCP tool from your editor. ## Ephemeral environments (Claude Code Web, sandboxes, containers) If the user is in an environment without a persistent home directory (Claude Code Web, a fresh CI container, a sandboxed runner), the browser-based `somewhere auth login` won't survive the session — but the MCP connection already has an authenticated session. Pair the CLI to that session in one step: ```text 1. npm i -g @somewhere-tech/cli # ~5 seconds 2. Call MCP tool auth_cli_pair # returns { key, expires_at } 3. somewhere auth set # 24h-TTL token 4. somewhere whoami # confirm identity 5. somewhere deploy # ready to go ``` `auth_cli_pair` mints a short-lived `smt_` key (kind='cli_pair', 24h TTL) scoped to the same user as the MCP session. The agent writes the token to the CLI config; the CLI works for the rest of the session. The key auto-expires; revoke early with `DELETE /v1/keys/` if you need to. Pure-MCP path (Claude.ai connector, no shell at all): skip the CLI entirely. `project_deploy` does the same write `somewhere deploy` would do — it just costs more tokens because the files round-trip as JSON. Reads (`fs_read`, `db_query`, `project_logs`) are identical either way. --- ## @somewhere-tech/sdk — the client SDK (sdk) The client for talking to the platform from a browser, a Node server, or any JS/TS runtime. It's the on-ramp for existing apps and AI-generated code: a familiar `createClient` → `from().select()` → `{ data, error }` shape. ```bash npm i @somewhere-tech/sdk ``` ## createClient ```js import { createClient } from '@somewhere-tech/sdk' const client = createClient(SOMEWHERE_URL, SOMEWHERE_KEY) ``` - SOMEWHERE_URL — your project URL, https://.somewhere.site. It's the functions.invoke host and how the client infers your project id. On a custom domain pass { projectId }: createClient(url, key, { projectId: 'my-app' }). - SOMEWHERE_KEY — an app-user token (browser) or a developer smt_ key (server only — never ship it to a browser). The smt_ prefix is detected automatically. The explicit form new Somewhere({ key, projectId }) is the same client. ## Database — from() ```js const { data, error } = await client.from('todos').select('*').eq('user_id', id) await client.from('todos').insert({ title: 'New', user_id: id }) await client.from('todos').update({ done: true }).eq('id', todoId) await client.from('todos').delete().eq('id', todoId) await client.from('users').upsert({ id, name }, { onConflict: 'id' }) // OR group, AND-ed with the rest: await client.from('todos').select('*').or('status.eq.active,priority.gt.3').eq('user_id', id) ``` Filters: eq, neq, gt, gte, lt, lte, like, ilike, in, is, match, or. Modifiers: order, limit, range, single, maybeSingle. Every call returns { data, error, count } — error is null on success, data is null on error. ## Auth — sw.auth ```js await client.auth.signUp({ email, password }) await client.auth.signInWithPassword({ email, password }) await client.auth.signInWithOAuth({ provider: 'google' }) // one call → data.url const { data: { user } } = await client.auth.getUser() client.auth.onAuthStateChange((event, session) => { /* SIGNED_IN / SIGNED_OUT / ... */ }) await client.auth.signOut() ``` In the browser these run in **cookie mode** by default (0.7.1): the SDK posts to your app's own auth routes (`/api/auth/*` — one pasteable backend file, see docs({ topic: 'auth-client' })) and the session is set as httpOnly cookies. No tokens in JS or localStorage; `error.message` carries the real cause ("Wrong email or password."). `getSession()` returns `{ cookie_session: true, user }`. Node/CLI keep header mode (tokens in SDK memory); pass `{ authMode: 'header' }` to opt a browser out. Everything else works directly from the browser. ## Storage — client.storage.from(bucket) ```js await client.storage.from('avatars').upload('me.png', file) client.storage.from('avatars').getPublicUrl('me.png') // { data: { publicUrl } } await client.storage.from('avatars').createSignedUrl('me.png', 3600) await client.storage.from('avatars').download('me.png') await client.storage.from('avatars').remove(['me.png']) ``` ## Realtime — client.channel(name) ```js client.channel('room') .on('broadcast', { event: 'message' }, ({ payload }) => render(payload)) .subscribe() await client.channel('room').send({ type: 'broadcast', event: 'message', payload: { text: 'hi' } }) ``` ## Functions — client.functions.invoke ```js const { data, error } = await client.functions.invoke('checkout', { body: { plan: 'pro' } }) ``` Other languages → docs({ topic: 'sdks' }). Porting an existing app → the migration guide via docs({ topic: 'migration-supabase' }). --- ## Client SDKs — languages and status (sdks) Two official SDKs today. We'd rather ship two we maintain than a pile we don't. | Language | Package | Install | Status | | --- | --- | --- | --- | | JavaScript / TypeScript | @somewhere-tech/sdk | npm i @somewhere-tech/sdk | Stable — primary (v0.7.1) | | Python | somewhere-tech | pip install somewhere-tech | Stable (v0.5.0) | Both share the same shape: createClient → from().select() → { data, error }, plus auth, storage, and functions. The JavaScript SDK is the primary and carries the newest surface (createClient, functions.invoke, onAuthStateChange, realtime channel subscribe) — reach for it first, especially when porting an app. → docs({ topic: 'sdk' }). For the command line, → docs({ topic: 'cli' }). --- ## @somewhere-tech/cli — full command reference (cli) The CLI deploys from disk, manages local project links, and shares one login with the MCP bridge. No account yet? Deploy the current directory immediately: ```bash npx @somewhere-tech/cli deploy ``` The anonymous command returns a live URL and claim link; login is optional until you decide to keep the project. ```bash npm i -g @somewhere-tech/cli somewhere auth login # opens a browser; session lands in ~/.somewhere/config.json ``` The same session powers the CLI, the MCP bridge, and `somewhere deploy`. Do not pass an API key for a human setup flow; the `smt_` key is for CI/CD. `somewhere deploy` ships raw source (`src/`, `index.html`, `public/`, `package.json`, `api/`) unless you explicitly opt into `--prebuilt`. The normal path has no local build step. → docs({ topic: 'deploy' }). ## Global commands and flags | Command | Flags | What it does | |---|---|---| | `somewhere --version` | `-V`, `--version` | Print the CLI version. | | `somewhere --help` | `-h`, `--help` | Print top-level help. | | `somewhere help [command]` | none | Print help for one command. | ## Auth | Command | Flags | What it does | |---|---|---| | `somewhere logout` | none | Remove stored credentials. | | `somewhere whoami` | `--json` | Show current user info; `--json` prints the raw account response. | | `somewhere auth` | none | Credential command group. | | `somewhere auth login` | none | Authenticate with the browser flow. | | `somewhere auth set ` | none | Save an `smt_` token directly without a browser flow. | | `somewhere auth status` | none | Show current login state, device ID, and key name. | ## Projects | Command | Flags | What it does | |---|---|---| | `somewhere init` | `--name `, `--link` | Initialize a project in the current directory. `--name` skips the prompt; `--link` links to an existing project instead of creating one. | | `somewhere project` | none | Project command group. | | `somewhere project create ` | `--subdomain `, `--json` | Create a new project. | | `somewhere project list` | `--json` | List all projects. | | `somewhere project view [name-or-id]` | `--json` | View project details. | | `somewhere project delete ` | `--json` | Tombstone a project, take its hosts offline, and start the 30-day recovery period. | | `somewhere status [project]` | `--json` | Show project, active release, and workspace status. | | `somewhere open [project]` | `--dashboard` | Open the project URL in your browser; `--dashboard` opens the dashboard. | ## Advisor | Command | Flags | What it does | |---|---|---| | `somewhere ask ""` | `-p, --project `, `--json` | Ask the broad platform advisor. Works without login; anonymous calls use economy processing and end with “Log in for faster answers.” Signed-in calls are faster and `--project` adds live project context. | ## Deploy and pull | Command | Flags | What it does | |---|---|---| | `somewhere deploy [dir]` | `--project `, `--scope `, `--dry-run`, `--replace-functions`, `--prebuilt`, `--temporary`, `--force`, `--yes`, `--json` | Deploy a directory to the linked or specified project. `--scope functions` deploys backend only; `--scope static` deploys site files only. `--dry-run` prints the diff without deploying. `--replace-functions` removes deployed functions missing locally. `--prebuilt` opts into bundled output. `--temporary` creates a temporary workspace without an account. `--force --yes` overwrites remote changes without prompting. | | `somewhere pull [project]` | `--out `, `--force`, `--json` | Download the live deployed source files and scaffold local typecheck files when absent. `--out` defaults to the current directory. | | `somewhere typecheck [dir]` | `--json` | Run local `tsc --noEmit` over a pulled project. | | `somewhere rollback [project]` | `-y, --yes`, `--json` | Select the previous retained live release. | | `somewhere deploy-check [dir]` | `--project `, `--json` | Upload source for a server-side dry compile without deploying. | ## Database | Command | Flags | What it does | |---|---|---| | `somewhere db` | none | Database command group. | | `somewhere db query ` | `--project `, `--json` | Run SQL against the project database. | | `somewhere db dump` | `--project `, `-o, --output `, `--json` | Export the full database as SQL. | | `somewhere db tables` | `--project `, `--json` | List tables in the project database. | ## Environment, logs, and errors | Command | Flags | What it does | |---|---|---| | `somewhere env` | none | Environment variable command group. | | `somewhere env list` | `--project `, `--json` | List environment variable names. Values are not returned. | | `somewhere env pull` | `--project `, `--out `, `--force`, `--json` | Write a local env template for the local-dev loop. Values are not included. | | `somewhere env set ` | `--project `, `--json` | Set an environment variable. | | `somewhere env delete ` | `--project `, `--json` | Delete an environment variable. | | `somewhere logs [project]` | `--level `, `--source `, `--function `, `--endpoint `, `--since `, `--tail `, `--follow`, `--json` | Show recent logs. `--tail` defaults to 20; `--follow` keeps polling. | | `somewhere errors [project]` | `--limit `, `--json` | Show the curated recent-exceptions view. `--limit` defaults to 20 and caps at 100. | ## Local execution and inspection | Command | Flags | What it does | |---|---|---| | `somewhere run