Sense — Overview¶
Sense is the data-collection product. Field inspectors record video, GPS, and radio scans on a phone; supervisors plan their routes and review the results on a web map; an external ML system enriches the frames with detections.
What ships in Sense¶
| Repo | What it is |
|---|---|
axion.sense.backend |
Two .NET 10 binaries — Sense API (REST + gRPC) and Sense Worker (Kafka + Hangfire). |
axion.sense.web |
Planner web app (React 19 + MapLibre + PMTiles). |
axion.sense.contracts |
Protobuf contracts (5 files, code-gen for Kotlin / Swift / C#). |
Subsystems¶
flowchart LR
subgraph mobile["Sense Mobile"]
record[Record video]
slice[Slice frames]
sensors[GPS / WiFi / cell]
end
subgraph planner["Planner Web"]
plan[Plan territories]
assign[Assign tasks]
review[Review coverage]
end
subgraph api["Sense API"]
rest[REST controllers]
grpc[gRPC services]
end
subgraph worker["Sense Worker"]
consumers[Kafka consumers]
jobs[Hangfire jobs]
end
mobile -- gRPC --> api
planner -- REST --> api
api -- Kafka events --> worker
worker -- FrameRecognitionRequest --> vision[Vision pipeline]
vision -- detections in ClickHouse --> worker
vision -- TrackMatchingResult --> worker
Vision is a separate Python service stack (Detections API / Worker / Quality / Clusterization / Matching) that owns the ML round-trip. It writes detection rows into the shared
axion_sense.detectionstable directly — no Kafka roundtrip back to Sense. See Frame upload + ML flow for the full picture.
Responsibilities¶
| Subsystem | Owns | Doesn't own |
|---|---|---|
| Sense Mobile App | Capture loop, frame slicing, offline queue, presigned uploads, detour task sync. | Detection logic, route planning. |
| Planner Web App | Territory and task UX, coverage map rendering, user/role admin. | Mobile workflows, analytics. |
| Sense API | Request handling, authn + authz, transactional state in Postgres, presigned URL issuance, Kafka publish on commit. | Long-running work, batch ingestion, ML round-trip. |
| Sense Worker | Kafka consumption, Hangfire scheduling, ClickHouse batch writes, PMTiles generation, Citylens sync, audit batching. | Synchronous user requests. |
Key architectural choices¶
- Dual-port API. REST (8080) for the web app; gRPC (8081) for the mobile app. See ADR-0002. The dual port avoids HTTP/1 vs HTTP/2 quirks in the same listener and lets us wire different middleware stacks per protocol.
- Mobile uploads frames directly to S3. API issues a presigned URL; mobile PUTs the bytes; only metadata flows through the API. This is the single most important throughput decision in Sense.
- Async-by-default. Anything that can wait — ML round-trip, audit aggregation, PMTiles generation, coverage refresh — happens in the Worker. The API's job ends when the data is durable.
- Two persistence stores by purpose. Postgres for transactional state (users, orgs, tasks, mappings); ClickHouse for high-volume immutable data (tracks, frames, detections, audit). See ADR-0004.
- Hangfire on Postgres. Same operational footprint as the API; no extra infra. See ADR-0003.
- OpenFGA for authz. Multi-tenant + relationship checks are awkward in plain RBAC. See ADR-0005.
Where to go next¶
- L2 containers of Sense: Sense Containers
- L3 components: Sense API · Sense Worker
- Data model: Data Model
- Wire contracts: Contracts
- Flagship flow: Frame upload + ML