How to run imbox locally, understand the architecture, and contribute changes that meet our performance, security, and design standards.
Local Development Setup
Prerequisites
- Node.js: Version 22 (LTS)
- Docker: For running the local PostgreSQL 16 development database
1. Clone & Install Dependencies
git clone https://github.com/fr3on/imbox.git && cd imbox
npm ci2. Start Local PostgreSQL Database
docker compose -f docker-compose.dev.yml up -d db # PostgreSQL 16 on port 54329
docker compose -f docker-compose.dev.yml exec db psql -U inbox -d postgres -c "CREATE DATABASE inbox_dev"3. Build & Run the Backend Server
export DATABASE_URL=postgres://inbox@localhost:54329/inbox_dev
export SESSION_SECRET=$(openssl rand -hex 32)
npm run build:node # Compiles Hono backend to node-dist/api.js
npm run start:selfhost # Starts backend and static server on http://localhost:8787Open http://localhost:8787 and complete the initial owner signup at /signup.
4. Run Vite Frontend with Hot Module Replacement (HMR)
In a separate terminal:
npm run devThis serves the React client on http://localhost:5173, automatically proxying /api and /auth requests to the backend server on 8787.
5. Seeding Demo & Test Data
- Screenshots & UI Demo: Run
node scripts/seed-demo.mjsafter signing up to populate invented contacts, threads, calendar events, and Screener senders. - High-Volume Load Testing: Run
node scripts/seed-load.mjsto populate hundreds of thousands of messages for query benchmarking.
Common Development Commands
| Command | Purpose |
|---|---|
npm run check | Runs TypeScript type checking across client, backend, and test suites. |
npm run lint | Runs Biome linter for strict formatting and error checks. |
npm test | Runs unit and route integration tests in Node against PostgreSQL. |
npm run test:pg | Runs full server end-to-end tests against real PostgreSQL (PG_TEST_URL). |
npm run build | Builds the production Vite React frontend into dist/. |
npm run build:node | Bundles the complete backend into node-dist/api.js. |
npm run gen:schema | Generates TypeScript database row types from the latest SQL migrations. |
npm run ext:pack | Packages the browser extension into imbox-extension.zip. |
Codebase Architecture
src/
├── web/ # Frontend: React 19, Vite, Tailwind v4, shadcn/ui
│ ├── components/ # Reusable UI components, dialogs, composer, calendar
│ ├── hooks/ # React Query hooks and client state
│ └── mobile/ # Dedicated mobile layout screens (not squeezed desktop)
├── api/ # Backend: Hono HTTP application
│ ├── routes/ # Route handlers grouped by resource (mail, auth, calendar, ai)
│ ├── db.ts # Database helper queries & parameter binders
│ └── cron.ts # 60-second scheduler for sync, scheduled sends, and jobs
├── node/ # Node.js-specific modules (raw IMAP & SMTP TCP sockets)
└── shared/ # Shared TypeScript types, schemas, and utility functions
server/ # Node.js server entry points, Caddy routing, and PostgreSQL connection pool
migrations/ # Sequentially numbered PostgreSQL migration files (XXXX_*.sql)
deploy/ # Production setup, hardening, Caddyfile, and deployment scripts
apps/
├── extension/ # Chrome/Firefox browser extension (popup, side panel, background worker)
└── desktop/ # Tauri shell for macOS desktop applicationRules for Changes
1. Database & Migrations
- Write plain PostgreSQL queries using
?for parameter placeholders (automatically translated to$1, $2...). - Always use standard PostgreSQL syntax:
ILIKE,ON CONFLICT ... DO NOTHING/DO UPDATE, andLEAST/GREATEST. - Migrations only add tables and columns. Never modify a migration that has already merged.
- After adding a migration, run
npm run gen:schemato updatesrc/api/schema.ts.
2. Route Security & Authorization
- Data Isolation: Every endpoint that accesses a resource by ID must verify that the resource belongs to the authenticated user ID.
- Input Validation: Validate all request parameters and payloads using Zod or
src/api/input.ts. Invalid input must return a clean400 Bad Request, never an uncaught500. - CSRF Protection: State-changing endpoints are automatically guarded by
src/api/security.ts.
3. Performance & Background Work
- Outbound network requests must use
fetchTimeout(src/api/http.ts) and validate destination IPs vianetguard.tsto prevent SSRF against private networks. - Long-running imports or processing must run within the 60-second background job (
runJobincron.ts), never blocking synchronous HTTP responses.
4. Design Aesthetics
- Monochrome Elegance: The visual design is strictly black-and-white. No colored category tags, rainbow badges, or colored buttons.
- No Emojis: Use clean typography or SVGs from
lucide-react. - Components: Adhere to the established design system tokens in
src/web/components/ui.