# Backend architecture ## Process model `src.main` constructs one FastAPI application with `root_path="/api"`. During startup it stores the running asyncio loop in `src.shared_objects`, creates an asyncpg `DatabasePool`, attaches a packet broadcaster to it, creates a second network-state broadcaster, and drains any capture records buffered before the DB became available. Shutdown stops capture, network resources, telemetry, tshark, and the packet tracker; then closes WebSocket broadcasters and the DB pool. ```mermaid flowchart LR UI[Frontend/client] --> API[FastAPI /api] API --> NET[Network and bridge API] API --> CAP[Sniffer API] API --> FW[Firewall API] API --> SCR[Script API] CAP --> NS[network_sniffer] NS --> PT[PacketTracker] EBPF[tc/eBPF telemetry process] --> PT NS <--> TS[tshark workers] PT --> DB[(PostgreSQL packets)] DB --> PB[PacketBroadcaster] PB --> WS1[Packet WebSocket] NET --> NB[Network broadcaster] NB --> WS2[Network WebSocket] API --> DB ``` ## Component boundaries | Component | Responsibility | Persistent state | Important side effects | | --- | --- | --- | --- | | `main.py` | app construction and lifecycle wiring | shared object references | starts/stops resources | | `api/` | validates requests and presents HTTP/WebSocket contracts | none by default | may alter Linux networking, nftables, or services | | `network_sniffer.py` | owns capture sessions and AF_PACKET sockets | in-process session map and pre-DB buffer | raw sockets, reader threads | | `packet_tracker.py` | merges capture and telemetry observations | bounded in-memory pending entries | asynchronous database persistence | | `database.py` | packet upsert/retrieval and SQL analysis | PostgreSQL `packets` table | WebSocket publication after single-row upserts | | `tshark_manager.py` | optional application-protocol enrichment | worker and metadata caches | `tshark` subprocesses/threads | | `bridge_telemetry.py` and `ebpf_bridge_events.py` | bridge tc/eBPF event collection | subprocess state and event queue | compiles/attaches tc programs | | `bridge_link_state_manager.py` | optionally propagates member failure/recovery state | watcher registry | link and Ethernet-profile changes | ## Shared runtime state `shared_objects.py` intentionally holds process-wide references rather than using request-scoped dependency injection: - `db`: initialized `DatabasePool`, or `None` after shutdown. - `web_loop`: FastAPI event loop used when worker threads need to schedule work. - `broadcaster`: packet update broadcaster. - `network_broadcaster`: network-state update broadcaster. Endpoints that require the database return HTTP 503 when `shared_objects.db` is unavailable. Worker components should tolerate the DB not being ready by buffering or logging failure, rather than assuming the application has fully started. ## Router mounting | Router module | Prefix added by `main.py` | Router-local prefix | Result | | --- | --- | --- | --- | | `network_api` | `/network` | none | `/api/network/...` | | `sniffer_api` | `/sniffer` | none | `/api/sniffer/...` | | `packet_api` | `/packets` | none | `/api/packets/...` | | `analysis_api` | `/analysis` | none | `/api/analysis/...` | | `nft_manager` | none | `/firewall` | `/api/firewall/...` | | `packet_scripting_api` | `/scripts` | `/scripts` | `/api/scripts/scripts/...` | The last row reflects the current code exactly. It is worth preserving this fact in examples until the duplicated prefix is deliberately changed.