Accord is an open-source payroll system of record. It is built for local governments and public works teams. Teams keep employee and payroll facts in one place. They record only dated changes and monthly exceptions. They calculate and approve payroll with a maker/checker flow. They then create export-ready Excel and PDF reports from posted, frozen data.
- System of record — there is no spreadsheet import path. Accord holds the facts itself.
- Exact money — money uses PostgreSQL
NUMERICand PythonDecimal. The API moves money as a plain decimal string.floatis banned from payroll domain code. - Temporal truth — master data changes are dated versions. A posted payroll keeps the exact version ids and snapshots it was built from.
- Immutable posting — posted results are never edited or deleted. To fix a mistake, withdraw before posting, or reverse after posting.
- Single organization — each deployment serves one organization (ADR 0011). Forced PostgreSQL row-level security stays in place as fail-closed kernel debt until Phase 2 removes it.
- Transactional evidence — every business change commits together with an append-only audit event and an outbox event.
The backend is FastAPI on Python with PostgreSQL. The frontend is React with TypeScript. Sign-in uses WorkOS. Report files land in S3-compatible object storage. Deployment is Docker Compose (self-hosted) or managed PostgreSQL plus object storage.
backend/— FastAPI app, payroll engine, migrations, worker, testsfrontend/— React app (design system carried over from Atlas)deploy/— Docker Compose, images, nginx, local MinIO profiledocs/— architecture, ADRs, domain reference, report specs, securityfixtures/sanitized/— synthetic test data only; real PII is never committed
You need four things installed first:
- pnpm 10.x — see
packageManagerin the rootpackage.json. - Python 3.14 — the version CI uses.
- PostgreSQL 18 — running locally. (Or skip local setup and use Docker Compose; see the next section.)
- Node.js 22.22 or newer — for the frontend.
Then run these three commands from the repository root:
# 1. Install frontend workspace dependencies.
pnpm install
# 2. One-time database and virtualenv setup. Creates the `accord` and
# `accord_test` databases, the ADR-0001 roles, and backend/.venv.
./scripts/dev-setup.sh
# If Postgres is not running yet (macOS Homebrew):
# ./scripts/dev-setup.sh --start
# 3. Start the API and the Vite dev server. This also runs
# `alembic upgrade head` for you.
./scripts/start.shThe scripts pick free ports and remember them under .accord-dev/:
- Postgres:
5432, then Homebrew's configured port, then5433 - Frontend:
5173, then5174, and so on - Backend:
8000, then8002, and so on
Set PGPORT, FRONTEND_PORT, or BACKEND_PORT to force a port.
Local dev runs with DEV_AUTH_BYPASS=true. Any email and password work on
the login form. The session identity comes from DEV_AUTH_EMAIL (default
dev@accord.local).
Before you can use the app, the deployment needs its one organization. This is a CLI step by design (ADR 0011):
PGPORT="$(tr -d '[:space:]' < .accord-dev/pg.port)"
DATABASE_URL="postgresql+asyncpg://accord:accord@127.0.0.1:${PGPORT}/accord" \
backend/.venv/bin/python scripts/provision_organization.py \
--name "My Org" --slug my-org --admin-email dev@accord.localRun ./scripts/status.sh, open the Frontend URL it prints, and sign in. You
land in the app as an organization administrator even when the default port
was busy.
To explore with realistic data, load the synthetic June 2026 fixture (32 employees, offices, pay components, recurring items):
BACKEND_PORT="$(tr -d '[:space:]' < .accord-dev/backend.port)"
backend/.venv/bin/python scripts/seed_june_fixture.py \
--base-url "http://127.0.0.1:${BACKEND_PORT}"Then create a payroll period and run in the UI, save the roster, and calculate. The totals match the golden test fixture.
New report exports use the normalized v3 workbook contract. Before calculating the run, complete the fields shown by Report readiness:
- In Pay Components → Report defaults, enter the organization identity, office address and contact details, DDO and treasury details, account heads, bank-advice recipient, GPF remittance destinations, fund/plan labels, and signatories.
- In Organization → Posts, enter sanctioned strength, vacancies, pay scale, and export order for every post in the run.
- Map every amount-bearing pay component to its Pay Bill column.
- Complete each employee's applicable PAN, retirement account, salary-bank, export remark, advance, and accommodation fields. Unknown optional identity values stay blank; do not invent them.
- On the pay run, enter an amount for exceptions and one-time items. An override of an existing rate-based component may instead enter a replacement rate. Add its reason and service period when applicable, then complete bill, advice, approval, token, and voucher metadata.
Calculate again after changing any of these facts so the immutable snapshot
contains them. Post the run, open Reports, resolve every readiness issue,
then choose Export. The result is one 18-sheet .xlsx workbook. See the
canonical export contract for
the exact sheet and formula rules. The v2 ZIP remains available only for legacy
API clients that request template_version=v2.
Compose brings its own PostgreSQL and MinIO, so nothing else is required:
cp deploy/.env.example deploy/.env # fill required secrets (WorkOS, SESSION_SECRET_KEY, …)
docker compose -f deploy/docker-compose.yml up --buildImages: ghcr.io/ornament-ai/accord/backend and
ghcr.io/ornament-ai/accord/web. The local web port is 8085 (see the
Compose web service and scripts/smoke-test.sh).
./scripts/verify.sh # lint, typecheck, unit tests (skips missing lanes)
./scripts/smoke-test.sh # health checks against a running deploy (default http://127.0.0.1:8085)Release gates are defined in
docs/release-acceptance.md (letters A–F and
H–K; there is no gate G). Gate K (deploy/restore/E2E) is the active
release gate.
| Gate | Status | One-line description |
|---|---|---|
| A — Atlas baseline | Complete | Upstream Atlas shell/transplant baseline verified |
| B — Phase 0 contracts | Complete | Testing, threat model, release acceptance, security, ADRs, domain contracts |
| C — Transplant shell CI | Complete | Lint, typecheck, unit, and API smoke on FastAPI + React shell |
| D — Cross-tenant isolation | Complete | Forced RLS + no cross-tenant IDOR via API/services/storage/workers |
| E — Effective-dated master data | Complete | Hire/pay/org effective-dating across period boundaries |
| F — Calculation correctness | Complete | Synthetic totals + Decimal-only payroll domain (no float) |
| H — Workflow integrity | Complete | Maker/checker, post, idempotency, posted SQL immutability |
| I — Export durability | Complete | S3-compatible artifacts endure; tenant object isolation |
| J — Reports & reconciliation | Complete | Shared DTO for Excel/PDF; reconciliation to posted source |
| K — Deploy / restore / E2E | In progress | Tag deploy workflow, clean-env deploy, backup/restore RLS, Playwright |
| Doc | Description |
|---|---|
| docs/architecture.md | Runtime components, workflow state machine, tenancy, report pipeline |
| docs/payroll-domain.md | Payroll domain glossary and gross-to-net model |
| docs/operations.md | Deploy, backup/restore, and day-two operations |
| docs/release-acceptance.md | Release gate matrix (A–K) and evidence requirements |
| docs/security.md | Security controls, roles, and operational expectations |
| docs/testing.md | Test strategy and gate → suite mapping |
| docs/threat-model.md | Threat model for tenancy, workflow, and exports |
| docs/atlas-upstream-manifest.md | Atlas upstream pin, inclusions, and exclusions |
| docs/report-specs/report-catalog.md | First-release report catalog and reconciliation rules |
| docs/adr/ | Architecture decision records 0001–0011 |
Accord transplants the design system and infrastructure conventions of the
Atlas application from a pinned upstream release tag. See
docs/atlas-upstream-manifest.md for exact
provenance, exclusions, and licenses.
Apache-2.0 — see LICENSE.