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.
| Concern | Polyrepo | Monorepo |
|---|
| Shared code changes | Publish → bump → PR × N | Change once, all see it |
| Type safety across services | Breaks at runtime if types drift | TypeScript catches at compile time |
| Atomic changes | Multiple PRs, coordination overhead | One PR covers API + client + types |
| Onboarding | Clone N repos, set up N envs | One clone, one install |
| CI/CD complexity | Simple per-repo, complex to coordinate | One pipeline, needs smart filtering |
| Blast radius | Isolated per repo | Bad 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
packages:
- 'apps/*'
- 'packages/*'
{
"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.
{
"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.