Harbor Pilot CRM is a local-first CRM workspace built from the original Deep Sea design package. The primary product is now the protected workspace routes, not the imported HTML screen library.
The legacy 49-screen design package still exists under /s for reference while
features are converted into real routes and database-backed workflows.
HarborPilot installs and builds without private secrets, but the working CRM requires your own Clerk app and Postgres database.
npm ci
cp .env.example .env.local
# fill in .env.local
node --env-file=.env.local ./node_modules/.bin/tsx scripts/migrate.ts
npm run devOpen http://localhost:3000, sign in through Clerk, then create the first
contact. See the Setup Guide for the required provider setup.
- Next.js 15 App Router, React 19, TypeScript
- Tailwind CSS 3 for the app shell and workspace pages
- Clerk authentication and user webhooks
- Stripe checkout, customer portal, and subscription webhooks
- Drizzle ORM on Neon Postgres
- Deployable to any Node.js/Next.js host after environment variables and migrations are configured.
/- public entry page/dashboard- command center/contacts- contact list, filtering, and create form/contacts/[id]- contact profile, edit form, linked opportunities, tasks, and activity/pipeline- opportunity board with stage updates/calendar- task queue and completion flow/messages- communication/activity timeline/automation- automation readiness/integrations- provider configuration status/audit- database-backed workspace action log/projects- project delivery records linked to contacts/documents- document metadata linked to contacts/projects/billing- Stripe checkout and customer portal actions/settings- workspace/system links/sand/s/[slug]- imported Deep Sea design reference library/api/health- public operational health check
All workspace routes are protected by Clerk middleware. The public page and auth/webhook endpoints are intentionally public.
The current app-owned tables are namespaced to avoid colliding with the broader existing Neon schema:
harbor_contactsharbor_opportunitiesharbor_tasksharbor_activitiesharbor_audit_eventsharbor_projectsharbor_documents
Existing shared tables remain:
users- Clerk user recordssubscriptions- Stripe subscription records
npm install
npm run dev
npm run build
npm run typecheck
npm run lint
npm run connections
npm run smoke
npm run smoke:workflowFor local dev, bind to 0.0.0.0 if testing through both localhost and
127.0.0.1:
npm run dev -- --port 3020 --hostname 0.0.0.0Generate migrations after schema changes:
npx drizzle-kit generateApply migrations using Node's env-file loader. Do not use source .env.local;
the database URL can contain shell-sensitive characters.
node --env-file=.env.local ./node_modules/.bin/tsx scripts/migrate.tsVerify live table shape:
node --env-file=.env.local ./node_modules/.bin/tsx scripts/verify-db.ts- Do not edit
screens/<slug>/code.htmlunless explicitly converting or fixing a design reference screen. - Do not build new product functionality by mutating the imported HTML mockups. Add real routes, components, server actions, and database-backed records.
- Do not add sample/demo data to make pages look full. Empty states should stay honest until real workspace records exist.
- Do not print or commit
.env.localvalues. - Keep external actions explicit and auditable.
Before pushing/deploying a working slice:
npm run build
npm run typecheck
npm run lintThen smoke-test:
npm run smoke -- http://localhost:3020npm run smoke:workflownpm run connections/returns200/api/healthreturns200withstatus: ok- protected routes redirect to
/sign-in - Coolify image hash matches the pushed commit
- public production URL returns
200