AI-powered support cockpit for n8n companies — classifies emails, routes tickets, and learns per-client behavior.
| Layer | Tech |
|---|---|
| Monorepo | Turborepo + Bun |
| WebApp | Vite + React 19 |
| Landing | Next.js 15 |
| API | Bun + Hono |
| Database | Supabase (Postgres + Auth) |
| AI | Claude API (prod) / Ollama (local) |
| Gmail API | |
| Deploy | Vercel |
| Language | TypeScript (strict) |
kairo/
├── apps/
│ ├── webapp/ # Vite + React — support dashboard
│ ├── landing/ # Next.js — marketing site
│ ├── api/ # Bun + Hono — backend
│ └── mobile/ # Expo — mobile app
├── packages/
│ ├── env/ # centralized env validation (@t3-oss/env-core)
│ ├── types/ # shared TypeScript interfaces
│ ├── i18n/ # shared translations (EN/ES)
│ ├── ui/ # shared ShadCN components
│ └── intelligence/ # modular LLM provider (Ollama / Anthropic)
│ └── prompts/ # versioned LLM prompts (markdown + YAML frontmatter)
├── supabase/
│ └── migrations/ # shared DB migrations (Postgres via Supabase)
└── kairo-internal/
└── architecture/ # 17 Architecture Decision Records
bun install
bun run dev- WebApp → http://localhost:5173
- Landing → http://localhost:3000
- API → http://localhost:3001
bun run build # build all apps
bun run lint # lint all packages
bun run clean # clean build artifacts
bun test # run testsKairo uses @kairo/env — a centralized, type-safe env layer built on @t3-oss/env-core. Every variable is validated with Zod at startup. Raw process.env and import.meta.env access is not allowed anywhere in the codebase.
All variables — shared and app-specific — live in one file at the monorepo root:
cp .env.example .env.local # fill in real values, never commitEach app reads from that single file:
| App | How it reads root .env.local |
|---|---|
apps/api |
Bun reads .env.local from the monorepo root natively |
apps/webapp |
Vite envDir: "../../" points it at the monorepo root |
apps/landing |
next.config.ts calls loadEnvConfig("../../") before webpack compiles, so NEXT_PUBLIC_* vars get inlined into the client bundle |
The .env.example is grouped into three sections — SHARED, LANDING, and WEBAPP — with comments explaining each variable.
| Package / App | File | Variables it owns |
|---|---|---|
packages/env |
index.ts |
SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, INTELLIGENCE_PROVIDER, ANTHROPIC_API_KEY, GOOGLE_CLIENT_SECRET |
apps/api |
src/env.ts |
Re-exports @kairo/env + PORT |
apps/webapp |
src/env.ts |
VITE_SUPABASE_URL, VITE_SUPABASE_ANON_KEY, VITE_LANDING_URL |
apps/landing |
env.ts |
All NEXT_PUBLIC_* vars + GOOGLE_CLIENT_SECRET |
Never access process.env or import.meta.env directly — always import from the nearest env.ts:
// ✅ correct
import { env } from "@/env";
const url = env.NEXT_PUBLIC_SUPABASE_URL;
// ❌ wrong — no validation, no types
const url = process.env.NEXT_PUBLIC_SUPABASE_URL;