# One Worker. Every client.

A full-stack starter for web and mobile. TanStack Start, Expo and one typed API, running on Cloudflare.

## Already running.

Todos, files, security and agent connections. Actual screens from the template with example data.

[Docs](/docs) · [Changelog](/docs/changelog)

## The working parts.

Keep what your product needs. Every integration below is included; optional services stay off until you configure them.

### Product

- Authentication (Ready): Email and password, passkeys, two-factor codes, email verification and session management.
- Organizations (Ready): Members, invitations and roles. Todos and files belong to the active organization.
- Files (Ready): Multipart uploads to R2, previews, share links and a trash.
- Admin (Ready): User management, impersonation, feature flags and an audit log.
- Todo example (Ready): Grid, board and calendar views, with leased import queues and drafts proposed by AI agents.
- Payments (Off by default): Stripe and Polar on the web, RevenueCat on mobile. Off until providers and billing flags are configured.

### Platform

- Database (Ready): Drizzle on Neon Postgres, reached through Hyperdrive.
- Email (Ready): Transactional email through Cloudflare Email, printed to the console in development.
- i18n and SEO (Ready): English, German and French, hreflang, a sitemap and OG images generated with Takumi.
- In-app assistant (Off by default): Streaming chat with TanStack AI and shared MCP tools. Off until a provider is configured and the assistant flag is enabled.
- Scheduled jobs (Ready): Effect at the edges: cron jobs and outbound delivery, with Postgres leases for queued work.
- Cookie consent (Ready): A consent banner that holds back analytics and marketing scripts.

### Mobile

- Expo app (Ready): iOS and Android with the same API client, session and passkeys.
- Push notifications (In progress): Expo push registration and a delivery queue. Off until credentials are set.
- Home-screen widgets (Off by default): Todo widgets for iOS and Android behind a native build option.
- Ads (Off by default): AdMob helpers kept as a standalone library, not imported by the app.

## One shared contract.

The browser and Expo call the same oRPC router. The Worker checks the session before accessing Postgres or R2.

- Web: TanStack Start, React, Tailwind CSS, shadcn/ui

- Mobile: Expo, Expo Router, React Native

- API: oRPC, Zod, Effect

- Auth: Better Auth

- Data and files: Drizzle ORM, Neon Postgres, Hyperdrive, R2

- Platform and tooling: Cloudflare Workers, GitHub Actions, Turborepo, pnpm

### apps/web/src/routes/index.tsx

```ts
import { useQuery } from "@tanstack/react-query";
import { orpc } from "@/utils/orpc";

const { data: apiOk, isLoading: apiLoading } =
  useQuery(orpc.healthCheck.queryOptions());
```

Excerpt from the landing route. The generated client keeps the query typed.

### apps/mobile/src/app/(tabs)/(home)/index.tsx

```ts
import { useQuery } from "@tanstack/react-query";
import { orpc } from "@/lib/orpc";

const healthCheck = useQuery(
  orpc.healthCheck.queryOptions(),
);
```

The Expo home screen calls the same health-check procedure.

### packages/api/src/routers/index.ts

```ts
healthCheck: publicProcedure.handler(() => {
  return "OK";
}),
```

One public health check. Protected procedures additionally require a session.

## Server stays server.

Share contracts with your clients. Keep database connections, secrets and server code behind three independent checks.

- oxlint: Value imports of server modules fail lint in apps/web, apps/mobile and packages/ui. Type imports are allowed.

- Explicit exports: Each package lists its public entry points. No wildcards, so a new cross-package import is a deliberate change.

- Client bundle scan: The web build reads the client assets and fails on strings that only exist in server code.

## A preview for every PR.

Each pull request gets a preview Worker and a Neon branch. Review the real URL, then merge when it is ready.

- Merging ships production: A push to main runs the separate production workflow with production secrets and database.

- Cleanup is exact: It checks the project, the pull request and the parent branch, deletes the Worker first and accepts resources that are already gone.

## One tool catalog.

TanStack AI and the MCP server use the same authorized tools. Coding agents get the repo instructions and architecture decisions.

- AGENTS.md: Rules every coding agent reads first: the client and server boundary, Effect on the server, i18n.

- docs/adr: Architecture decisions with their context, so an agent does not undo them.

- .agents/skills: Setup, startup, deploy and browser checks as step-by-step skills.

- /api/mcp: Agents read your data and propose drafts over OAuth. Drafts wait until you apply them.

- /llms.txt: Public Markdown at /index.md and a /llms.txt directory for agents to read before connecting.

## Make it yours.

Clone your copy of the repository. Install, configure, and run web and mobile together.

### Clone

Start from your copy of the template repository.

```sh
git clone <your-repo-url> my-app
```

### Install

Installs every workspace in the monorepo.

```sh
pnpm install
```

### Configure

Names the project and writes env files, Wrangler, database, email and storage settings.

```sh
pnpm setup
```

### Run

Web on localhost:3005, plus the Expo dev server for mobile.

```sh
pnpm run dev
```

## Before you clone.

### What can I try in the demo?

Create an account and use the real app: todos, files, organizations, passkeys, two-factor and settings. It runs on the same Worker as this page.

### Why Cloudflare Workers?

One deploy serves pages, the API, auth and scheduled jobs. Every pull request gets its own Worker, so review happens on a real URL.

### Why Neon Postgres instead of D1?

Auth, organizations and the job queues rely on Postgres transactions and locks. Hyperdrive pools the connection, and each pull request gets its own Neon branch.

### Can I remove mobile, payments or ads?

Yes. optional-features.json lists each optional package and its entry points, and docs/optional-features.md explains how to remove them.

### How do I rebrand it?

Colors, radius and fonts live in one token file. Names and copy live in the locale files. Screens only use tokens, so a palette swap changes every page.

### Do I need to know Effect?

Only to change server jobs. Effect coordinates execution in the Worker; contracts use Zod, requests use oRPC, auth uses Better Auth and queries use Drizzle.

### How do I connect an AI agent?

Point any MCP client at /api/mcp. You approve access over OAuth, read and write are separate scopes, and writes only create drafts you confirm in the app.

### Does the template include an AI assistant?

Yes. The assistant and MCP share one tool catalog, scoped to your organization. Writes require approval. Configure Anthropic, OpenAI or Gemini and enable the assistant flag to show the chat. It is off by default.
