Skip to content

Sense — Containers (L2)

The Sense slice of the platform-level container diagram, with notes on each box.

The full Platform Containers diagram covers all of Sense — this page reuses it but narrates only the Sense-relevant containers.

Container Diagram

Frontend

Sense Mobile App

  • Tech: Kotlin (Android) and Swift (iOS) native — no shared cross-platform framework.
  • Storage: Local SQLite for offline queue (track session, pending frame uploads, cached detour tasks).
  • Talks to: API Gateway via gRPC (HTTP/2). S3 directly for frame PUT.
  • Auth: OIDC client; refresh tokens stored in keystore/keychain.
  • Repo: separate mobile repos (not in scope of this site).

Planner Web App

  • Tech: React 19, TypeScript 5.7, Vite 6, TanStack Router, TanStack Query, Zustand. MapLibre GL JS 5.18 + Deck.gl + PMTiles.
  • API client: generated by Orval from the .NET Swagger document (localhost:5501/swagger/v1/swagger.json). Generated hooks live under src/shared/api/generated/. Custom Axios mutator at src/shared/api/client.ts injects the bearer token and silently refreshes on 401.
  • Architecture: Feature-Sliced Designapp / pages / widgets / features / entities / shared.
  • Repo: axion.sense.web.

Backend

Sense API

  • Tech: .NET 10, ASP.NET Core. Two ports on one process: REST on 8080 (HTTP/1.1) and gRPC on 8081 (HTTP/2).
  • Auth:
  • OIDC JWT (primary) — RFC 7523, 5-minute clock skew tolerance.
  • API key (X-Api-Key) for service-to-service.
  • A policy scheme picks the right authenticator per request.
  • Authz: IPermissionService wrapper around the OpenFGA C# SDK. Uses BatchCheck for list endpoints.
  • Migrations: dotnet Axion.Sense.Api.dll --migrate (one-shot K8s job).
  • Repo: axion.sense.backend/src/Axion.Sense.Api.
  • Components: Sense API L3.

Sense Worker

  • Tech: .NET 10, KafkaFlow 3.1, Hangfire 1.x.
  • Ports: 8080 (health, OIDC for Hangfire dashboard), 8888 (Hangfire Dashboard UI).
  • Migrations: dotnet Axion.Sense.Worker.dll --migrate — migrates axion_sense_tasks and ensures Kafka topics.
  • Repo: axion.sense.backend/src/Axion.Sense.Worker.
  • Components: Sense Worker L3.

Why two binaries (and not one)

The classic .NET-on-Postgres pattern is to host background workers inside the same process as the API, gated by an environment flag. We don't, because:

  1. Different scaling axes. API scales with concurrent users; Worker scales with Kafka throughput and Hangfire job pressure. Mixing them means over-provisioning one to feed the other.
  2. Different failure modes. A KafkaFlow consumer hot-loop or a long-running tippecanoe shell-out should never block API request threads.
  3. Different deployment cadences. Worker can roll independently to drain queues without affecting user-facing latency.

The price: a shared Axion.Sense.Core project with HostBinding markers so DI registrations target API or Worker correctly. Worth it.

Where to go next