Open-source realization kernel for consulting firms: utilization, WIP, contract and milestone billing, partner-level profitability, and PostgreSQL row-level security. Timesheets are an input to realization — not the product. Optional Xero and QuickBooks journal push. See docs/DESIGN-PIVOT.md.
Permit rule: Realization numbers come from approved time, WIP, and contracts — not from a model. Humans operate this kernel. Same instinct: SafetyMP.
- Kimai is a time tracker. Hours, approvals, and timesheets are its product.
- This repo is realization. WIP, contract/milestone billing, utilization, and partner-level profitability are the product; time entries feed those numbers.
- Postgres RLS is the tenancy model. Organization isolation is enforced in the database, not only in application middleware.
- Multi-tenant organizations with role-based access (Admin, Manager, Consultant)
- Clients & projects — tasks, team members, budgets, billing models (T&M, fixed fee, retainer, milestone)
- Time & expenses — approval workflows, categories, receipt uploads, bulk approval
- Resources — profiles, allocations, utilization reporting
- Billing — WIP and contract/progress invoicing, milestone billing, PDF export, journal CSV
- Accounting — chart of accounts, Xero / QuickBooks OAuth, push journals from invoices
- Reporting — profitability with contract/milestone columns, expense breakdown by category
- Row-level security — PostgreSQL RLS enforces organization isolation
| Dashboard | Invoices | Profitability | Accounting |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
- Next.js 16 (App Router) · React 19 · TypeScript 6
- PostgreSQL 16 · Prisma 7 · Auth.js / NextAuth v5
- Tailwind CSS 4 · Vitest
- Node.js 22+ (see
.nvmrc) - Docker (for local Postgres) or an existing PostgreSQL 16 instance
- npm
git clone https://github.com/SafetyMP/Professional-Service-Automation.git
cd Professional-Service-Automation
cp .env.example .env
# Edit .env: set AUTH_SECRET (e.g. openssl rand -base64 32)
docker compose up -d
npm install
npm run db:migrate
npm run db:seed
npm run dev -- -p 3005
# http://localhost:3005| Field | Value |
|---|---|
| Organization | demo-firm |
| Admin | admin@demo.com / password123 |
| Manager | manager@demo.com / password123 |
Do not use demo credentials in production. Change passwords and rotate AUTH_SECRET before deploying.
See .env.example:
| Variable | Purpose |
|---|---|
DATABASE_URL |
App database URL (RLS role psa_app) |
DIRECT_URL |
Direct Postgres URL for migrations (postgres superuser) |
AUTH_SECRET |
Session signing secret (required) |
AUTH_URL |
Public app URL (e.g. http://localhost:3005) |
XERO_CLIENT_ID / XERO_CLIENT_SECRET |
Optional — Xero OAuth |
QUICKBOOKS_CLIENT_ID / QUICKBOOKS_CLIENT_SECRET |
Optional — QuickBooks OAuth |
QUICKBOOKS_ENV |
sandbox (default) or production |
Local Docker Postgres listens on port 5440 (mapped from container 5432).
For production or a one-command demo stack (Postgres + app in Docker):
export AUTH_SECRET="$(openssl rand -base64 32)"
docker compose -f docker-compose.stack.yml up --build -d
# http://localhost:3000 — demo-firm / admin@demo.com / password123See docs/deploy.md for Railway, Fly.io, manual Node deployment, and the production checklist.
| Command | Description |
|---|---|
npm run dev |
Prisma generate + Next.js dev server |
npm run build |
Production build |
npm run lint |
ESLint |
npm run typecheck |
TypeScript check |
npm run test |
Vitest unit tests |
npm run db:migrate |
Apply Prisma migrations |
npm run db:seed |
Reset and seed demo data |
npm run check:boundaries |
Module import boundary lint |
./scripts/verify.sh |
Full local verification gate |
npm run screenshots |
Capture README screenshots and demo GIF (requires running server) |
lib/ Domain services (clients, projects, time, billing, …)
src/app/ Next.js App Router pages and API routes
prisma/ Schema, migrations, RLS policies
specs/ Domain rules and product scope
tests/ Unit tests
scripts/ Seed, verify, boundary checks
Domain logic lives in lib/<domain>/service.ts. Cross-domain imports must go through public service files only (enforced by scripts/check-boundaries.ts).
Contributions are welcome. See CONTRIBUTING.md for setup, conventions, and the pull request checklist.
Phase 1 (MVP) is implemented. Planned Phase 2 items are listed in specs/product/mvp-scope.md, including accounting integrations, expanded expense UI, and general ledger support.
Release history: CHANGELOG.md.
GitHub Actions on every push and pull request:
| Workflow | Purpose |
|---|---|
| CI | Lint, typecheck, unit tests, boundaries, Prisma validate, production build, E2E smoke tests |
| CodeQL | Security analysis (weekly + on PR) |
| Release | Publish Docker image to GHCR on release |
| Screenshots | Regenerate README images on release |
Dependabot opens weekly PRs for npm, GitHub Actions, and Docker base images.
Local E2E: npm run test:e2e (requires running server + seeded DB on port 3005).
docs/DESIGN-PIVOT.md— realization-kernel positioning (why not Kimai / generic PSA)docs/deploy.md— Docker, Railway, Fly.io, and production checklistdocs/development.md— local setup, architecture, testing, and common tasksAGENTS.md— community agent/developer contractdocs/factory-overlay.md— factory/site overlay (moved out of root AGENTS.md)specs/domain/billing-rules.md— billing model rulesspecs/domain/profitability-rules.md— profitability calculations
See SECURITY.md for reporting vulnerabilities.




