# Parking System — Project Guide A parking-management system: a **web app on a dedicated, hardened Linux appliance**, deployed on-site at a parking facility. Two forces shape almost every decision: **offline-first** operation and a **threat model whose primary adversary is the legitimate operator at the booth** (not an outsider). Keep both front of mind. ## Repository layout This directory is a **Turborepo** monorepo. App code lives here; the knowledge base lives in `wiki/`. ``` parking-system/ ├── CLAUDE.md # this file — app development guide ├── package.json # turborepo root ├── turbo.json ├── apps/ │ ├── server/ # Fastify backend (device drivers, API, auth); serves the SPA │ └── web/ # React + Vite SPA (operator UI) ├── packages/ │ ├── db/ # Drizzle ORM schema + migrations (SQLite local; PostgreSQL sync target) │ ├── devices/ # device adapters behind shared interfaces (reader/printer/relay) │ └── shared/ # shared types/utils └── wiki/ # LLM-maintained knowledge base (Obsidian vault) — see wiki/CLAUDE.md ``` > Code layout above is the intended target; scaffold packages as the work reaches them rather > than all up front. ## The wiki is the knowledge base — consult it first `wiki/` is an LLM-maintained design knowledge base (the "LLM Wiki" pattern). It is **not app code** and has its own schema at `wiki/CLAUDE.md`. Before making architectural decisions or implementing a subsystem, **read the relevant wiki pages** for the rationale, rejected alternatives, and open questions: - Start at `wiki/overview.md`; catalog in `wiki/index.md`. - Settled decisions: `wiki/decisions/standing-decisions.md`. - Unsettled, procurement-driving items: `wiki/decisions/open-questions.md` — **do not hard-code around these without flagging them.** When app work surfaces a new design fact, decision, or contradiction, **update the wiki** following `wiki/CLAUDE.md` (ingest/query/lint workflows). Source documents go in `wiki/raw/`. ## Stack (settled) All dependencies are **MIT / Apache / BSD** — a hard constraint to avoid vendor lock-in and license rug-pulls. See `wiki/entities/technology-stack.md` for the full table and rationale. | Layer | Choice | | --- | --- | | Monorepo | Turborepo | | Backend | Node.js + Fastify | | Frontend | React (SPA, Vite), served by Fastify | | Local DB | SQLite (`better-sqlite3`) + Drizzle ORM (Drizzle Kit) | | Remote sync target | PostgreSQL (deferred — not a runtime dependency) | | Auth | Local JWT (`@fastify/jwt`) + bcrypt + role guard (admin/operator/cashier/readonly) | ## Architecture constraints that bind the code These are not negotiable defaults — they come from the threat model and safety analysis: - **Offline-first.** Nothing in core operation may depend on a network. Auth, DB, and device decisions must work air-gapped. No external identity provider; no cloud runtime dependency. - **Append-only, signed event log.** Entry/exit events are **never edited or deleted** — a "void" is itself an appended event. Events are **hash-chained** (each stores the prior event's hash) and **signed by an ATECC608 secure element**. This is the core anti-fraud mechanism; don't add update/delete paths to event records. - **Device-agnostic adapters.** Business logic talks **only to interfaces** (reader/printer/relay), never to a device SDK. Hardware swaps = a new adapter in `packages/devices`, nothing else. - **A barrier is not a door.** Never drive a barrier as a timed "open for N ms" auto-close. Physical safety lives in the barrier operator's firmware; the app only ever expresses **intent ("open")**. Relay interfaces are `pulseOpen`, never timed close. - **Fail-state.** On power/network/host loss: **entry fails closed, exit fails open** (never trap a vehicle — often a legal egress requirement). - **Network isolation for access controllers.** The UHPPOTE controller speaks unauthenticated UDP; it must sit on an isolated VLAN reachable only by the host. Treat its event log as tamper-evident (host-side index tracking), not tamper-proof. - **Keep PCI scope out of the app.** Payments go through a standalone bank-certified P2PE terminal — the application must not handle card data. For the full reasoning behind each, follow the links from `wiki/overview.md`. ## Conventions - TypeScript throughout. Match the style of surrounding code. - Confirm before destructive or outward-facing actions. Commit/push only when asked.