For developers
Local setup
Run the website, the panel, the worker, the database and mail on your own machine, with a seeded demo brokerage.
You need
- Node 22 or newer
- pnpm 11 (
corepack enable) - Docker (for PostgreSQL, file storage and Mailpit)
First time
cp .env.example .env # local values; nothing secret leaves your machine
pnpm install
pnpm db:up # PostgreSQL + Mailpit in Docker
pnpm db:migrate
pnpm db:seed # one demo brokerage
pnpm dev # website :3000 · panel :3001 (also at localhost:3000/app) · worker
| Open | Address |
|---|---|
| Website | http://localhost:3000 |
| These docs | http://localhost:3000/docs |
| Panel | http://localhost:3001/app (or http://localhost:3000/app) |
| Caught email (Mailpit) | http://localhost:8025 |
| PostgreSQL | port 5433 |
| S3 storage | port 9100 |
Seeded staff
Every seeded user signs in with their first name and the password Harbor2026.
| Username | Role |
|---|---|
owais | Owner (admin). Sets up two-factor on first sign-in |
dana | Manager |
marcus | Finance (two-factor) |
renee · tomas | Sales |
ash | Ops + dispatch |
priya | Compliance |
The demo book is deliberately small (three customers, three carriers, three loads), and each record carries one of the hard cases: a container past its last free day, a customer over its credit limit, a carrier blocked at the compliance gate, and an advance holding dispatch.
Scripts
| Command | Does |
|---|---|
pnpm dev | Website, panel and background worker in watch mode |
pnpm build | Production builds of both apps |
pnpm test | All unit tests (core rules, database, integrations, app) |
pnpm typecheck · pnpm lint | Must pass before every commit |
pnpm db:up · db:migrate · db:seed | Local database lifecycle |
pnpm db:reset | Drops and recreates the local database (deletes everything); run db:seed after |
pnpm db:studio | Prisma Studio |
pnpm --filter @harbor/app worker | The worker on its own |
pnpm --filter @harbor/app job <name> | Run one background job now |
Environment
.env at the repo root is read by both apps and the worker. The main settings:
| Setting | For |
|---|---|
DATABASE_URL, POSTGRES_* | The database |
AUTH_SECRET | Session signing. Production refuses one under 32 characters |
SITE_DOMAIN, NEXT_PUBLIC_SITE_URL, APP_ORIGIN, APP_BASE_PATH | Where the site and panel live |
S3_ENDPOINT, S3_BUCKET, S3_KEY, S3_SECRET | File storage |
SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS, SMTP_FROM, SALES_INBOX | |
STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET | Billing. Without them billing runs in simulated test mode |
BACKUP_REMOTE, BACKUP_RCLONE_CONF, BACKUP_STAGING | The nightly off-box backup |
JOBS_TIMEZONE | The worker's schedule time zone (default America/Chicago) |
Common problems
| Symptom | Likely cause |
|---|---|
| New loads refused for the demo customer | Earlier test runs pushed it over its credit limit. pnpm db:reset && pnpm db:seed |
| Port 3000 or 3001 in use | Another dev server is still running |
| Two-factor code refused | Codes are single-use. Wait for the next 30-second window |
| No email arrives | Look in Mailpit at http://localhost:8025 |