System status
Session risk analysis for telemetry collected by a network appliance. The status below is read live from the API on every load: the request goes browser → web container → API → Postgres and Redis, so a green result means the whole chain is wired, not just that a process is listening.
Stack status
checking
- Environment
- …
- API version
- …
In place
- FastAPI serviceSettings, structured logging, error envelope, middleware stack
- Postgres + AlembicAsync SQLAlchemy 2, UUIDv7 keys, autogenerate wired to app.models
- Domain persistencesessions and events tables, DB-level cascade, demo stories via make seed
- Risk engineCategory-capped 0–100 scoring with per-user country baseline, computed on read
- Sessions APICRUD, search with filters / sort / pagination, stats with day-over-day, status counters and the distinct-user / event record counts (D-43, D-44) — every response carries the risk score
- Events APIAdd, list and delete nested under a session — immutable events, session-scoped deletes; GET /events cross-session feed joined to the parent's user and status (D-43)
- Users read modelGET /users per-user aggregates — session / flagged / dangerous counts, distinct countries and devices, last seen, page-bounded max risk (D-43) — with prefix search, four sort keys and a stable user_id tiebreak, plus GET /users/{user_id} for one user (D-44). Max risk is not a sort key: it is computed on read, so the API rejects it rather than scoring the whole table
- RedisRate-limit counters, OTP state, JSON cache helpers; Celery broker and result backend on DB 2
- MinIOS3-compatible bucket, created on API startup
- AuthSignup, email OTP, login, refresh rotation, logout
- Next.js + TailwindSame-origin API proxy, design tokens, shadcn-convention UI primitives
- Web auth & shellLogin for the seeded analyst, in-memory access token with silent refresh, app shell with light / dark / system theming
- Overview dashboardGreeting, four KPI cards with a vs-yesterday delta, recent sessions and a ring-gauge session-detail rail at / (D-42); every sidebar tab carries its live record count from the one shared stats read (D-44)
- Users surfaceKPI cards, URL-synced prefix search and sortable columns over the per-user aggregates at /users, and a detail route at /users/[userId] — profile, per-user KPI cards and every session that user has, each row clicking through to the session detail (D-44)
- Event feed surfaceType and exact-user filters, an order toggle and pagination over GET /events at /events, each row linking to both its parent session and the user (D-44)
- Sessions listKPI strip and dense table at /sessions (moved from / by D-42), URL-driven filters and sort through server-side search, a searchable ISO country picker that matches on name or code, hover copy on ids and IPs, load-more paging
- Session detailRisk breakdown with factor evidence and an evidence timeline at /sessions/[id] whose nodes expand to the whole stored event record (both ids, exact duration, occurred_at vs created_at, the full metadata blob), one node or every node at once; create / edit dialogs, add-event, inline event deletes and a cascade-counting session delete
- AI risk summaryPOST /sessions/{id}/ai-summary — layered prompt containment, content-addressed Redis cache, 5/min + 100/day tier, keyless stub provider; the advisory narrative card on the detail
- Data import & exportD-39 — filtered export to CSV/JSON, stored in the MinIO reports bucket and downloaded; all-or-nothing JSON import with row-indexed errors; Export / Import in the list toolbar
- Demo data generatorD-40 — POST /generator (1–500) on Celery over Redis DB 2, a sixth worker container, polled created/total progress and live list refresh in the Generate-data dialog
Future work — by choice
- SSE / WebSocket progress pushNamed future work — generator progress and list refresh are polled every 2 s by decision; a push channel is the natural upgrade