TAEDS Architecture & Design
Reference Documentation
TAEDS is the Kenya Police traffic accident & enforcement information system. Every layer, from the officer's installable phone app in a no-signal valley, through the API that records the case, to the PostgreSQL database that becomes the national statistic, is engineered so a record captured once is complete, tamper-evident and true. Eleven reference diagrams below document how the system is built, secured, synchronised, delivered and recovered.
Modular monolith
Thirteen strict Laravel modules in one deployable. Tight borders, single transaction per case, easy operations — not a ball of mud.
PostgreSQL is the truth
One source of truth with PostGIS spatial power. Search, cache and object store are derived — never authoritative.
Offline-first by design
Field capture runs on a durable on-device outbox. No signal, no lost record — and replay never duplicates.
Secure by default
Backend-enforced RBAC and data scope, segregation of duties, SHA-256 evidence and an append-only audit trail.
Complete Platform Architecture
The full request path — from the officer's PWA device through the TLS gateway, the API and application modules, down to the data services and physical infrastructure that run them.
Client layer
- Field officers use an installable PWA (React) that works offline.
- Command, analytics and administration use the same app in a desktop browser.
- Future consumers (partners, integrations) will use the public API contract.
Edge & application
- An nginx gateway terminates TLS, serves static UI and proxies
/api. - Laravel (PHP 8.5) exposes the REST API and owns every business rule.
- Thirteen modules share one codebase with enforced boundaries.
Data services
- PostgreSQL + PostGIS — authoritative store, relational + spatial.
- Redis — sessions, cache and the queue broker.
- PostgreSQL FTS — full-text search built into the source of truth; MinIO/S3 — evidence objects.
- Workers (Horizon + scheduler) drain the outbox.
Backend Architecture & the Request Path
One deployable Laravel application holding thirteen modules with strict borders, and the single disciplined route every mutation takes through the system.
Module ownership
- Each module owns its tables, Actions, API surface and tests.
- Cross-module access happens only through public contracts — never internals.
- Architecture tests fail CI if a module reaches into a neighbour's internals.
Why a modular monolith
- Whole-case transactions are easy — no distributed saga for one accident file.
- One build, one deploy, simple operations and rollbacks.
- Module discipline keeps it clean as volume grows.
Controllers are thin
Controller → Form Request → Authorization → Action → Domain → DB → Outbox → Resource.- Side effects exit through the durable outbox, never inside the request tail.
- Domain code never imports HTTP, cloud SDKs or browser APIs.
| Module | Owns (tables / responsibilities) | Primary consumers |
|---|---|---|
| Accidents | accident, location, road conditions, vehicles, drivers, casualties, witnesses, status history, initial/situation/crime reports, inquest, diary, conclusion | Officers, investigators |
| Enforcement | offences, citations, inspections, verifications, court cases, bonds, cash bail | Enforcement officers |
| Evidence | evidence, chain_of_custody, shared links, medical/parade sub-forms | All case roles |
| Reporting | reports, schedules, correspondence, sketches, analytics views | Command, analysts |
| Workflow | transitions, comments, task queue | Investigators, commanders |
| GIS | map tiles proxy, blackspots, boundaries, spatial queries | All roles |
| Integrations | NTSA / IPRS / payment circuit state, outbound calls | Backend workers |
| Administration / Auth / Audit / Sync / Notifications / Analytics | users, roles, permissions, stations, audit_logs, outbox, sync_cursors, notifications, dashboard aggregates | Everyone |
Database & Data Flow
Structured records land in PostgreSQL — which also hosts full-text search (tsvector) — while evidence objects land in MinIO and Redis serves cache. Every store is backed up and archived automatically.
Schema by module
- Every module owns its tables; ownership is enforced by tests, not by convention.
- Human case numbers
TAEDS-ACC-2026-000123; ULIDs for internal keys. - Migrate expand/contract; destructive changes require approval.
Spatial (PostGIS)
- Accident point geometry, road network, county/station boundaries.
- Black-spot clustering and heat-mapping run against geometry.
- GIS basemap tiles are proxied server-side (credentials never reach devices).
Evidence objects
- Files stored in MinIO (S3); rows in Postgres reference them by SHA-256.
- Metadata, hashes and custody live in the database — never only in the bucket.
- Backups cover both the database and the object store.
Frontend Architecture & Trust Boundaries
One React PWA serves the field device and the office. Server state, offline state and auth are deliberately separated, and every API payload is validated at the trust boundary.
Stack
- React 19 · TypeScript strict · TanStack Start/Router · Tailwind 4 + shadcn/ui.
- react-hook-form + Zod for typed forms; Recharts for dashboards; Leaflet for maps.
- PWA via Workbox — installable and offline-capable.
State strategy
- TanStack Query — server state cache of API truth.
- Dexie/IndexedDB — durable offline working copy + outbox.
- UI state stays in React; auth has a dedicated boundary.
Rules that hold
- Never hand-edit generated OpenAPI client or route tree.
- No raw
fetchin components — one central HTTP client. - Every screen models all states: loading · empty · offline · syncing · error · conflict.
Offline-First & the Outbox
Records are captured on the device first. The outbox replays them durably and idempotently once a connection returns — the server is always the source of truth.
Capture first
- Every field autosaves to IndexedDB as a local draft.
- A visible status shows
ONLINE / OFFLINE / SYNCING / SYNC ERROR. - The officer never waits on a signal to do their job.
Replay, don't guess
- Local mutations carry idempotency keys.
- The server writes the change and its outbox event in one transaction.
- Horizon consumers fan out to search, notifications and integrations.
Guarantees (tested)
- Durable — a crash never loses a queued draft.
- Idempotent — replaying never duplicates a record.
- Deterministic conflicts — server resolution wins, always.
Security & Role-Based Access
Layered controls from the network edge to the record — and a role matrix with data scope enforced by the backend on every action.
Backend is the lock
- Every sensitive Action checks permission and data scope.
- Self/station scope closes off accidental or malicious cross-record access.
- Audit + status history written in the same transaction as the change.
Roles & scopes
- Officers = self · investigators & commanders = station · regional = region · HQ/analyst/ICT = national.
- The RBAC matrix is one tested source of truth shared by UI and API (locked by an automated RBAC conformance suite).
- Segregation of duties: you cannot approve a case you recorded.
Hygiene
- Sanctum sessions + CSRF; persisted sessions are verified against
/auth/me. - No secrets in code or env files; TLS in transit.
- No demo-account fallback in production.
| Role | Backend code | Data scope | Highlights |
|---|---|---|---|
| Traffic officer | tofficer | self | capture, complete, submit |
| Enforcement officer | enforcement_officer | self | citations, money, court referrals |
| Station investigator | investigator | station | case files, statements, sketches, CRF |
| Station / County / Regional commander | commander · county_commander · rcommander | station · county · region | review & approve, reports, users view |
| Data analyst | analyst | national (read) | analytics, exports, forecasts |
| HQ administrator | hqadmin | national | approvals, master data, audit |
| System administrator | ictadmin | national | settings, security, users |
Evidence & Chain of Custody
From a photo taken at the scene to an archived exhibit — every byte is fingerprinted with SHA-256 and every touch is appended to an un-editable custody chain.
Capture & hash
- Client computes SHA-256 of the real bytes; server re-hashes and compares on verify.
- Status lifecycle: pending → uploaded → verified → approved.
- Storage keys derive from hashes — corruption is detectable.
Chain of custody
- Append-only events: captured · reviewed · downloaded · transferred · approved.
- Each event records who, when, from where (IP) and any details.
- Nothing is silently overwritten — change is a new event.
Governance
- Public share links carry random capability tokens and expiry.
- Medical (P3), parade and specimen sub-forms follow the same integrity rules.
- Defined by a dedicated design record and enforced with security tests.
Accident Case Workflow & State Machine
One accident travels through a legal state machine from the officer's draft to a closed national record — with returns, escalations and approvals guarded by roles.
Officer track
- Draft → completed offline → submit (owner-only).
- Completeness meter before submit — no half-finished cases.
- Returned cases come back with the commander's comment.
Investigator track
- Under Review: diary, statements, sketches, conclusion, CRF.
- Evidence validated and approved into the file.
- Statutory outputs assembled from structured data.
Command & beyond
- Approve / return / escalate — each with comment and audit.
- Approved cases feed dashboards, exports and national statistics.
- Status transitions are legal only via backend Actions.
Integrations & Resilience
NTSA, IPRS, payments and messaging integrate through one failure-tolerant contract. No external provider is ever the source of truth.
Connected providers
- NTSA — vehicle & driver verification.
- IPRS — identity screening.
- eCitizen / payments and email/SMS — notification & court outputs.
Failure contract
- Timeouts and exponential backoff; circuit breakers stop hammering dead providers.
- Feature flags and cached fallbacks keep the app usable.
- Every integration call is audited.
GIS tiles
- Basemap tiles are proxied by the backend so provider keys never reach a device.
- Public CARTO fallback keeps maps working with no provider configured.
CI / CD & Delivery Pipeline
Every change travels the same automated path from commit to production — with contract-first APIs, per-repository quality gates and image-based delivery.
Three repositories, one contract
- TAEDS-Platform — docs, constitution, decisions.
- TAEDS-Backend — Laravel + OpenAPI contract.
- TAEDS-Frontend — React PWA; client generated from the contract.
Quality gates
- Backend:
pint · phpstan · test · arch. - Frontend:
typecheck · lint · format · vitest · build · Playwright. - CI refuses to lie — a change is done only when gates pass.
Delivery
- GitHub Actions publish
ghcr.ioimages on main (+ sha tags). - Production stack pulls images behind an nginx gateway; dev stack hot-reloads bind mounts.
- Environment-specific config comes from env/secret management — never the repo.
Observability, Operations & Disaster Recovery
Well-behaved containers, queue workers, healthchecks and a backup ladder — with restore paths tested on a schedule.
What runs
- Containers: app · frontend · redis · pgsql(postgis) · minio · scheduler · horizon · mailpit.
- Queue workers drain outbox; scheduler runs housekeeping.
Backup ladder
- Daily/weekly/monthly PostgreSQL dumps + volume snapshots.
- Evidence objects replicate to an offsite/cold tier.
- Restore drills follow the runbook on a fixed schedule.
Observability
- Requests carry a
request_idend-to-end. - Healthchecks gate compose dependencies (pgsql, redis, etc.).
- Logs are per-channel/daily; operations runbook steps ship with the code.
Design Decisions — why each technology was chosen
Every significant choice below was made against real alternatives, weighted for a field-first, offline-capable, security-critical national system run by a small but senior team. Choices are defended here so a reviewer can challenge them on merit — and revisit them as the system grows.
Backend, data & infrastructure decisions
| Decision | Chosen | Alternatives weighed | Why this won | Revisit when… |
|---|---|---|---|---|
| Language & framework | PHP 8.5 + Laravel 13 | Node/NestJS · Java/Spring · Go · Python/Django · C#/.NET | Very fast, safe-by-default MVC; first-class ORM + migrations + queues; large talent pool; strong typed tooling (PHPStan) gives near-static guarantees without forfeiting velocity. Team's core skill. | If polyglot services appear and PHP talent thins. |
| Application topology | Modular monolith | Microservices · serverless · classic single app | One accident = one transaction across many tables — no distributed saga. One build/deploy keeps ops trivial; strict module boundaries give service-like discipline without the operational tax. Serverless adds vendor lock & cold-start pain for a government data platform. | If any single module needs independent scaling or a separate release train. |
| Database engine | PostgreSQL 18 + PostGIS | MySQL/MariaDB · MS SQL · Oracle · MongoDB | ACID reliability + rich constraints; excellent spatial support (PostGIS) for crash geometry; JSONB for flexible form payloads; open source with mature ops. MongoDB lacks the relational integrity a case file demands; MySQL spatial is weaker. | Never — this is the source of truth. Extend it, don't replace it. |
| Primary keys | ULIDs (sortable) internally; human numbers externally | UUIDv4 · auto-increment int · natural keys | ULIDs are globally unique for offline-first sync, sortable by time (useful for cursors), and unguessable — no ID enumeration. Auto-increment leaks volume and breaks offline creation. | — |
| Authentication | Laravel Sanctum cookie sessions + CSRF | Pure stateless JWT · Passport/OAuth2 · API keys | Cookies are revocable server-side instantly and avoid long-lived bearer tokens on devices; same-site policy + CSRF protect browser flows. Sessions verified against the server before any local session is trusted. | If machine-to-machine partners need OAuth2 — add an OAuth bridge, keep sessions for humans. |
| Cache, sessions, queues | Redis (+ Laravel Horizon) | RabbitMQ · Kafka · Beanstalkd · in-memory | One well-understood store covers cache, sessions and a reliable queue; Horizon gives a dashboard and supervised workers. Kafka is overkill at this volume. | If event volume/ordering demands Kafka. |
| Search | PostgreSQL FTS (tsvector + GIN) | Elasticsearch/OpenSearch · Postgres trigram · hosted search | Search lives inside the source of truth — no second index to drift or operate, and an officer's cross-record search is served by ranked tsvector indexes (with trigram for prefix/typo tolerance). Elasticsearch adds a cluster for marginal gain at this stage. | If cross-field analytics search outgrows Postgres, bolt on a dedicated engine — data model is already engine-agnostic. |
| Object storage | MinIO (S3-compatible) | AWS S3 primary · local disk · Azure Blob | S3 API is the de-facto standard (swap to any provider later); self-hosted keeps evidence inside police infrastructure; local disk lacks durability & replication story. | If a national cloud/MoICT directive mandates a provider — switch driver, not code. |
| Containers & orchestration | Docker + Compose (dev & prod) | Kubernetes · Nomad · bare metal · serverless | Compose is the smallest thing that gives reproducible dev/prod parity on one host today; K8s would add a whole platform team. Container images are portable if we grow into K8s later. | When multi-host scaling / self-healing is required. |
| Image registry & delivery | GHCR immutable images + tags | Build-on-server · other registries | One immutable artifact per commit, signed in CI, tagged latest + short-sha — makes rollback a tag revert, not a code fix. | — |
Frontend & offline decisions
| Decision | Chosen | Alternatives weighed | Why this won | Revisit when… |
|---|---|---|---|---|
| Delivery form | Installable PWA (React) | Native Android/iOS · React Native · Flutter | One codebase for phone and desktop, zero app-store friction for officers, works offline via service worker + IndexedDB. Field devices are mixed Android; PWA covers them all and updates instantly. | If deep hardware (camera pipelines, offline maps at scale) demands native. |
| Frontend framework | React 19 + TanStack Start/Router | Vue/Nuxt · SvelteKit · Angular · Next.js | React's ecosystem and hiring pool; TanStack gives type-safe routing & SSR without framework lock-in; file-based routes keep large apps navigable. | — |
| Server state | TanStack Query | Redux Toolkit · SWR · manual fetch | Server cache, retries, invalidation and offline semantics out of the box; Redux adds boilerplate for little gain at this scale. | If complex client state grows beyond forms/UI. |
| Forms & validation | react-hook-form + Zod | Formik + Yup · plain controlled inputs | RHF is fast and low-re-render; Zod gives a single schema shared conceptually with the API contract for runtime validation at trust boundaries. | — |
| Offline store | Dexie (IndexedDB) | localStorage · sql.js/WASM SQLite · PouchDB/CouchDB | IndexedDB is durable and queryable; Dexie is a thin, typed layer. localStorage is synchronous, tiny and loss-prone; SQLite-in-browser needs WASM shipping; PouchDB drags a sync server we don't need (we own the API). | If we need complex local joins, re-evaluate sql.js. |
| Sync strategy | Durable outbox + idempotency; server authoritative | CouchDB replication · CRDTs · naive replay | CRDTs are clever but foreign to a police DB and risk confusing audit trails. A deterministic outbox with idempotency keys gives "replay never duplicates" semantics with full control and auditable server-side events. | — |
| API contract & client | OpenAPI + generated client (Orval) | Hand-written TS client · GraphQL · tRPC | OpenAPI is the single contract both sides trust; codegen removes drift. GraphQL shifts complexity and complicates caching/permissions for little benefit here. | — |
| Styling | Tailwind 4 + shadcn/ui | CSS Modules · styled-components · hand-rolled design system | Design-token driven, small, accessible primitives; full control without fighting a UI kit. Enforced by design tokens (no raw colours in components). | — |
Engineering process decisions
| Decision | Chosen | Why |
|---|---|---|
| Repository layout | Three repositories (platform / backend / frontend) | A clean seam at the API contract: docs & decisions, server code, and client code each release on their own cadence without dragging unrelated diffs. |
| Testing layers | Pest/PHPUnit · Vitest + RTL · Playwright E2E · architecture tests | Each layer protects a different regression: unit for rules, feature/HTTP for API behaviour incl. RBAC boundaries, E2E for the officer journey, architecture tests to keep module boundaries honest. |
| Static analysis | PHPStan (Larastan) + TypeScript strict | Catch whole classes of bugs before they run — essential when a field officer may be offline and can't be patched live. |
| Code style | Pint · ESLint · Prettier | Machines, not meetings, decide formatting; PRs review logic. |
| Change control | Short branches → CI gates → merge to main → publish | Every merge is green before it ships; production is always a known, reproducible commit. |
Data Model — schema, rules, normalization & indexes
How the police case file becomes a relational database: one accident fans out into vehicles, drivers, casualties, witnesses, evidence and history — each owning table designed to stay in third normal form unless there is a tested reason to denormalise.
Keys & identity rules
- Internal keys are ULIDs (globally unique, sortable, unguessable) — required for offline creation.
- Human-facing identifiers:
TAEDS-ACC-2026-000123generated server-side from a per-year sequence. - Every row that is "owned" records
created_by/updated_byfor accountability.
Normalization stance
- Targets 3NF: facts live once; vehicles aren't repeated inside casualty rows.
- Example: accident → vehicles → drivers(1:1 to vehicle); accident → casualties; accident → witnesses.
- Small lookup lists (road surface, weather, lighting, offence) are reference data, referenced by FK — not free text.
Intentional denormalization
- Read-optimised aggregate snapshots (
analytics_snapshots,forecast_data) computed on a schedule from the source of truth. - Counts shown on cards are computed in queries (with eager-loading), not stored and allowed to drift.
- JSONB used only for genuinely flexible payloads (form extras), never for core relations.
Core tables & their purpose
| Module | Table (principal) | Purpose / notable fields |
|---|---|---|
| Accidents | accidents | case_number, status, severity, accident_date_time, station_id, created_by, version, sync_status |
| Accidents | accident_locations / _road_conditions / _weather | road, geometry (PostGIS point), surface/condition, weather/illumination/alcohol |
| Accidents | accident_vehicles → accident_drivers | vehicle & owner data; driver 1:1; licence details |
| Accidents | accident_casualties | class of road user, injury severity (fatal/serious/slight), safety measures |
| Accidents | accident_witnesses | statement data at the scene |
| Accidents | accident_status_history | append-only state changes: from, to, by, at, comment (the on-screen timeline) |
| Forms/files | initial_reports · situation_reports · crime_incident_reports · investigation_diaries · investigation_conclusions · sketches · crf_records … | digitised A–J file sections as structured rows |
| Evidence | evidence | type, status, captured_at, sha256_hash, sha256_verified, storage_key, classification |
| Evidence | evidence_chain_of_custody | append-only events (who / when / ip / action / details) |
| Enforcement | offences · traffic_citations · bonds · cash_bail_receipts · court_cases | offence catalogue, citations, money tracks, court flow |
| Administration | users · roles · role_permission · permissions · police_stations · organizations · reference_data | identity, RBAC, geography, lookups |
| Platform | outbox_events | durable side-effect queue written in the same transaction as the change |
| Platform | sync_cursors · sync_conflicts | device sync state; deterministic conflict ledger |
| Platform | audit_logs | immutable security & action trail |
Schema rules & integrity constraints
Constraints
- NOT NULL on mandatory fields; CHECK/enum-like constraints for statuses, severities, injury classes.
- Foreign keys everywhere relations exist (no orphaned children); deletions are restricted, not cascaded, for case data.
- Unique indexes: case_number, per-year sequence, share tokens, citation references.
Integrity by workflow
- State transitions are legal only through backend Actions — not raw SQL or free-form edits.
- Status history + audit write in the same transaction as the change.
- Evidence rows cannot be overwritten silently; changes are new chain events.
Indexing policy
- PKs/FKs indexed; covering indexes on list filters (status, severity, station, date).
- GiST spatial indexes on geometry; GIN
tsvectorindexes power full-text search; GIN trigram indexes add prefix/typo tolerance. - Queries eager-load relations to avoid N+1; lists paginate server-side with stable ordering.
Migrations & evolution
- Expand / contract phases; destructive steps require explicit approval.
- Migrations are versioned and run forward-only in pipelines (rollback = deploy old app + forward fix).
- Dates stored in UTC; human formatting happens at the edge.
vehicle_ref. Editing one vehicle's registration fixes every casualty that references it, and there is exactly one place a fact lives.CI / CD in Depth — from commit to production
Continuous integration turns every pull request into a verified build; continuous delivery turns every main merge into a published, deployable image. This section explains the pipeline job by job, and how environments, migrations and rollback actually work.
CI — integration
- Triggers: every pull request and push to
odhiamboormain. - Backend job:
pint(style) →phpstan(static analysis) → Pest feature/unit tests → architecture tests → route/contract sanity. - Frontend job:
typecheck→lint→format check→ Vitest → productionbuild→ Playwright end-to-end against a preview. - A red gate blocks the merge. CI never lies to you.
CD — delivery
- Publishing runs only on
main(or an explicit manual trigger). - Docker images built from the same commit and pushed to GHCR:
latest+ short-sha tags, with build cache. - Production pulls the new image, runs forward migrations, restarts app + workers, then health-checks.
- Rollback = point compose at the previous sha tag and redeploy.
Contract-first seam
- The OpenAPI spec is the contract between backend and frontend.
- Frontend regenerates its typed client from the contract; never hand-edits generated code.
- Both sides validate the contract in CI so an API change can't silently break the UI.
Environments & configuration
| Environment | Runs from | Config highlights | Who uses it |
|---|---|---|---|
| Developer | Docker Compose, hot-reload of source | debug on, Mailpit for mail, seeded reference data, pilot scope toggleable | Engineers |
| Staging / demo | Published images (or compose) against staging env | realistic data, production-like behaviour, no debug | Reviewers, demo, training |
| Production | Published images behind an nginx gateway | debug off, real SMTP, TLS, backups on, no demo-account fallback | Live users |
Threat Model & Controls
A national police record system holds sensitive personal data and court-relevant evidence. Using the STRIDE lens, each threat class is mapped to the concrete control that mitigates it — and to the test that proves it.
| Threat (STRIDE) | Real-world risk here | Control | Proven by |
|---|---|---|---|
| Spoofing identity | Someone acts as an officer or commander | Sanctum sessions, server-side session verification, CSRF protection, role-scoped login | Auth feature tests + session-verify flow |
| Tampering | Evidence photo or record altered after capture | SHA-256 of real bytes + append-only chain of custody + verify action | Evidence integrity tests |
| Repudiation | Officer denies approving / editing a case | Audit log + status history written atomically with each change | Audit & workflow tests |
| Information disclosure | Officer reads another station's cases | Backend data-scope enforcement (self/station/county/region/national) on every query | RBAC boundary tests (HTTP-level) |
| Denial of service | Abuse of public endpoints / tiles | Rate limits, request timeouts, bounded per_page, caching, healthchecks | Load & validation tests |
| Elevation of privilege | Officer approves their own case; sub-entity edits cross scope | Segregation of duties on approval; scope checks on all sub-entity routes (fixed + regression-tested) | RBAC conformance suite |
| Web app abuse | XSS, CSRF, SQL injection, mass assignment | Framework escaping + CSP posture, CSRF tokens, prepared statements/ORM, explicit fillable lists | Framework + OWASP-oriented review |
| Secret leakage | Keys shipped in repo or client bundle | No secrets in code; tile/API keys server-side only; CI secret scanning posture | Repo hygiene + env model |
| Data loss | Server dies / disk fails / human error | Postgres dumps + volume snapshots, object replication to cold tier, tested restore drills | DR runbook + scheduled drills |
Appendix — Modules, RBAC, Endpoints & References
Self-contained cheat-sheet tables — glossary, digitised forms, key endpoints, and a map to the deep-dive chapters of this manual.
Digitised forms (A–J)
- A accident package · C medical & damage · D statements & parade.
- E accused statement · F diary · G prosecution package.
- H evidence/admin · I summary/findings · misc records.
- Each form maps to structured rows — fill / view / approve by role.
Key endpoints (contract-first)
POST /auth/login · GET /auth/me · POST /auth/logoutGET|POST /accidents · PATCH /accidents/{id}/submit · /approve · /reject · /return-for-correction · /escalate/cases/{id}/evidence … /verify · /approve · /chain/enforcement/* · /gis/tiles · /admin/* · /audit
Glossary
- P41, P3, P81A, P32, P2020A — police/medical form codes digitised.
- OB occurrence book · CR crime register · CRF case record file.
- Case file A–J — the bundle one accident generates.
- Outbox — durable queue replayed to the server.
- RBAC / SoD — role-based access · segregation of duties.
- PWA / Dexie — installable app · offline IndexedDB.
| Deep-dive chapter | Where in this manual |
|---|---|
| Why we chose each technology (Laravel, React, PostgreSQL, Redis, MinIO, Docker…) | § 12 — Design decisions & rationales |
| Database schema, tables, keys, constraints, normalization and indexing rules | § 13 — Data model deep dive |
| Continuous integration & continuous delivery in depth (jobs, gates, images, envs, rollback) | § 14 — CI/CD in depth |
| Threat model, defences and how each risk is controlled | § 15 — Threat model & controls |