Reference

Development

Local development workflow, code conventions, testing suites, and architecture overview.

imbox docs

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 ci

2. 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:8787

Open 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 dev

This 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.mjs after signing up to populate invented contacts, threads, calendar events, and Screener senders.
  • High-Volume Load Testing: Run node scripts/seed-load.mjs to populate hundreds of thousands of messages for query benchmarking.

Common Development Commands

CommandPurpose
npm run checkRuns TypeScript type checking across client, backend, and test suites.
npm run lintRuns Biome linter for strict formatting and error checks.
npm testRuns unit and route integration tests in Node against PostgreSQL.
npm run test:pgRuns full server end-to-end tests against real PostgreSQL (PG_TEST_URL).
npm run buildBuilds the production Vite React frontend into dist/.
npm run build:nodeBundles the complete backend into node-dist/api.js.
npm run gen:schemaGenerates TypeScript database row types from the latest SQL migrations.
npm run ext:packPackages 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 application

Rules 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, and LEAST / GREATEST.
  • Migrations only add tables and columns. Never modify a migration that has already merged.
  • After adding a migration, run npm run gen:schema to update src/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 clean 400 Bad Request, never an uncaught 500.
  • 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 via netguard.ts to prevent SSRF against private networks.
  • Long-running imports or processing must run within the 60-second background job (runJob in cron.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.

Next

  • Database: how queries and migrations are kept plain.
  • Design: the rules for how screens look and read.
  • Releasing: cut a version.