727 lines
30 KiB
Markdown
727 lines
30 KiB
Markdown
# 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*
|