Complete Field Guide

Monorepos &
Cloudflare Workers

How to structure a single repository for multiple deployment targets — Workers, mobile apps, dashboards — sharing the same TypeScript packages, orchestrated with kata.

01 / FOUNDATIONS

What Is a Monorepo?

A monorepo is a single Git repository containing multiple distinct projects — apps, services, packages — developed together but deployed independently. The "mono" refers to the repository, not the application.

Monorepo — one repo, many deployable units
my-company/one git repository │ ├── apps/deployable units (each ships independently) │ ├── worker-api/ ← deploys → Cloudflare Workers edge │ ├── worker-auth/ ← deploys → Cloudflare Workers edge │ ├── dashboard/ ← deploys → Cloudflare Pages │ └── mobile/ ← builds → iOS / Android via Expo │ ├── packages/shared code (never deployed directly) │ ├── shared-types/ ← TypeScript interfaces used by all apps │ ├── utils/ ← pure functions, validation, helpers │ └── api-client/ ← SDK for calling your Worker APIs │ ├── pnpm-workspace.yaml ← declares workspace members ├── turbo.json ← build pipeline orchestration └── tsconfig.base.json ← shared TypeScript config

The mental model: Packages are LEGO bricks. Apps are things you build from those bricks. You share the bricks — you don't share the finished builds.

02 / FOUNDATIONS

Monorepo vs Polyrepo

Polyrepo — version drift problem
worker-api/ worker-auth/ dashboard/ (separate repo) (separate repo) (separate repo) │ │ │ npm publish npm publish npm install @myorg/types @myorg/types @myorg/types v1.0.0 v1.0.0 v0.9.0 ← drift! A type change requires: publish → update → PR × N repos.
Monorepo — one source of truth
my-company/ (single repo) │ ├── packages/shared-types/ ← one source of truth │ ├── apps/worker-api/ ← "workspace:*" → always latest local ├── apps/worker-auth/ ← "workspace:*" └── apps/dashboard/ ← "workspace:*" Change shared-types once → all apps see it immediately. No publish cycle. No version drift. One PR covers everything.
ConcernPolyrepoMonorepo
Shared code changesPublish → bump → PR × NChange once, all see it
Type safety across servicesBreaks at runtime if types driftTypeScript catches at compile time
Atomic changesMultiple PRs, coordination overheadOne PR covers API + client + types
OnboardingClone N repos, set up N envsOne clone, one install
CI/CD complexitySimple per-repo, complex to coordinateOne pipeline, needs smart filtering
Blast radiusIsolated per repoBad merge can affect all packages
03 / FOUNDATIONS

The Apps + Packages Pattern

The kata creator's pattern: "Each worker I deploy to is an app but can also do like mobile build etc with the same shared code that goes in local packages."

The key reframe: "app" just means a deployment target. A Cloudflare Worker, React Native app, Next.js dashboard, and CLI are all "apps" — they just deploy to different targets.

Apps are deployment targets — packages are shared bricks
┌─────────────────────────────────────────────────────────┐ apps/ worker-gateway/ worker-auth/ dashboard/ mobile/ │ │ │ │ CF Edge CF Edge CF Pages App Store └─────────────────────────────────────────────────────────┘ all import from ↓ ┌─────────────────────────────────────────────────────────┐ packages/ shared-types/ api-client/ utils/ Pure interfaces fetch() SDK Pure functions └─────────────────────────────────────────────────────────┘
04 / CLOUDFLARE WORKERS

How Workers Fit the Pattern

Each Worker is a small, focused TypeScript file that handles a specific concern and deploys to Cloudflare's global edge. Each has its own wrangler.toml — deploys independently, fails independently.

One Worker per concern — each is its own app
apps/ ├── openclaw-gateway/ ← main API gateway │ ├── src/index.ts │ ├── wrangler.toml │ └── package.json ├── nano-claw/ ← event ingestion Worker │ ├── src/index.ts │ ├── wrangler.toml │ └── package.json └── exo-claw/ ← external boundary Worker ├── src/index.ts ├── wrangler.toml └── package.json

Key point: Wrangler is scoped to a single Worker. Run wrangler deploy from inside apps/nano-claw/ to deploy only that Worker. Other Workers are unaffected.

05 / CLOUDFLARE WORKERS

The V8 Constraint

Workers do not run on Node.js. They run on the V8 JavaScript engine directly — no Node.js APIs.

Node.js vs V8 Isolate runtime
Node.js runtime Cloudflare Worker (V8 Isolate) ───────────────────────── ─────────────────────────────── V8 + Node.js APIs: V8 only: ✓ fs, path, Buffer, process ✗ fs, path, Buffer, process ✓ node-fetch, axios ✗ node-fetch, axios ✓ crypto (Node) ~ crypto.subtle (Web Crypto only) Web APIs available everywhere: fetch(), Request, Response, URL crypto.subtle, TextEncoder ReadableStream, Headers

Common mistake: Adding a utility package that imports lodash or axios which internally uses Node.js APIs. Wrangler will fail at build time. Rule: if a package might be used by a Worker, it must be edge-safe.

06 / CLOUDFLARE WORKERS

Wrangler's Bundling Model

Wrangler uses esbuild to follow every import and produce a single self-contained file. Workspace packages get inlined at deploy time — no runtime npm resolution.

What happens when you run wrangler deploy
apps/worker-gateway/src/index.ts import { AuthRequest } from '@myorg/shared-types' ──► esbuild inlines it import { validateToken } from '@myorg/utils' ──► esbuild inlines it // your Worker logic ──► single output file everything inlined → uploads to CF edge → no runtime npm needed
07 / CLOUDFLARE WORKERS

Edge-Safe Packages

Edge-safe vs Node-only packages
packages/ ├── shared-types/ EDGE-SAFE ✓ Pure TS interfaces, zero runtime deps ├── utils/ EDGE-SAFE ✓ Uses fetch(), URL, crypto.subtle ├── api-client/ EDGE-SAFE ✓ HTTP SDK using fetch(), not axios └── node-scripts/ NODE-ONLY ✗ Uses fs, path — never imported by Workers
08 / SETUP

Workspace Configuration

pnpm-workspace.yamlYAML
packages:
  - 'apps/*'
  - 'packages/*'
apps/nano-claw/package.jsonJSON
{
  "name": "@org/nano-claw",
  "dependencies": {
    "@org/shared-types": "workspace:*",
    "@org/tracking-utils": "workspace:*"
  }
}
09 / SETUP

Turborepo Build Pipeline

Dependency-aware build order
turbo run deploy --filter=nano-claw Turbo reads the dependency graph: nano-claw → depends on → shared-types, tracking-utils Phase 1 (parallel): ✓ shared-types: build ✓ tracking-utils: build ← waits for shared-types Phase 2: ✓ nano-claw: deploy ← wrangler deploy fires here Cache hit? If nothing changed in shared-types, skip its build.
turbo.jsonJSON
{
  "tasks": {
    "build":  { "dependsOn": ["^build"] },  ← ^ means build my deps first
    "deploy": { "dependsOn": ["build"], "cache": false }
  }
}
10 / SETUP

Full File Anatomy

Complete monorepo anatomy
organized-ai/ ├── apps/ │ ├── openclaw-gateway/ wrangler.toml + src/index.ts │ ├── nano-claw/ wrangler.toml + src/index.ts │ ├── exo-claw/ wrangler.toml + src/index.ts │ ├── hermes-worker/ wrangler.toml + src/index.ts │ └── clawdbot-dashboard/ wrangler.toml + src/ ├── packages/ │ ├── shared-types/ pure TS interfaces │ ├── openclaw-sdk/ fetch()-based client │ ├── tracking-utils/ edge-safe CAPI validation │ └── agent-protocol/ ACP message format ├── pnpm-workspace.yaml ├── turbo.json ├── tsconfig.base.json ├── biome.json ├── AGENTS.md └── .kata/kata.yaml
11 / ORCHESTRATION

kata as the Orchestration Layer

Turborepo handles build ordering. kata handles developer workflow ordering. They operate at different levels and complement each other.

kata mode lifecycle for a Worker deploy
kata enter worker-deploy --issue=42 Phase 0: env check + wrangler auth + build packages Phase 1: implement changes (imports from @org/shared-types) Phase 2: turbo run typecheck → all Workers green wrangler dev → test locally Phase 3: turbo run deploy --filter=@org/nano-claw → esbuild inlines packages → CF edge GET /health → 200 ✓ Phase 4: commit + push Stop Hook checks: ✓ tasks_complete ✓ tests_pass ✓ committed ✓ pushed
12 / YOUR STACK

Applied to Organized AI

organized-ai monorepo structure
organized-ai/ ├── apps/ │ ├── openclaw-gateway/ OpenClaw HTTP surface (claws :18789) │ ├── nano-claw/ NanoClaw IS this Worker │ ├── exo-claw/ ExoClaw IS this Worker │ ├── hermes-worker/ Hermes thin receiver │ └── clawdbot-dashboard/ Paperclip UI → CF Pages ├── packages/ │ ├── shared-types/ AgentRequest, TrackingEvent, ClawSession │ ├── openclaw-sdk/ createTailscaleClient('100.82.244.127') │ ├── tracking-utils/ validatePurchaseEvent, hashValue │ └── agent-protocol/ buildTask, buildResult, isACPMessage ├── .kata/ 5 modes: worker-deploy, package-update, │ pages-deploy, codex-qa, agent-handoff └── AGENTS.md rules for CC + Codex operating in this repo

What you've built: A system where Claude Code cannot ship a Worker with type errors in shared packages, cannot declare victory before the health endpoint confirms deployment, and cannot leave uncommitted changes. This is the enforced discipline that turns vibes-based coding into production-grade agentic engineering.