Skip to content

Architecture Diagrams

The canonical technical diagrams. Rendered as monospace blocks so they stay correct in every theme and copy cleanly.

System architecture

text
                    ┌───────────────┐
                    │   Dashboard   │   React/Vite bundle (dashboard + Training Center)
                    └───────┬───────┘
                            │  credentialed fetch (HttpOnly session cookie)
                    ┌───────▼───────┐
                    │   Flask API   │   dps-code-api (Render) — routes, gates, validation
                    └───────┬───────┘
                            │  service-role Supabase client
                    ┌───────▼───────┐
                    │    Services   │   domain rules, shared operations, serialization
                    └───────┬───────┘
                            │  named RPC calls / gated table access
                    ┌───────▼───────┐
                    │ RPC / DB Auth │   transactional functions, row locks, RLS, grants
                    └───────┬───────┘

                    ┌───────▼───────┐
                    │  PostgreSQL   │   Supabase — single source of truth
                    └───────┬───────┘

                 ┌──────────┴──────────┐
                 │                     │
           Application Data       Audit / Events
           (reports, agents,      (timeline, events,
            queues, training)      admin_audit_log)

Side channels: the Discord bot (BotGhost) talks to the API with the shared API key; Discord OAuth flows browser → API → Discord; the ER:LC relay bridges the API to the Roblox game-server API; Supabase Realtime broadcasts training events to browsers (best-effort synchronization only).

Request lifecycle

text
Request
 → Authentication      browser session cookie (live agents row) or API key
 → Authorization       clearance / rank / allowlist / assignment
 → Validation          fields, enums, URLs, bounds, reasons
 → Service             domain rules, shared operation
 → Authoritative DB Operation   named RPC (one transaction) or gated table write
 → Audit/Event         timeline + canonical event in the same transaction
 → Response            serialized, identifier-scrubbed payload

Investigation lifecycle

text
                    ┌─────────────────────────────────────────────┐
                    │  Open / Pending (not started)               │
                    └──────────────────┬──────────────────────────┘
                                       │  action=investigate
                                       │  (reopen of Closed/Completed/
                                       │   Validated/Invalidated needs
                                       │   clearance 4+)

                    ┌─────────────────────────────────────────────┐
                    │  Under Investigation                        │
                    │  · notes / evidence / timeline / contact    │
                    └───────┬──────────────────┬──────────────────┘
                            │                  │
              action=validate            action=invalidate
                            │                  │
                            ▼                  ▼
                    ┌──────────────┐   ┌───────────────┐
                    │  Validated   │   │  Invalidated  │
                    └──────────────┘   └───────────────┘
                            │                  │
                            └───────┬──────────┘
                                    │  both: close open contact thread,
                                    │  queue bot action, emit
                                    │  report.investigation_concluded

                    Under Investigation ──(conclude)──► Pending
                    (no bot work; timeline + event only)

Report action side effects

text
POST /reports/<id>/action  →  perform_report_action  →  rpc_report_action

                    ┌─────────────────────────────────────┼────────────────────┐
                    ▼                                     ▼                    ▼
        status / assignment update            timeline row(s)        canonical event
        (under FOR UPDATE lock,              ("Report validated",   (report.* / contact.*,
         guarded against races)               "Investigation begun",  same transaction)
                                              "Case claimed by agent")

                    ├──► contact_messages row      (contact_reporter only)
                    └──► pending_actions row       (bot work; not for conclude)

Training session lifecycle

text
pending ──(start)──► active ⇄ (pause/resume) ──(end)──► completed
    │                    │                                 ▲
    │                    ├──(scenario activate)──► child: waiting → active → completed
    │                    │
    └──(abort)──────────► aborted     (trainee sees the no-active state)

Realtime synchronization invariant

text
API/database mutation → persistent state/event → best-effort Realtime broadcast
→ recipient receives event → authoritative GET/reconciliation

A missed broadcast never corrupts UI state: pages reconcile with an authoritative GET after connect and after event bursts.