Thanks for your interest in contributing! This guide covers everything you need to get started.
- Bun 1.3.10 (pinned in
.bun-versionandpackage.json) - Node.js 22 or newer (see
.nvmrcandpackage.json) - Docker, when using the Compose stack or testing Docker-based deployments
git clone https://github.com/oblien/openship.git
cd openship
bun install --frozen-lockfile
cp apps/api/.env.example apps/api/.env
cp apps/dashboard/.env.example apps/dashboard/.env
bun dev:localThis starts the API and dashboard used for local development:
| Service | URL |
|---|---|
| Dashboard | http://localhost:3001 |
| API | http://localhost:4000 |
Use the root scripts to run a different workspace or the full development graph:
bun dev:api # API and its workspace dependencies
bun dev:dashboard # Dashboard only
bun dev:web # Marketing site (http://localhost:3009)
bun dev:desktop # Electron desktop app
bun dev:email # Email server and client
bun dev # All workspace dev tasksThe root .env.example is for the Docker Compose stack. To run that stack instead, copy it
to .env and run docker compose up -d --build. Compose starts PostgreSQL, Redis, the API,
the dashboard, and the web app; its web app is exposed at http://localhost:3000.
apps/
api/ → Hono API engine (port 4000)
cli/ → CLI tool (`openship deploy`)
dashboard/ → Next.js deployment dashboard (port 3001)
desktop/ → Electron desktop app and local service launcher
email/ → Email engine and Zero server/client orchestrator
web/ → Next.js marketing site (port 3009 in development)
packages/
adapters/ → Docker, bare, and cloud runtimes plus infrastructure adapters
core/ → Shared types, constants, utilities, errors
db/ → Drizzle ORM schema + client + repositories
db-email/ → Email-server Drizzle schemas + client
onboarding/ → Shared onboarding flows, validation, and API client
ui/ → Shared React components (Tailwind)
Most workspaces extend tsconfig.base.json at the repo root. The email client and server
maintain their own strict TypeScript configurations.
- Commits: Conventional Commits -
feat:,fix:,docs:,chore: - Branches:
feat/,fix/,docs/,chore/ - Code style: Prettier - run
bun formatbefore committing - Types: TypeScript strict mode everywhere
Each API module lives in apps/api/src/modules/<name>/ and follows this structure:
<name>.routes.ts # Route definitions (Hono)
<name>.controller.ts # Request handlers
<name>.service.ts # Business logic
<name>.schema.ts # TypeBox validation schemas
Shared modules (auth, projects, deployments, domains, webhooks, health) are always mounted. The billing module is cloud-only - it's only mounted when CLOUD_MODE=true in the environment.
If you're adding something that should only exist in the cloud version:
- Gate it behind
CLOUD_MODEinapps/api/src/app.ts - Make any required env vars (like Stripe keys) optional in
apps/api/src/config/env.ts - Self-hosters should never see 500s from missing cloud config
bun db:generate # Generate Drizzle migration files from schema
bun db:push # Push schema to dev database (no migration file)
bun db:migrate # Run pending migrations (production)
bun run --cwd packages/db db:studio # Open Drizzle Studio (database browser)Schema lives in packages/db/src/schema/.
The root test and build scripts run the corresponding tasks across the workspaces that define them:
bun run test
bun run buildRun workspace checks directly when working on a single area. For example, the API typecheck
is bun run --cwd apps/api lint. Run bun format before committing and review the resulting
diff so unrelated files are not included.
Open an issue or start a discussion - happy to help!