Architecture
V53 #1..#4 ──(1NCE LTE, outbound TCP, JT/T 808)──▶ router NAT :8888
│
┌─────────────────▼──────────────────┐
│ Kubernetes namespace `phimi` │
│ │
│ phimi-ingest (Rust/Tokio) │
│ TCP listener · decoder · ACK │
│ sessions · downlink commands │
│ │ events │
│ ▼ │
│ phimi-core (validation, state, │
│ geofences, alerts) │
│ │ │
│ ▼ │
│ PostgreSQL + PostGIS MongoDB │
│ positions, accounts raw frames │
│ ▲ │
│ phimi-api (Axum: REST, WebSocket) │
│ ▲ │
│ phimi-ui (Next.js dashboard) │
└────────────────────────────────────┘
Decisions
PostgreSQL + PostGIS for positions. Geofences, distance checks (ST_DWithin
compares in metres on geography and uses GiST indexes) and time-range history are
relational, spatial queries. PostGIS fits them better than MongoDB. The existing
cluster PostgreSQL already ships the PostGIS extension.
MongoDB is optional. It can keep raw frames for diagnostics and replay, but the map never depends on it.
No Kafka, Redis or microservice mesh for the MVP. Three application services are
enough for four trackers: ingest, API and UI. phimi-core is a library today and can
become its own process later.
The protocol crate stays pure. phimi-protocol has no Tokio, Axum or database
dependency. It only encodes and decodes, so it is trivially testable and the simulator
uses exactly the same code as the server.
One ingest replica. A tracker's TCP connection lives in one pod, and downlink
commands must reach that pod. With several replicas, sessions need a shared registry
and command routing. Until then phimi-ingest runs with replicas: 1 and the
Recreate strategy.
The tracker endpoint is raw TCP, not HTTP. It cannot go through an HTTP Ingress. On this single-node bare-metal cluster there is no LoadBalancer controller, so the service is a NodePort (30888) and the router forwards public TCP 8888 to it.
Security posture
JT/T 808 from the V53 is not encrypted. Phimi therefore:
- treats every frame as untrusted: size limit, escape and length validation, XOR checksum;
- accepts registration only from allowlisted terminal ids;
- issues per-terminal auth codes derived from a secret (HMAC-SHA256), and ignores reports until a terminal authenticates;
- limits concurrent connections and closes idle ones;
- never logs coordinates, and logs only the last four digits of a terminal id;
- keeps metrics and session listings on a cluster-internal port.
User authentication for the dashboard is independent of device identity and arrives with the API in phase 4.