LetsBeBiz-Redesign/docs/architecture-proposal/claude/09-REPO-STRATEGY.md

727 lines
30 KiB
Markdown
Raw Permalink Normal View History

# LetsBe Biz — Repository Structure
**Date:** February 27, 2026
**Team:** Claude Opus 4.6 Architecture Team
**Document:** 09 of 09
**Status:** Proposal — Competing with independent team
---
## Table of Contents
1. [Decision: Monorepo](#1-decision-monorepo)
2. [Turborepo Configuration](#2-turborepo-configuration)
3. [Directory Tree](#3-directory-tree)
4. [Package Architecture](#4-package-architecture)
5. [Dependency Graph](#5-dependency-graph)
6. [Migration Plan](#6-migration-plan)
7. [Development Workflow](#7-development-workflow)
8. [Monorepo Trade-offs](#8-monorepo-trade-offs)
---
## 1. Decision: Monorepo
### Why Monorepo?
| Factor | Monorepo | Multi-Repo | Winner |
|--------|---------|-----------|--------|
| **Shared types** | Single source of truth; import directly | npm publish on every change; version drift | Monorepo |
| **Atomic changes** | Change type + all consumers in one PR | Coordinate releases across repos | Monorepo |
| **CI/CD** | One pipeline, matrix builds | Per-repo pipelines, dependency triggering | Monorepo |
| **Code discovery** | `grep` across everything | Search multiple repos separately | Monorepo |
| **Prisma schema** | One schema, shared by Hub and types | Duplicate or publish as package | Monorepo |
| **Developer onboarding** | Clone one repo, `npm install`, done | Clone 3-4 repos, configure each | Monorepo |
| **Build caching** | Turborepo caches across packages | Each repo builds independently | Monorepo |
| **Independence** | Packages are more coupled | Fully independent deploy | Multi-Repo |
| **Repo size** | Grows over time | Each repo stays lean | Multi-Repo |
| **CI isolation** | Bad test in one package blocks others | Fully isolated | Multi-Repo |
**Decision:** Monorepo with Turborepo. The shared types, Prisma schema, and tight coupling between Safety Wrapper ↔ Hub ↔ Secrets Proxy make a monorepo the clear winner. The provisioner (Bash) stays as a separate package within the monorepo but could also remain as a standalone repo if the team prefers — it has no TypeScript dependencies.
### What Stays Outside the Monorepo
| Component | Reason |
|-----------|--------|
| **OpenClaw** | Upstream dependency. Pulled as Docker image. Not forked. |
| **Tool Docker stacks** | Compose files and nginx configs live in the provisioner package. |
| **Mobile app** | React Native/Expo has different build tooling. Lives in `packages/mobile` but uses its own `metro.config.js`. |
---
## 2. Turborepo Configuration
### `turbo.json`
```json
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["**/.env.*local"],
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**"]
},
"typecheck": {
"dependsOn": ["^build"]
},
"lint": {},
"test": {
"dependsOn": ["^build"],
"env": ["DATABASE_URL", "NODE_ENV"]
},
"test:p0": {
"dependsOn": ["^build"],
"cache": false
},
"test:p1": {
"dependsOn": ["^build"],
"cache": false
},
"test:integration": {
"dependsOn": ["build"],
"cache": false
},
"test:benchmark": {
"dependsOn": ["build"],
"cache": false
},
"dev": {
"cache": false,
"persistent": true
},
"db:push": {
"cache": false
},
"db:generate": {
"outputs": ["node_modules/.prisma/**"]
}
}
}
```
### Root `package.json`
```json
{
"name": "letsbe-biz",
"private": true,
"workspaces": [
"packages/*"
],
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev --parallel",
"test": "turbo run test",
"test:p0": "turbo run test:p0",
"test:integration": "turbo run test:integration",
"lint": "turbo run lint",
"typecheck": "turbo run typecheck",
"format": "prettier --write \"packages/*/src/**/*.{ts,tsx}\"",
"clean": "turbo run clean && rm -rf node_modules"
},
"devDependencies": {
"turbo": "^2.3.0",
"prettier": "^3.4.0",
"typescript": "^5.7.0"
},
"engines": {
"node": ">=22.0.0"
}
}
```
---
## 3. Directory Tree
```
letsbe-biz/
├── .gitea/
│ └── workflows/
│ ├── ci.yml # Monorepo CI (lint, typecheck, test)
│ ├── safety-wrapper.yml # SW-specific pipeline
│ ├── secrets-proxy.yml # SP-specific pipeline
│ ├── hub.yml # Hub pipeline
│ ├── integration.yml # Integration test pipeline
│ ├── deploy-staging.yml # Auto-deploy to staging
│ ├── deploy-hub.yml # Production Hub deploy
│ └── tenant-update.yml # Tenant server rollout
├── packages/
│ ├── safety-wrapper/ # Safety Wrapper (localhost:8200)
│ │ ├── src/
│ │ │ ├── index.ts # Entry point: HTTP server startup
│ │ │ ├── server.ts # Express/Fastify HTTP server
│ │ │ ├── config.ts # Configuration loading
│ │ │ ├── classification/
│ │ │ │ ├── engine.ts # Command classification engine
│ │ │ │ ├── shell-classifier.ts # Shell command allowlist + classification
│ │ │ │ ├── docker-classifier.ts # Docker subcommand classification
│ │ │ │ └── rules.ts # Classification rule definitions
│ │ │ ├── autonomy/
│ │ │ │ ├── resolver.ts # Autonomy level resolution
│ │ │ │ ├── external-comms.ts # External Communications Gate
│ │ │ │ └── approval-queue.ts # Local approval queue (SQLite)
│ │ │ ├── executors/
│ │ │ │ ├── shell.ts # Shell command executor (execFile)
│ │ │ │ ├── docker.ts # Docker command executor
│ │ │ │ ├── file.ts # File read/write executor
│ │ │ │ └── env.ts # Env read/update executor
│ │ │ ├── secrets/
│ │ │ │ ├── registry.ts # Encrypted SQLite secrets vault
│ │ │ │ ├── injection.ts # SECRET_REF resolution
│ │ │ │ └── api.ts # Secrets side-channel API
│ │ │ ├── hub/
│ │ │ │ ├── client.ts # Hub communication (register, heartbeat, config)
│ │ │ │ └── config-sync.ts # Config versioning and delta sync
│ │ │ ├── metering/
│ │ │ │ ├── token-tracker.ts # Per-agent, per-model token tracking
│ │ │ │ └── bucket.ts # Hourly bucket aggregation
│ │ │ ├── audit/
│ │ │ │ └── logger.ts # Append-only audit log
│ │ │ └── db/
│ │ │ ├── schema.sql # SQLite schema (secrets, approvals, audit, usage, state)
│ │ │ └── migrations/ # SQLite migration files
│ │ ├── test/
│ │ │ ├── p0/
│ │ │ │ ├── classification.test.ts # 100+ classification tests
│ │ │ │ └── autonomy.test.ts # Level × tier matrix tests
│ │ │ ├── p1/
│ │ │ │ ├── shell-executor.test.ts
│ │ │ │ ├── docker-executor.test.ts
│ │ │ │ └── hub-client.test.ts
│ │ │ └── integration/
│ │ │ └── openclaw-routing.test.ts
│ │ ├── Dockerfile
│ │ ├── package.json
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ │
│ ├── secrets-proxy/ # Secrets Proxy (localhost:8100)
│ │ ├── src/
│ │ │ ├── index.ts # Entry point
│ │ │ ├── proxy.ts # HTTP proxy server (transparent)
│ │ │ ├── redaction/
│ │ │ │ ├── pipeline.ts # 4-layer pipeline orchestrator
│ │ │ │ ├── layer1-aho-corasick.ts # Registry-based exact match
│ │ │ │ ├── layer2-regex.ts # Pattern safety net
│ │ │ │ ├── layer3-entropy.ts # Shannon entropy filter
│ │ │ │ └── layer4-json-keys.ts # Sensitive key name detection
│ │ │ └── config.ts
│ │ ├── test/
│ │ │ ├── p0/
│ │ │ │ ├── redaction.test.ts # 50+ redaction tests (TDD)
│ │ │ │ ├── false-positives.test.ts # False positive prevention
│ │ │ │ └── performance.test.ts # <10ms latency benchmark
│ │ │ └── adversarial/
│ │ │ └── bypass-attempts.test.ts # Adversarial attack tests
│ │ ├── Dockerfile
│ │ ├── package.json
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ │
│ ├── hub/ # Hub (Next.js — existing codebase, migrated)
│ │ ├── src/
│ │ │ ├── app/ # Next.js App Router (existing structure)
│ │ │ │ ├── admin/ # Staff admin dashboard (existing)
│ │ │ │ ├── api/
│ │ │ │ │ ├── auth/ # Authentication (existing)
│ │ │ │ │ ├── v1/
│ │ │ │ │ │ ├── admin/ # Admin API (existing)
│ │ │ │ │ │ ├── tenant/ # NEW: Safety Wrapper protocol
│ │ │ │ │ │ │ ├── register/
│ │ │ │ │ │ │ ├── heartbeat/
│ │ │ │ │ │ │ ├── config/
│ │ │ │ │ │ │ ├── usage/
│ │ │ │ │ │ │ ├── approval-request/
│ │ │ │ │ │ │ └── approval-response/
│ │ │ │ │ │ ├── customer/ # NEW: Customer-facing API
│ │ │ │ │ │ │ ├── dashboard/
│ │ │ │ │ │ │ ├── agents/
│ │ │ │ │ │ │ ├── usage/
│ │ │ │ │ │ │ ├── approvals/
│ │ │ │ │ │ │ ├── billing/
│ │ │ │ │ │ │ └── tools/
│ │ │ │ │ │ ├── orchestrator/ # DEPRECATED: keep for backward compat, redirect
│ │ │ │ │ │ ├── public/ # Public API (existing)
│ │ │ │ │ │ └── webhooks/ # Stripe webhooks (existing)
│ │ │ │ │ └── cron/ # Cron endpoints (existing)
│ │ │ │ └── login/ # Login page (existing)
│ │ │ ├── lib/
│ │ │ │ ├── services/ # Business logic (existing + new)
│ │ │ │ │ ├── automation-worker.ts # Existing
│ │ │ │ │ ├── billing-service.ts # NEW: Token billing, Stripe Meters
│ │ │ │ │ ├── chat-relay-service.ts # NEW: App→Hub→SW→OpenClaw
│ │ │ │ │ ├── config-generator.ts # Existing (updated)
│ │ │ │ │ ├── push-notification.ts # NEW: Expo Push service
│ │ │ │ │ ├── tenant-protocol.ts # NEW: SW registration/heartbeat
│ │ │ │ │ └── ... # Other existing services
│ │ │ │ └── ...
│ │ │ ├── hooks/ # React Query hooks (existing)
│ │ │ └── components/ # UI components (existing)
│ │ ├── prisma/
│ │ │ ├── schema.prisma # Shared Prisma schema (existing + new models)
│ │ │ ├── migrations/ # Prisma migrations
│ │ │ └── seed.ts # Database seeding
│ │ ├── test/
│ │ │ ├── unit/ # Existing unit tests (10 files)
│ │ │ ├── api/ # NEW: API endpoint tests
│ │ │ └── integration/ # NEW: Hub↔SW protocol tests
│ │ ├── Dockerfile
│ │ ├── package.json
│ │ ├── next.config.ts
│ │ └── tsconfig.json
│ │
│ ├── website/ # Website (letsbe.biz — separate Next.js app)
│ │ ├── src/
│ │ │ ├── app/
│ │ │ │ ├── page.tsx # Landing page
│ │ │ │ ├── onboarding/ # AI-powered onboarding flow
│ │ │ │ │ ├── business/ # Step 1: Business description
│ │ │ │ │ ├── tools/ # Step 2: Tool recommendation
│ │ │ │ │ ├── customize/ # Step 3: Customization
│ │ │ │ │ ├── server/ # Step 4: Server selection
│ │ │ │ │ ├── domain/ # Step 5: Domain setup
│ │ │ │ │ ├── agents/ # Step 6: Agent config (optional)
│ │ │ │ │ ├── payment/ # Step 7: Stripe checkout
│ │ │ │ │ └── status/ # Step 8: Provisioning status
│ │ │ │ ├── demo/ # Interactive demo page
│ │ │ │ └── pricing/ # Pricing page
│ │ │ └── lib/
│ │ │ ├── ai-classifier.ts # Gemini Flash business classifier
│ │ │ └── resource-calc.ts # Resource requirement calculator
│ │ ├── Dockerfile
│ │ ├── package.json
│ │ └── tsconfig.json
│ │
│ ├── mobile/ # Mobile App (Expo Bare Workflow)
│ │ ├── src/
│ │ │ ├── screens/
│ │ │ │ ├── LoginScreen.tsx
│ │ │ │ ├── ChatScreen.tsx
│ │ │ │ ├── DashboardScreen.tsx
│ │ │ │ ├── ApprovalsScreen.tsx
│ │ │ │ ├── UsageScreen.tsx
│ │ │ │ ├── SettingsScreen.tsx
│ │ │ │ └── SecretsScreen.tsx
│ │ │ ├── components/
│ │ │ ├── hooks/
│ │ │ ├── stores/ # Zustand stores
│ │ │ ├── services/ # API client, push notifications
│ │ │ └── navigation/ # React Navigation
│ │ ├── app.json
│ │ ├── eas.json # EAS Build + Update config
│ │ ├── metro.config.js
│ │ ├── package.json
│ │ └── tsconfig.json
│ │
│ ├── shared-types/ # Shared TypeScript types
│ │ ├── src/
│ │ │ ├── classification.ts # Command classification types
│ │ │ ├── autonomy.ts # Autonomy level types
│ │ │ ├── secrets.ts # Secrets registry types
│ │ │ ├── protocol.ts # Hub ↔ SW protocol types
│ │ │ ├── billing.ts # Token metering types
│ │ │ ├── agents.ts # Agent configuration types
│ │ │ └── index.ts # Barrel export
│ │ ├── package.json
│ │ └── tsconfig.json
│ │
│ ├── shared-prisma/ # Shared Prisma client (generated)
│ │ ├── prisma/
│ │ │ └── schema.prisma # → symlink to packages/hub/prisma/schema.prisma
│ │ ├── package.json
│ │ └── tsconfig.json
│ │
│ └── provisioner/ # Provisioner (Bash — migrated from letsbe-ansible-runner)
│ ├── provision.sh # Main entry point
│ ├── steps/
│ │ ├── step-01-system-update.sh
│ │ ├── step-02-docker-install.sh
│ │ ├── step-03-create-user.sh
│ │ ├── step-04-generate-secrets.sh
│ │ ├── step-05-deploy-stacks.sh
│ │ ├── step-06-nginx-configs.sh
│ │ ├── step-07-ssl-certs.sh
│ │ ├── step-08-backup-setup.sh
│ │ ├── step-09-firewall.sh
│ │ └── step-10-deploy-ai.sh # REWRITTEN: OpenClaw + Safety Wrapper
│ ├── stacks/ # Docker Compose files for 28+ tools
│ │ ├── chatwoot/
│ │ │ └── docker-compose.yml
│ │ ├── nextcloud/
│ │ │ └── docker-compose.yml
│ │ ├── letsbe/ # NEW: LetsBe AI stack
│ │ │ └── docker-compose.yml # OpenClaw + Safety Wrapper + Secrets Proxy
│ │ └── ...
│ ├── nginx/ # nginx configs for 33+ tools
│ ├── templates/ # Config templates
│ │ ├── openclaw-config.json5.tmpl
│ │ ├── safety-wrapper.json.tmpl
│ │ ├── tool-registry.json.tmpl
│ │ └── agent-templates/ # Per-business-type agent configs
│ ├── references/ # Tool cheat sheets (deployed to tenant)
│ │ ├── portainer.md
│ │ ├── nextcloud.md
│ │ ├── chatwoot.md
│ │ ├── ghost.md
│ │ ├── calcom.md
│ │ ├── stalwart.md
│ │ └── ...
│ ├── skills/ # OpenClaw skills (deployed to tenant)
│ │ └── letsbe-tools/
│ │ └── SKILL.md # Master tool skill
│ ├── agents/ # Default agent configs (deployed to tenant)
│ │ ├── dispatcher/
│ │ │ └── SOUL.md
│ │ ├── it-admin/
│ │ │ └── SOUL.md
│ │ ├── marketing/
│ │ │ └── SOUL.md
│ │ ├── secretary/
│ │ │ └── SOUL.md
│ │ └── sales/
│ │ └── SOUL.md
│ ├── test/
│ │ ├── step-10.bats # bats-core tests for step 10
│ │ ├── cleanup.bats # n8n cleanup verification
│ │ └── full-run.bats # Full provisioner integration test
│ ├── Dockerfile
│ └── package.json # Minimal — just for monorepo workspace inclusion
├── test/ # Cross-package integration tests
│ ├── docker-compose.integration.yml # Full stack for integration tests
│ ├── fixtures/
│ │ ├── openclaw-config.json5
│ │ ├── safety-wrapper-config.json
│ │ ├── tool-registry.json
│ │ └── test-secrets.json
│ └── e2e/
│ ├── signup-to-chat.test.ts
│ ├── approval-flow.test.ts
│ └── secrets-never-leak.test.ts
├── docs/ # Documentation (existing)
│ ├── technical/
│ ├── strategy/
│ ├── legal/
│ └── architecture-proposal/
│ └── claude/ # This proposal
├── turbo.json
├── package.json # Root workspace config
├── tsconfig.base.json # Shared TypeScript config
├── .gitignore
├── .eslintrc.js # Shared ESLint config
├── .prettierrc
└── README.md
```
---
## 4. Package Architecture
### Package Responsibilities
| Package | Language | Purpose | Depends On | Deployed As |
|---------|----------|---------|-----------|-------------|
| `safety-wrapper` | TypeScript | Command gating, tool execution, Hub comm, audit | `shared-types` | Docker container on tenant VPS |
| `secrets-proxy` | TypeScript | LLM traffic redaction (4-layer pipeline) | `shared-types` | Docker container on tenant VPS |
| `hub` | TypeScript (Next.js) | Admin dashboard, customer portal, billing, tenant protocol | `shared-types`, `shared-prisma` | Docker container on central server |
| `website` | TypeScript (Next.js) | Marketing site, onboarding flow | — | Docker container on central server |
| `mobile` | TypeScript (Expo) | Customer mobile app | `shared-types` | iOS/Android app (EAS Build) |
| `shared-types` | TypeScript | Type definitions shared across packages | — | npm workspace dependency |
| `shared-prisma` | TypeScript | Generated Prisma client | — | npm workspace dependency |
| `provisioner` | Bash | VPS provisioning scripts, tool stacks | — | Docker container (on-demand) |
### Package Size Estimates
| Package | Estimated LOC | Files | Build Output |
|---------|--------------|-------|-------------|
| `safety-wrapper` | ~3,000-4,000 | ~30 | ~200KB JS |
| `secrets-proxy` | ~1,500-2,000 | ~15 | ~100KB JS |
| `hub` | ~15,000+ (existing) + ~3,000 new | ~250+ | Next.js standalone |
| `website` | ~2,000-3,000 | ~20 | Next.js standalone |
| `mobile` | ~4,000-5,000 | ~40 | Expo bundle |
| `shared-types` | ~500-800 | ~10 | ~50KB JS |
| `provisioner` | ~5,000 (existing + new) | ~50+ | Bash scripts |
---
## 5. Dependency Graph
```
┌──────────────┐
│ shared-types │
└──────┬───────┘
┌────────────┼────────────┬────────────┐
│ │ │ │
┌────────▼──────┐ ┌──▼────────┐ ┌─▼──────┐ ┌──▼──────┐
│safety-wrapper │ │secrets- │ │ hub │ │ mobile │
│ │ │proxy │ │ │ │ │
└───────────────┘ └───────────┘ └────┬───┘ └─────────┘
┌──────▼──────┐
│shared-prisma│
└─────────────┘
┌───────────┐ ┌───────────┐
│ website │ │provisioner│
│(no deps) │ │(Bash, no │
│ │ │ TS deps) │
└───────────┘ └───────────┘
```
**Key constraints:**
- `shared-types` has ZERO dependencies. It's pure TypeScript type definitions.
- `shared-prisma` depends only on Prisma and the schema file.
- `safety-wrapper` and `secrets-proxy` never import from `hub` (no circular deps).
- `hub` never imports from `safety-wrapper` or `secrets-proxy` (communication via HTTP protocol).
- `website` is fully independent — no shared package dependencies.
- `provisioner` is Bash — no TypeScript dependencies at all.
---
## 6. Migration Plan
### Current State (5 Separate Repos)
```
letsbe-hub → packages/hub (TypeScript, Next.js)
letsbe-ansible-runner → packages/provisioner (Bash)
letsbe-orchestrator → DEPRECATED (capabilities → safety-wrapper)
letsbe-sysadmin-agent → DEPRECATED (capabilities → safety-wrapper)
letsbe-mcp-browser → DEPRECATED (replaced by OpenClaw native browser)
```
### Migration Steps
#### Step 1: Create Monorepo (Week 1, Day 1-2)
```bash
# Create new repo
mkdir letsbe-biz && cd letsbe-biz
git init
npm init -y
# Install Turborepo
npm install turbo --save-dev
# Create workspace structure
mkdir -p packages/{safety-wrapper,secrets-proxy,hub,website,mobile,shared-types,shared-prisma,provisioner}
# Create turbo.json (from Section 2)
# Create root package.json (from Section 2)
# Create tsconfig.base.json
```
#### Step 2: Migrate Hub (Week 1, Day 1)
```bash
# Copy Hub source (preserve git history via subtree or fresh copy)
cp -r ../letsbe-hub/src packages/hub/src
cp -r ../letsbe-hub/prisma packages/hub/prisma
cp ../letsbe-hub/package.json packages/hub/
cp ../letsbe-hub/next.config.ts packages/hub/
cp ../letsbe-hub/tsconfig.json packages/hub/
cp ../letsbe-hub/Dockerfile packages/hub/
# Update Hub package.json:
# - name: "@letsbe/hub"
# - Add workspace dependency on shared-types, shared-prisma
# Verify Hub builds
cd packages/hub && npm install && npm run build
```
#### Step 3: Migrate Provisioner (Week 1, Day 1)
```bash
# Copy provisioner scripts
cp -r ../letsbe-ansible-runner/* packages/provisioner/
# Add minimal package.json for workspace inclusion
echo '{"name":"@letsbe/provisioner","private":true}' > packages/provisioner/package.json
```
#### Step 4: Create New Packages (Week 1, Day 2)
```bash
# shared-types — create from scratch
cd packages/shared-types
npm init -y --scope=@letsbe
# Add type definitions
# safety-wrapper — create from scratch
cd packages/safety-wrapper
npm init -y --scope=@letsbe
# Scaffold Express/Fastify server
# secrets-proxy — create from scratch
cd packages/secrets-proxy
npm init -y --scope=@letsbe
# Scaffold HTTP proxy
```
#### Step 5: Verify Everything Works (Week 1, Day 2)
```bash
# From repo root:
npm install # Install all workspace dependencies
turbo run build # Build all packages
turbo run typecheck # Type check all packages
turbo run test # Run all tests (Hub's existing 10 tests)
turbo run lint # Lint all packages
```
#### Step 6: Archive Old Repos (Week 2)
Once the monorepo is confirmed working and the team has switched:
1. Mark `letsbe-orchestrator` as archived (deprecated)
2. Mark `letsbe-sysadmin-agent` as archived (deprecated)
3. Mark `letsbe-mcp-browser` as archived (deprecated)
4. Keep `letsbe-hub` and `letsbe-ansible-runner` read-only for reference
5. Update Gitea CI to point to new monorepo
### Git History Preservation
**Option A (Recommended): Fresh start with reference.**
- New monorepo gets a clean git history.
- Old repos remain accessible (read-only archive) for historical reference.
- This is cleaner and avoids complex git subtree merges.
**Option B: Preserve history via git subtree.**
- Use `git subtree add` to bring Hub and provisioner history into the monorepo.
- More complex but preserves `git blame` lineage.
**Recommendation:** Option A. The codebase is being substantially restructured. Historical blame on the old code is less valuable than a clean starting point. The old repos stay available for reference.
---
## 7. Development Workflow
### Daily Development
```bash
# Start all dev servers (Hub + Safety Wrapper + Secrets Proxy)
turbo run dev --parallel
# Run tests for a specific package
turbo run test --filter=safety-wrapper
# Run P0 tests only
turbo run test:p0
# Build a specific package
turbo run build --filter=secrets-proxy
# Type check everything
turbo run typecheck
# Lint everything
turbo run lint
```
### Adding a Shared Type
```bash
# 1. Add type to packages/shared-types/src/classification.ts
# 2. Export from index.ts
# 3. Import in consuming package:
# import { CommandTier } from '@letsbe/shared-types';
# 4. Turbo automatically rebuilds shared-types before dependent packages
```
### Adding a New Package
```bash
# 1. Create directory
mkdir packages/new-package
# 2. Initialize
cd packages/new-package
npm init -y --scope=@letsbe
# 3. Add to root workspaces (already covered by packages/* glob)
# 4. Add to turbo.json pipeline if needed
# 5. Add Dockerfile if it's a deployed service
```
### Docker Development
```yaml
# docker-compose.dev.yml (root level, for local development)
services:
postgres:
image: postgres:16-alpine
ports: ['5432:5432']
environment:
POSTGRES_DB: hub_dev
POSTGRES_USER: hub
POSTGRES_PASSWORD: devpass
hub:
build:
context: .
dockerfile: packages/hub/Dockerfile
ports: ['3000:3000']
environment:
DATABASE_URL: postgresql://hub:devpass@postgres:5432/hub_dev
depends_on: [postgres]
safety-wrapper:
build:
context: .
dockerfile: packages/safety-wrapper/Dockerfile
ports: ['8200:8200']
secrets-proxy:
build:
context: .
dockerfile: packages/secrets-proxy/Dockerfile
ports: ['8100:8100']
```
---
## 8. Monorepo Trade-offs
### Advantages Realized
| Advantage | Concrete Benefit |
|-----------|-----------------|
| **Atomic type changes** | Change `CommandTier` enum in `shared-types` → all consumers updated in same PR |
| **Turborepo caching** | Rebuild only changed packages; CI runs ~60% faster after first run |
| **Shared tooling** | One ESLint config, one Prettier config, one TypeScript base config |
| **Cross-package refactoring** | Rename a protocol field → update Safety Wrapper + Hub in one commit |
| **Single dependency tree** | No version conflicts between packages; hoisted node_modules |
| **Simplified onboarding** | Clone one repo → `npm install``turbo run dev` → everything running |
### Disadvantages Accepted
| Disadvantage | Mitigation |
|-------------|------------|
| **Larger repo size** | Turborepo's `--filter` flag runs only affected packages |
| **Bash in TypeScript monorepo** | Provisioner is loosely coupled — workspace inclusion is just for organization |
| **Mobile build complexity** | Expo has its own build system (EAS); it coexists but doesn't use Turbo for builds |
| **CI runs all checks** | Path-based triggers (see pipeline YAML) skip unrelated packages |
| **Single repo = single SPOF** | Gitea backup strategy; consider GitHub mirror for disaster recovery |
### When to Reconsider
The monorepo should be split if:
- The team grows beyond 8-10 engineers and package ownership boundaries become clear
- Mobile app development cadence diverges significantly from backend
- A package needs a fundamentally different build system or language (e.g., Rust Safety Wrapper rewrite)
- CI times exceed 20 minutes even with caching
None of these are likely before reaching 100 customers.
---
*End of Document — 09 Repository Structure*