Thybrid (everything)ThybridOSSeparation plan CouplingTarget layoutIntegration contractWhat to cutOrder of workRisks

Separating ThybridOS from Thybrid

ThybridOS becomes its own repo and Worker. Thybrid (the public site and funnel) stays in the current repo and talks to the OS through a small integration contract. This is a plan, not done work. Nothing has been moved.

How tied together they are today

1DeployableOne Worker, one wrangler config, one deploy lock, one 128-binding budget.
246Shared lib filesreached by both sides (284 OS-only, 28 public-only)
103Direct cross-importslib files imported by public code and reachable from the OS
662 / 274Import edgesOS into shared set / public into shared set
~40Public files that import OS codethe real coupling to cut
~100Side-effect edgesfunnel calls Slack, Zoom, WhatsApp, journeys in-process

The heaviest shared files (imports from public / from OS)

FilePublicOSRole
lib/env.ts21116One typed Worker env
app/db.ts1442DynamoDB layer
lib/pg.ts350Neon client
lib/slack.ts518All Slack cards
lib/guards.ts1112Request guards
lib/apply-review.ts89Human review flow
lib/crm.ts710CRM switchboard
lib/tracking.ts77Attribution
flowchart LR
  subgraph NOW["Today: one Worker"]
    P1["Public funnel"] --- S1["Shared lib: env, db, pg, crm, slack"]
    O1["ThybridOS"] --- S1
    P1 -.->|"in-process calls"| O1
  end

Target: two repos, one core package

flowchart TD
  subgraph PUB["thybrid-landing repo (Thybrid)"]
    F["app/apply, book, ve, pages"]
    FA["api: apply, booker, booking, otp, phone, ev, rec"]
    H["hot-worker, media-worker, leads-worker"]
  end
  subgraph CORE["packages/core (shared, versioned)"]
    C["env, db (DynamoDB), pg, crm, guards, token, tracking, types"]
  end
  subgraph OSR["thybridos repo (ThybridOS)"]
    OA["apps/os: admin.thybrid.com"]
    OD["packages/domains: booking, journey, calls, social, ..."]
    OW["workers: sweep, crm-sync, jobs, mcp"]
  end
  PUB --> CORE
  OSR --> CORE
  FA -->|"signed events: application, booking, cancel, reschedule"| OA
thybridos/
  apps/os/              Next.js 16 + OpenNext, admin.thybrid.com
    app/(admin)/...     today's app/admin-v2 pages and server actions
    app/api/{admin,cron,slack,stripe,zoom,whatsapp,meta,call-bot,
             call-transcript,v1,ses,email,marketing,reports}
    middleware.ts       admin host, CSP, fetch-site
  packages/core/        env, db (DynamoDB), pg, crm, guards, token, tracking, types
  packages/domains/     booking-native, journey, automations, calls, call-bot,
                        zoom-native, social, connections, command-centre,
                        personal-notify, privacy, exports, sync, ask, ai,
                        email-campaigns, invariants, weekly-digest, webhooks, recorder
  workers/              sweep, crm-sync, jobs, mcp
  db/migrations/        0001 to 0033 plus pending/
  infra/                Terraform: DynamoDB, S3, Lambda ads-sync
  docs/  tests/

The public repo keeps app/apply, book, ve, b, confirm, reschedule, the marketing pages, api/{apply,booker,booking,otp,phone,ev,rec} and hot-worker, plus a thin client of the core package.

Integration contract between the two

SeamRule
DynamoDB thybrid-applicationsFunnel writes rows (create, draft, booking fields, events). The OS reads and updates status, notes, outcome and payment. Needs a written row schema and the existing optimistic-write rules. Triage status stays in data.triageStatus.
Neon CRMThe OS owns schema and migrations. The funnel gets a narrow database role (contact upsert, appointment create, slots read) instead of the owner role.
KV switchesThe OS writes settings:*, the public site reads. Fail-open behaviour is kept. Either share the namespace or split by prefix.
EventsFunnel posts signed events to POST /api/ingest/{application,booking,cancel,reschedule} on the OS, or uses a Cloudflare Queue. Precedents exist: the sweep Worker's service binding and the call-score queue.
LinksOS to funnel for /j/ and /prepare links through SITE_ORIGIN.
Provider webhooksStripe, Slack, Zoom, WhatsApp, Meta, Resend and SES point only at the OS. Each vendor URL change is a human-gated step.
Shared secrets to alignCron secret, reply token, reschedule secret, transcript link secret, short-link secret, admin session secret. Split the Secrets Manager manifest so the public Worker no longer holds Zoom, Stripe, Slack signing, Resend inbound or Meta DM secrets.

What has to be cut or duplicated

  1. Host and routes. Stop serving admin.thybrid.com from the thybrid Worker. Move the custom domain, the Access policy and the middleware admin branch to the new Worker. Old /admin redirects stay on the public site.
  2. Core package. Move env typing, db, pg, crm, guards, token, tracking, throttle and applicant helpers into one internal workspace package. Copying instead would drift (env.ts alone is imported 137 times).
  3. Side effects. Public booking and apply routes call Slack cards, journey emit, call-bot, personal-notify, WhatsApp and Zoom in-process. Replace with an outbound event. This is the biggest cut.
  4. OS imports in public code. About 40 public files import from admin-v2 (call-confirm, call-hold, team-booking, crm-native, wa-senders, apply-review-deps, booking-native hours and google-connect, privacy erase). Relocate those helpers to the core package first.
  5. Booking engine. booking-native must stay callable from the public site. Either keep it in core with the funnel reading slots (hot-worker already isolates that), or expose slots and book from the OS.
  6. Crons. The sweep Worker's service binding and URL variables re-target the OS Worker. The sweep route itself reads DynamoDB and expires unbooked applications, so it stays in the OS.
  7. Deploy tooling. scripts/deploy.mjs, the worker guard, env-bake allowlist and binding-count guard are single-target. They need a second target.
  8. Satellites. crm-sync, mcp, jobs and cron-worker move with the OS. leads, adset-analyzer, media, hot, the static sites, ads-sync and alarm-slack stay independent. reports-worker (Command Centre) overlaps OS Home and needs a keep-or-retire decision.

Suggested order of work

flowchart LR
  A["1. Relocate the 40 OS helpers used by public code"] --> B["2. Extract packages/core"]
  B --> C["3. Event contract for funnel side effects"]
  C --> D["4. Stand up OS Worker on dev, own KV prefix and secrets"]
  D --> E["5. Move webhooks and crons, vendor by vendor"]
  E --> F["6. Cut admin host over, remove OS branches from public middleware"]
  F --> G["7. Apply row-level security, then multi-tenant"]

Steps 1 to 3 change no deployed behaviour and can ship from the current repo. Steps 4 to 6 each need the owner's word before anything goes live.

Risks