# Architecture Overview ## System Purpose Real-time visualization of APRS (Automatic Packet Reporting System) packets on an interactive map. Tracks amateur radio stations, weather data, and station telemetry via APRS-IS network feed. ## Technology Stack | Layer | Technology | |---|---| | Language | Elixir 1.17+ | | Web Framework | Phoenix 1.8 + LiveView 1.2 | | Database | PostgreSQL + PostGIS | | APRS Parsing | `aprs` (Gleam BEAM library) | | Map Client | Leaflet (lazy-loaded) | | Charts | Chart.js (lazy-loaded) | | Metrics | PromEx + Telemetry | | Deployment | Kubernetes (ArgoCD), Fly.io, Docker | ## High-Level Data Flow ``` APRS-IS Network (TCP) │ ▼ Aprsme.Is (GenServer — TCP connection manager) │ raw APRS frames parsed via aprs.parse/1 ▼ PacketProducer (GenStage — water-mark backpressure) │ buffers incoming packets, signals backpressure upstream ▼ PacketConsumer pool (GenStage consumers) │ sanitize → normalize → insert into Postgres │ broadcast to PubSub ├──► Postgres packets table (daily-partitioned) └──► Broadcasting ├── SpatialPubSub (viewport-filtered, grid-indexed) ├── StreamingPacketsPubSub (ETS-based, bounds-filtered) └── Phoenix.PubSub (per-callsign topics) │ ▼ LiveView clients (MapLive, PacketsLive, InfoLive) Mobile WebSocket clients (MobileChannel) ``` ## Supervision Tree ``` Aprsme.Application ├── Aprsme.Repo ├── Phoenix.PubSub ├── Aprsme.Cache / DeviceCache / WeatherCache / RegexCache ├── Aprsme.RateLimiter ├── Aprsme.PacketPipelineSupervisor │ ├── PacketProducer │ └── PacketConsumerPool │ └── PacketConsumer (×N) ├── Aprsme.BroadcastTaskSupervisor ├── Aprsme.Is.IsSupervisor │ └── Aprsme.Is ├── Aprsme.SpatialPubSub ├── Aprsme.StreamingPacketsPubSub ├── Aprsme.CleanupScheduler ├── Aprsme.PartitionManager ├── Aprsme.PostgresNotifier ├── Aprsme.ConnectionMonitor ├── Aprsme.SignalHandler ├── Aprsme.ShutdownHandler ├── Aprsme.PromEx ├── Cluster modules (optional, via libcluster) └── AprsmeWeb.Endpoint (Bandit) ``` ## Key Design Decisions ### Database Partitioning The `packets` table is **daily-partitioned** via `PartitionManager`. High ingestion volume makes partitioning essential for query performance and retention cleanup. ### Spatial Filtering Rather than broadcasting every packet to every client, `SpatialPubSub` maintains a 1° grid spatial index. Clients register their map viewport bounds; only packets intersecting those bounds are delivered. ### Backpressure `PacketProducer` uses water-mark backpressure (>80% buffer → pause TCP; <30% → resume) to prevent memory exhaustion under load spikes. ### Symbol System APRS symbols are rendered client-side from sprite sheets. `AprsSymbol` provides server-side symbol table lookups for info pages. ### Clustering Optional multi-node deployment via `libcluster`. Uses leader election to designate the node that maintains the APRS-IS connection. Packets are relayed to other nodes via `PacketDistributor`. ### Lazy Asset Loading The map bundle (Leaflet + plugins) and chart bundle (Chart.js) are loaded on-demand only for pages that need them, keeping initial page load small.