3.5 KiB
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.
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: initializedDatabasePool, orNoneafter 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.