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

727 lines
30 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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*