Canada's Phoenix pay system launched in 2016. It burned. By 2018, it had produced 1.2 million pay errors. Public servants went unpaid, were overpaid, or received no pay at all. The total cost to fix it? $2.4 billion and counting.
The solution? The government hired Dayforce (Ceridian) for another $4.2 billion to replace it. The phoenix rises again — at taxpayer expense.
Castor is named after the beaver (Castor canadensis) — Canada's national symbol, and nature's engineer. Beavers don't rise from ashes. They just build things that work.
This is a proof-of-concept federal payroll system, built entirely by agentic AI, demonstrating that you don't need a mythical bird or a $4.2 billion contract to pay people correctly.
Phoenix burned. Dayforce is expensive. The beaver just builds.
Castor is a working demo of the core Canadian federal payroll system — employee self-service, compensation advisor tools, HR actions, manager approvals, collective agreements, retroactive pay, acting appointments, statutory deductions — the whole deal.
It was vibe-coded by AI. Yes, really. Every line. The domain logic, the UI, the database schema, the bilingual translations, the 251 tests — all generated by LLMs following architecture patterns and domain rules.
The point isn't that AI writes perfect code. The point is that the distance between "we need a payroll system" and "here's a working demo" should not be measured in billions of dollars.*
| Role | What they see |
|---|---|
| Employee | Pay stubs, tax slips, leave balances, overtime requests, transactions, direct deposit |
| Compensation Advisor | Case management, employee search, audit trail, pay corrections, retroactive pay simulation, acting appointment management |
| HR Officer | Employee management, onboarding, employment changes, leave entry, department transfers |
| Manager | Approval queue, team view, acting appointments |
- 4 collective agreements (PA, EC, IT, SV) with actual TBS pay scales
- CRA 2025 statutory deductions: CPP, CPP2, EI, federal/provincial income tax, pension
- Retroactive pay engine — the hard problem. Simulate a 2.5% increase across 50,000 PA employees, see the cost, then execute with full audit trail
- Acting pay differentials, overtime (including IT's 1.5x/2.0x two-tier), bilingual bonus, LIA agreements
- Money value object — bigint cents, zero floating-point. Because this is payroll, not a tip calculator.
Every string, English and French. Language toggle on every page. Because Gouvernement du Canada.
| Layer | Choice | Why |
|---|---|---|
| Framework | Next.js 16 + App Router | Server components, server actions, file-based routing |
| Database | PostgreSQL + Drizzle ORM | Type-safe queries, migrations, zero magic |
| UI | shadcn/ui + Tailwind v4 | GC-inspired design system, accessible out of the box |
| Testing | Vitest | 251 tests. They pass. |
| i18n | next-intl | Proper bilingual, not bolted on |
| Auth | Better Auth (mock) | Role picker for demo purposes |
| Money | Custom Money class (bigint cents) |
Never use number for currency. Ever. |
src/
domain/ <- Pure TypeScript. Zero framework deps. Fully testable.
agreements/ Collective agreement strategies (PA, EC, IT, SV)
pay/ Entitlement, deduction, retroactive, acting, LIA calculators
models/ Employee, Assignment, PayStatement, PayCorrection...
shared/ Money value object, domain types
infrastructure/ <- Drizzle, PostgreSQL, repositories
db/
schema.ts 16 tables
seed/ 500+ employees, pay periods, cases, approvals
repositories/
application/ <- Use-case orchestration
pay-run-service.ts
overtime-integration-service.ts
app/ <- Next.js App Router (thin server actions, client components)
(employee)/
(advisor)/
(hr)/
(manager)/
Hexagonal architecture. Domain layer has no idea Next.js exists. Swap the framework tomorrow and the pay calculation engine doesn't change.
# Start PostgreSQL
docker compose up -d
# Install dependencies
npm install
# Push schema to database
npm run db:push
# Seed with 500+ employees
npm run db:seed
# Start dev server
npm run devOpen http://localhost:3000. Pick an employee. Pick a role. Go wild.
| Metric | Value |
|---|---|
| Lines of code | ~15,800 |
| Test files | 19 |
| Tests passing | 251 |
| Database tables | 16 |
| Collective agreements | 4 (PA, EC, IT, SV) |
| App routes | 30 |
| Server actions | 33 |
| Bilingual messages | 750+ lines (EN + FR) |
| Seed employees | 500+ |
| Cost to build | ~$0 in API calls |
| What Canada paid for Phoenix | $2,400,000,000 |
| What Canada is paying for Dayforce | $4,200,000,000 |
npm run dev # Start dev server
npm run build # Production build
npm run test # Run tests
npm run test:watch # Run tests in watch mode
npm run lint # ESLint
npm run db:push # Push schema to database
npm run db:seed # Seed with sample data
npm run db:studio # Drizzle Studio (browse DB)- This is a proof of concept, not production software
- Auth is mocked — there's a role picker, not SAML/2FA
- Tax calculations follow CRA 2025 formulas but aren't CRA-certified
- 500 seed employees, not 280,000
- No, the AI didn't do this alone — a human architect directed every phase
- Yes, a real system needs security audits, load testing, accessibility certification, change management, training...
But also: none of that costs $6.6 billion.
MIT
