Orbit CRM
A multi-tenant sales CRM for a fictional team, with a copilot that cites a record for every fact and changes nothing without approval.
- Kind
- Live demo
- For
- A fictional business
- What it is
- Business SaaS dashboard
- My role
- Design and engineering
- Built
- 2026


The idea
A multi-tenant sales CRM for a fictional team: leads, contacts and companies, a drag-and-drop pipeline, tasks and one honest timeline per record, analytics that explain every figure, and a copilot that cites a record for every fact. Running as a public demonstration: the data is fictional and stays in your browser, nothing is sent anywhere, and the copilot uses offline rules, not an AI model.
- Who it's for
- Small sales and operations teams tracking leads and customers in spreadsheets and chat threads.
- The problem
- Small sales teams lose track of leads and deals between spreadsheets and chat threads. A CRM has to make the next step obvious, keep each team's data apart, and give figures that explain where they came from.
What I built
Everything below works in the public demonstration today.
I designed and built Orbit CRM end to end: the application, its two data stores, the PostgreSQL schema and security, the copilot and the tests.
What works in the demo
- Leads, contacts and companies with search, filters, sorting and pages kept in the URL; lead qualification and conversion into a company, contact and deal
- A pipeline board with drag and drop by mouse, touch and keyboard, a Move button as the accessible alternative, won and lost with reasons, and totals kept per currency
- Tasks with due dates in the workspace's time zone, a sales home of follow-ups and deals that need attention, and an in-app inbox
- One timeline per record that says how each entry was recorded (by Orbit, by a person, or simulated for the demo), with notes, team comments and logged calls, meetings and emails
- Analytics where every figure states its source, period, denominator and currency rule
- A copilot that briefs you on a deal or lead, finds stalled deals by named rules, drafts a follow-up and suggests tasks; every fact links to its record and nothing changes until you approve it (offline rules in the public demo)
- Role preview (owner, admin, sales rep, viewer) and two fictional workspaces, with reset at any time
The experience
Real screens, captured from the live site as a first-time visitor sees them.



The engineering
How it is put together, and the problems that took the most care.
One application with two stores behind one contract. In a real deployment, PostgreSQL enforces every permission with row-level security; in the public demo, the same screens and rules run on a store in the visitor's browser. A parity suite checks that both give the same answers for every role.
Shared screens and rules
The same components, validation schemas, permission matrix and form logic serve the live application and the demo.
Live application
Server actions over PostgreSQL 18, one transaction per call as a least-privileged role, with the workspace taken from the session, never from the request.
Public demo
The same screens over an in-browser store: two fictional workspaces, role preview and reset. No database and no AI provider.
Copilot
A deterministic router and read-only tools compose every answer from records the member may open. In the public demo it runs on offline rules, not an AI model.
Technology
- In the public demo
- Next.js 16
- React 19
- TypeScript
- Tailwind CSS v4
- dnd-kit
- Zod
- Vitest
- Playwright
- Built and tested, not used by the demo
- PostgreSQL 18 with row-level security
- Anthropic SDK
Hard problems, and how I solved them
Isolation tested by attack
- Problem
- In a multi-tenant CRM, one team must never see another's records, even through a forged id or a replayed request.
- Approach
- Row-level security, tenant-aware composite keys and sessions checked on every request, tested with forged workspace ids, record ids, sessions and idempotency keys across two workspaces on real PostgreSQL.
Two people, one deal
- Problem
- When two people move the same deal at once, one change must not silently overwrite the other.
- Approach
- Moves are optimistic in the browser and decided by the database with a version check and an idempotency key.
A copilot that cannot invent facts
- Problem
- Sales answers are only useful if every figure and claim can be traced to a record.
- Approach
- Code reads the records and composes the answer. Facts, rule findings and suggestions are kept apart, and every fact links to its source. A model, when one is configured, may only add a labelled interpretation that is checked against those records. Nothing changes until a person approves it.
The evidence
What can be checked, and what the public version does not do.
- Isolation tests
- 371 tests on real PostgreSQL, including the release isolation suite
- Copilot
- 63 evaluations and 22 database tests: no cross-workspace retrieval, no fabricated facts, no unapproved change
- Lighthouse (production)
- Mobile 90–95, desktop 100 on every demo page; accessibility 100; CLS 0
Figures from the project's own release record, written when it shipped. The public demonstration runs on fictional data.
Not in the public demo
- A hosted multi-tenant deployment for a real team (the PostgreSQL schema, row-level security, sessions and invitations are built and tested against PostgreSQL, but not connected to the public demo)
- Email delivery, password reset, two-factor sign-in and SSO
- A hosted AI model behind the copilot (the provider adapter is built and tested against a stand-in, not against a real model)
- Saved reports and forecast refinements
Explore the product
Running your sales from spreadsheets?
Orbit shows the business software I build: one pipeline, honest records, figures that explain themselves and access rules enforced by the database, not just hidden in the interface.