System Overview

Registration-only backend. No AI, no agents, no background workers. A CRUD wizard with server-computed validation, dynamic step visibility, and a real Spark ledger.

IN SCOPE
  • Legal onboarding wizard (CLIFF_ONB module)
  • Steps welcomeindirectTaxlaunch
  • Server-side Spark ledger (real, not display reconstruction)
  • Amazon Cognito auth (backend-mediated)
  • 32 onboarding screens + step resolver
  • Master data seeding from frontend/src/config/data/*.json
OUT OF SCOPE
  • Knowledge of Product module (.kop-root)
  • Season / market simulation
  • Company Room dashboard
  • Concept, Plan, Budget, Hire, Build, Market, Record
  • Real government registry integration
  • AI / LLM / agent runtime

Four Domains

01
auth
Cognito-backed founder accounts. Built first — everything else depends on it.
cognito_client.py jwt_verifier.py schemas.py routes.py
02
ledger
Spark event log. Built second — onboarding calls into it for every priced action.
models.py reasons.py project.py assert_supply.py repository.py
03
masterdata
Read-only lookups. 19 tables seeded from JSON. GET-only endpoints.
models.py repository.py schemas.py routes.py
04
onboarding
The wizard. Calls into ledger for every priced action. Owns all step logic.
models.py step_resolver.py type_rules.py fee_service.py registration_service.py

Registration State Machine

draft
Default. Mutable through every step up to review.
submitted
Set when review declarations all pass.
approved
Demo: ~10s animated loader. Issues registration ID.
launched
Requires approved + bank_opened. Calls handoff().

Tech Stack

RuntimePython 3.11 + FastAPI + uv
ORMSQLAlchemy 2.0 async (asyncpg driver)
MigrationsAlembic — autogenerated, reviewed before apply
Primary KeysUUID, app-generated via uuid4()
AuthAmazon Cognito, backend-mediated (confidential app client)
DatabasePostgreSQL 16
FrontendNext.js 16.3.x Pages Router, static export, React 19, TypeScript 5
Testingpytest + pytest-asyncio + pytest-cov

Locked Decisions (§ 4A)

4A.1
SQLAlchemy 2.0 async — not bare asyncpg. 35 tables with FK, JSONB, and a monotonic ledger seq need typed models and reviewable migrations.
4A.2
UUID primary keys — app-generated with uuid4(), not serial integers. registration_id is the ledger join key and must not be guessable.
4A.3
One error hierarchyDomainErrorNotFoundError / ValidationError / ConflictError. One FastAPI handler renders all as {"error": {"code", "message", "details"}}.
4A.4
GRANT fires on country first-set. country is then immutable — a second PATCH attempt returns ConflictError("COUNTRY_ALREADY_SET").

The 32 Onboarding Screens

Pulled from the HTML's ONB array. Conditional steps are skipped server-side by the step resolver — the frontend renders whatever GET /steps returns.

Data Model

35 tables total: 19 master data (seeded once from JSON, read-only at runtime) + 16 transactional (written per founder session).

API Endpoints

Every onboarding route requires a valid Cognito bearer token. Master data endpoints are GET-only, no auth needed.

Spark Ledger

10,000 Spark granted flat on country selection. Every cost is a ledger event — never a field mutation. Balance is always computed by folding the event log.

⚡ Spark Flow Simulator Click steps to simulate Spark consumption
Supply invariant: minted − burned = circulating = treasury + locked + capital ✓ VALID

Simulate Events

Event Log

#0GRANTMINT+10,000

Closed Reason-Code Table

Three Accounts Per Registration

treasury
Spendable now. The only account that can fund new burns. Starts at 10,000 after GRANT.
locked
Held by the bank during the compliance delay. Still the founder's money — just frozen. Unlocks after delay_days per country (IN:4, UK:1, SG:5, AE:14, US:0).
capital
Subscribed into the company as share capital. No longer free cash, but not destroyed — returned to the founder as paid-up capital at handoff.

Build Order

Eight sequential phases. Each phase must be stable before the next begins. No step is optional.

§ 13 — Genuinely Pending (Not In Scope)

🏗️
Company / Product Building
plan.ts + build-sim.ts run entirely in browser. A future domain/company/ would burn BUILD_SPEND against the same treasury this phase hands off.
👥
Employees / Hiring
hiring.ts, crew.ts, desk.ts — all client-side, lost on refresh. A future domain/crew/ would burn HIRE_FEE, PAYROLL, SEVERANCE.
📈
Spark During the Season
Reason codes REVENUE, PAYROLL, OVERHEAD, FUNDING_ROUND are already reserved. The ledger was designed to extend past this phase without a reshape.