auth-backend vs boo-backend: Should they merge?

Table of Contents
auth-backend vs boo-backend: Should they merge?

Architecture · Codebase Analysis

Merge auth-backend into backend

The two services are already one system split across two processes, with a bidirectional runtime dependency, a shared secret, and no real security boundary. The split costs latency on every request and has already caused production bugs.

Shared by Boo·October 2026·boo-backend + authentication-backend

2.2K
auth-backend lines
12 source files
42K
boo-backend lines
90+ source files
40
cross-service calls
every auth'd request
2-3d
estimated merge effort
mechanical, low risk

What auth-backend owns

Auth-backend is a thin Hono service wrapping better-auth. It manages 7 tables in a separate boo_auth Postgres database: users, sessions, accounts (OAuth/OTP), verifications, organizations, members, and invitations.

It handles login flows (email OTP, Google OAuth), session management, org CRUD, membership, invitations, and a permissions middleware. It also exposes internal routes consumed by boo-backend for user/org lookups and mutations.

Notably, auth-backend does not own email sending. It calls back to boo-backend's internal email endpoint to send OTPs and invitation emails, creating a circular dependency.

How they're coupled

Request flow: every authenticated API call

Browser → boo-backend → auth-backend → boo-backend → browser. Session validation is an HTTP call with no caching.
Coupling pointImpact
Session validation - every auth'd requestNetwork hop, no caching, 5-20ms per call
Auth proxy - all frontend /api/auth/* callsDouble hop: browser → backend → auth → backend → browser
Bidirectional dependencyAuth needs backend for email; backend needs auth for everything
Shared INTERNAL_SECRETMust match across both, negates security boundary
Duplicated typesPermission, OrgRole, actionPolicies copy-pasted in both repos
Separate databasesNo cross-DB FKs; org IDs stored as plain text in 15+ tables
Neither service can function without the other. An auth-backend outage makes the entire product 401/503. A boo-backend outage breaks auth-backend's email sending (no signups, no invitations).

The security boundary is already broken

The split looks like security isolation, but boo-backend already has full access to auth-backend through the shared INTERNAL_SECRET and 40 internal API call sites. It forwards raw session cookies via the auth proxy. A compromised boo-backend can call any internal route on auth-backend, including user and org mutations.

  • INTERNAL_SECRET shared across both services, no mTLS
  • auth-proxy.ts forwards all /api/auth/* calls with raw cookies
  • Internal routes accept boo-backend's x-internal-actor: automation header for full CRUD
  • No audit boundary, no separate access logs, no compliance framework requires it

Real cost of the split

Performance

  • Every authenticated request pays a network hop (5-20ms within Railway)
  • No session caching on the hot path
  • A dashboard page load with 10 API calls = 10 extra round trips

Operations

  • Two repos, two deploys, two migration sets
  • Deployment ordering matters (contract changes)
  • 12+ env vars to coordinate
  • 6 test files mock authServiceCall

Already caused a production bug

After the database was split, boo-backend kept reading org slugs from a stale local copy of the organizations table. Branded-domain provisioning silently broke for 6 orgs over 3 weeks before anyone noticed.

Arguments for keeping the split (and why they don't hold)

ArgumentCounter
Security isolationINTERNAL_SECRET is shared. boo-backend has full internal API access. The boundary is porous.
Independent scalingCurrent scale doesn't need it. The network hop costs more than any scaling gain.
Blast radius containmentBidirectional dependency means either outage takes down both.
Compliance separationNo SOC 2 / HIPAA requirement in the codebase. Shared secret and implicit trust wouldn't satisfy one anyway.

Merge plan

1

Move better-auth setup into boo-backend as src/lib/auth.ts. It becomes the session provider, called directly instead of over HTTP.

~1 hour
2

Unify the database. Move auth-backend's 7 tables into the boo_api database. Real foreign keys between auth and business tables become possible.

~2 hours + migration script
3

Replace resolveSession() HTTP call with a direct auth.api.getSession() function call. Eliminates the per-request network hop.

biggest latency win
4

Replace all 40 authServiceCall() sites with direct function calls or Drizzle queries against the now-local schema.

~4 hours, mechanical
5

Delete auth-proxy.ts and mount better-auth's handler directly at /api/auth/*. Frontend auth calls go from double-hop to direct.

~30 minutes
6

Merge permission systems into one policies.ts. Delete duplicated types, policies, and the getActionPermissions() copy.

~1 hour
7

Clean up env vars. Delete INTERNAL_SECRET, AUTH_SERVICE_URL, INTERNAL_API_URL. Absorb BETTER_AUTH_SECRET and GOOGLE_CLIENT_* into boo-backend.

~15 minutes

Analysis based on full source review of both codebases as of October 2026. auth-backend: 2,204 lines across 12 TypeScript files. boo-backend: ~42,000 lines across 90+ files. Cross-service calls counted by grep of authServiceCall, AUTH_SERVICE_URL, and fetch patterns in production source (tests excluded from the count).

Chart, full screen