TAEDS Kenya National Police Service
Platform Architecture · Reference Manual · v1.1 ↑ Overview
Architecture Principles

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.

Officers Accident Records Evidence Case Workflow Dashboards Backups Audit Trail Business Data
Status of this document. TAEDS is under active development. This manual describes the current architecture and the reasoning behind it; specific tables, endpoints, tool versions and pipeline steps will continue to change as the system matures toward national rollout. Diagrams are conceptual snapshots, not pixel-perfect renderings of the running code. Treat anything here as a living specification — the principles in § 12–15 are the stable part.

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.

Reference Diagram 01 · System Stack

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.

01
Live data path
Derived reads (Redis cache)
Worker / sync path
Runs in our own infrastructure
Every client request lands on the same spine: UI → Gateway → Laravel → one PostgreSQL transaction → a durable outbox event. Redis and MinIO only ever serve derived data; full-text search runs inside PostgreSQL itself.

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.
Reference Diagram 02 · Modular Backend

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.

02
Request path (mutations)
Module boundary

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.
ModuleOwns (tables / responsibilities)Primary consumers
Accidentsaccident, location, road conditions, vehicles, drivers, casualties, witnesses, status history, initial/situation/crime reports, inquest, diary, conclusionOfficers, investigators
Enforcementoffences, citations, inspections, verifications, court cases, bonds, cash bailEnforcement officers
Evidenceevidence, chain_of_custody, shared links, medical/parade sub-formsAll case roles
Reportingreports, schedules, correspondence, sketches, analytics viewsCommand, analysts
Workflowtransitions, comments, task queueInvestigators, commanders
GISmap tiles proxy, blackspots, boundaries, spatial queriesAll roles
IntegrationsNTSA / IPRS / payment circuit state, outbound callsBackend workers
Administration / Auth / Audit / Sync / Notifications / Analyticsusers, roles, permissions, stations, audit_logs, outbox, sync_cursors, notifications, dashboard aggregatesEveryone
Reference Diagram 03 · Persistence

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.

03
Object & document data
Derived reads (Redis cache)
Backup / archive
Source of truth

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.
Reference Diagram 04 · Frontend

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.

04
Feature modules
State strategy
Generated code (never hand-edited)

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 fetch in components — one central HTTP client.
  • Every screen models all states: loading · empty · offline · syncing · error · conflict.
iThe frontend mirrors permissions for UX. The backend remains the real security boundary — hiding a button is convenience, not protection.
Reference Diagram 05 · Synchronisation

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.

05
Offline queue
Replay / sync path
Server 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.
Reference Diagram 06 · Defense in Depth

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.

06
Perimeter
Identity & access
Data at rest

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.
RoleBackend codeData scopeHighlights
Traffic officertofficerselfcapture, complete, submit
Enforcement officerenforcement_officerselfcitations, money, court referrals
Station investigatorinvestigatorstationcase files, statements, sketches, CRF
Station / County / Regional commandercommander · county_commander · rcommanderstation · county · regionreview & approve, reports, users view
Data analystanalystnational (read)analytics, exports, forecasts
HQ administratorhqadminnationalapprovals, master data, audit
System administratorictadminnationalsettings, security, users
Reference Diagram 07 · Evidence Integrity

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.

07
Upload & verify path
Chain of custody

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.
Reference Diagram 08 · Case Workflow

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.

08
Happy path
Return
Escalate

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.
Reference Diagram 09 · External Integration

Integrations & Resilience

NTSA, IPRS, payments and messaging integrate through one failure-tolerant contract. No external provider is ever the source of truth.

09
Outbound via outbox
External provider

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.
Reference Diagram 10 · Release Process

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.

10
Deployment path
Rollback path (revert tag)

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.io images 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.
Reference Diagram 11 · Operations & Continuity

Observability, Operations & Disaster Recovery

Well-behaved containers, queue workers, healthchecks and a backup ladder — with restore paths tested on a schedule.

11
Primary stores
Backup / cold tier

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_id end-to-end.
  • Healthchecks gate compose dependencies (pgsql, redis, etc.).
  • Logs are per-channel/daily; operations runbook steps ship with the code.
Reference Manual · Deep Dive §12

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.

12

Backend, data & infrastructure decisions

DecisionChosenAlternatives weighedWhy this wonRevisit when…
Language & frameworkPHP 8.5 + Laravel 13Node/NestJS · Java/Spring · Go · Python/Django · C#/.NETVery 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 topologyModular monolithMicroservices · serverless · classic single appOne 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 enginePostgreSQL 18 + PostGISMySQL/MariaDB · MS SQL · Oracle · MongoDBACID 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 keysULIDs (sortable) internally; human numbers externallyUUIDv4 · auto-increment int · natural keysULIDs 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.
AuthenticationLaravel Sanctum cookie sessions + CSRFPure stateless JWT · Passport/OAuth2 · API keysCookies 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, queuesRedis (+ Laravel Horizon)RabbitMQ · Kafka · Beanstalkd · in-memoryOne 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.
SearchPostgreSQL FTS (tsvector + GIN)Elasticsearch/OpenSearch · Postgres trigram · hosted searchSearch 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 storageMinIO (S3-compatible)AWS S3 primary · local disk · Azure BlobS3 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 & orchestrationDocker + Compose (dev & prod)Kubernetes · Nomad · bare metal · serverlessCompose 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 & deliveryGHCR immutable images + tagsBuild-on-server · other registriesOne immutable artifact per commit, signed in CI, tagged latest + short-sha — makes rollback a tag revert, not a code fix.

Frontend & offline decisions

DecisionChosenAlternatives weighedWhy this wonRevisit when…
Delivery formInstallable PWA (React)Native Android/iOS · React Native · FlutterOne 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 frameworkReact 19 + TanStack Start/RouterVue/Nuxt · SvelteKit · Angular · Next.jsReact's ecosystem and hiring pool; TanStack gives type-safe routing & SSR without framework lock-in; file-based routes keep large apps navigable.
Server stateTanStack QueryRedux Toolkit · SWR · manual fetchServer 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 & validationreact-hook-form + ZodFormik + Yup · plain controlled inputsRHF is fast and low-re-render; Zod gives a single schema shared conceptually with the API contract for runtime validation at trust boundaries.
Offline storeDexie (IndexedDB)localStorage · sql.js/WASM SQLite · PouchDB/CouchDBIndexedDB 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 strategyDurable outbox + idempotency; server authoritativeCouchDB replication · CRDTs · naive replayCRDTs 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 & clientOpenAPI + generated client (Orval)Hand-written TS client · GraphQL · tRPCOpenAPI is the single contract both sides trust; codegen removes drift. GraphQL shifts complexity and complicates caching/permissions for little benefit here.
StylingTailwind 4 + shadcn/uiCSS Modules · styled-components · hand-rolled design systemDesign-token driven, small, accessible primitives; full control without fighting a UI kit. Enforced by design tokens (no raw colours in components).

Engineering process decisions

DecisionChosenWhy
Repository layoutThree 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 layersPest/PHPUnit · Vitest + RTL · Playwright E2E · architecture testsEach 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 analysisPHPStan (Larastan) + TypeScript strictCatch whole classes of bugs before they run — essential when a field officer may be offline and can't be patched live.
Code stylePint · ESLint · PrettierMachines, not meetings, decide formatting; PRs review logic.
Change controlShort branches → CI gates → merge to main → publishEvery merge is green before it ships; production is always a known, reproducible commit.
Net effect: boring, mainstream, well-documented technology that a public-sector operator can run for a decade — with the cleverness concentrated where it matters (offline sync, evidence integrity, RBAC), not in exotic tooling.
Reference Manual · Deep Dive §13

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.

13

Keys & identity rules

  • Internal keys are ULIDs (globally unique, sortable, unguessable) — required for offline creation.
  • Human-facing identifiers: TAEDS-ACC-2026-000123 generated server-side from a per-year sequence.
  • Every row that is "owned" records created_by / updated_by for 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

ModuleTable (principal)Purpose / notable fields
Accidentsaccidentscase_number, status, severity, accident_date_time, station_id, created_by, version, sync_status
Accidentsaccident_locations / _road_conditions / _weatherroad, geometry (PostGIS point), surface/condition, weather/illumination/alcohol
Accidentsaccident_vehiclesaccident_driversvehicle & owner data; driver 1:1; licence details
Accidentsaccident_casualtiesclass of road user, injury severity (fatal/serious/slight), safety measures
Accidentsaccident_witnessesstatement data at the scene
Accidentsaccident_status_historyappend-only state changes: from, to, by, at, comment (the on-screen timeline)
Forms/filesinitial_reports · situation_reports · crime_incident_reports · investigation_diaries · investigation_conclusions · sketches · crf_records …digitised A–J file sections as structured rows
Evidenceevidencetype, status, captured_at, sha256_hash, sha256_verified, storage_key, classification
Evidenceevidence_chain_of_custodyappend-only events (who / when / ip / action / details)
Enforcementoffences · traffic_citations · bonds · cash_bail_receipts · court_casesoffence catalogue, citations, money tracks, court flow
Administrationusers · roles · role_permission · permissions · police_stations · organizations · reference_dataidentity, RBAC, geography, lookups
Platformoutbox_eventsdurable side-effect queue written in the same transaction as the change
Platformsync_cursors · sync_conflictsdevice sync state; deterministic conflict ledger
Platformaudit_logsimmutable 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 tsvector indexes 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.
Worked example of 3NF: a casualty is not a column on the accident row, and its vehicle is not copied into the casualty row — the casualty stores a vehicle_ref. Editing one vehicle's registration fixes every casualty that references it, and there is exactly one place a fact lives.
Reference Manual · Deep Dive §14

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.

14

CI — integration

  • Triggers: every pull request and push to odhiambo or main.
  • Backend job: pint (style) → phpstan (static analysis) → Pest feature/unit tests → architecture tests → route/contract sanity.
  • Frontend job: typechecklintformat check → Vitest → production build → 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

EnvironmentRuns fromConfig highlightsWho uses it
DeveloperDocker Compose, hot-reload of sourcedebug on, Mailpit for mail, seeded reference data, pilot scope toggleableEngineers
Staging / demoPublished images (or compose) against staging envrealistic data, production-like behaviour, no debugReviewers, demo, training
ProductionPublished images behind an nginx gatewaydebug off, real SMTP, TLS, backups on, no demo-account fallbackLive users
iRules that keep delivery safe: secrets live in the environment/secret store, never in images or the repo · migrations run forward-only before the app rollout · every image is immutable and tagged · healthchecks gate restarts · no demo fallback path in production.
Reference Manual · Deep Dive §15

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.

15
Threat (STRIDE)Real-world risk hereControlProven by
Spoofing identitySomeone acts as an officer or commanderSanctum sessions, server-side session verification, CSRF protection, role-scoped loginAuth feature tests + session-verify flow
TamperingEvidence photo or record altered after captureSHA-256 of real bytes + append-only chain of custody + verify actionEvidence integrity tests
RepudiationOfficer denies approving / editing a caseAudit log + status history written atomically with each changeAudit & workflow tests
Information disclosureOfficer reads another station's casesBackend data-scope enforcement (self/station/county/region/national) on every queryRBAC boundary tests (HTTP-level)
Denial of serviceAbuse of public endpoints / tilesRate limits, request timeouts, bounded per_page, caching, healthchecksLoad & validation tests
Elevation of privilegeOfficer approves their own case; sub-entity edits cross scopeSegregation of duties on approval; scope checks on all sub-entity routes (fixed + regression-tested)RBAC conformance suite
Web app abuseXSS, CSRF, SQL injection, mass assignmentFramework escaping + CSP posture, CSRF tokens, prepared statements/ORM, explicit fillable listsFramework + OWASP-oriented review
Secret leakageKeys shipped in repo or client bundleNo secrets in code; tile/API keys server-side only; CI secret scanning postureRepo hygiene + env model
Data lossServer dies / disk fails / human errorPostgres dumps + volume snapshots, object replication to cold tier, tested restore drillsDR runbook + scheduled drills
Design posture: assume the worst — an attacker with a valid officer account, a lost device reconnecting late, a provider that is down. Every control above is enforced in the backend; the UI only mirrors it for usability.
Appendix · Reference Material

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.

A

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/logout
  • GET|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 chapterWhere 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
Non-negotiable rules (from the engineering constitution): security before convenience · PostgreSQL is the only truth · backend auth is the real boundary · offline-first in every feature · sync is durable and idempotent · evidence is evidential · OpenAPI is the contract · TypeScript strict · modules keep boundaries · no secrets in the repo.