isidora
Observaobserva

Observa

Multi-tenant incident management with AI-generated dashboards

Personal project · 2026 · Full-stack
C#
.NET 10
Marten
Wolverine
PostgreSQL
SignalR
Google Gemini
React
TypeScript
Vite
TanStack Query
Tailwind CSS
Recharts
Docker
Caddy
Railway
Neon
GitHub Actions
Incidents & dashboards

Teams report incidents and move them through statuses and custom fields they define themselves, then ask a question in plain English and get back a dashboard of metric cards, charts, and tables over their own data — savable, re-runnable, and exportable as CSV or PDF.

Row-level security

Isolated every tenant with row-level security enforced by Postgres, not the query builder: the app connects as a NOBYPASSRLS role, policies cover both document and event tables, and a separate admin role applies schema through a migrate step that aborts if that role could bypass RLS.

AI dashboards

Kept the model out of the query path: Gemini fills in a declarative dashboard spec instead of writing SQL, a resolver validates every name against the tenant's own schema, and a compiler turns each widget into one parameterized statement run in a read-only, UTC-pinned transaction — so saved dashboards replay with no AI involved.

Guarded model cost with plan caching in HybridCache — the plan is cached, never the numbers — plus per-user and per-company daily allowances and IP-partitioned rate limiting.

Backend & realtime

Built the API in ASP.NET Core on .NET 10 with clean architecture and CQRS vertical slices — one file per use case carrying its command or query, validator, precondition, and handler — dispatched by Wolverine as mediator, with cookie auth over ASP.NET Core Identity on a Marten-backed user store.

Event-sourced incidents in Marten, document store and event store in one, so the audit trail is the source of truth: commands append to the stream, an inline projection folds it into the document queries read, and changes reach other browsers over per-tenant SignalR groups through Wolverine's outbox.

Frontend

Shipped the frontend in React 19 and TypeScript on Vite with TanStack Query driven by realtime cache invalidation, Tailwind v4 over Base UI primitives, Recharts widgets, Zod-mirrored API contracts, light/dark theming, and client-side CSV and PDF export captured from the rendered dashboard.

Deployment

Deployed both apps on Railway with Neon Postgres — the API as a multi-stage Docker image, the SPA behind Caddy proxying /api and /hubs over the private network for a single origin — with schema applied by an idempotent migrate release step, GitHub Actions CI, and 200+ unit tests.

observa

Track every incident. Ask any question.

Raleway
Instrument Serif italic
MeshGradient
Ink#3B3A39
Cream#F5F3F0
Blue#4392DE
BrowserReact SPA · SignalRRailwayira-webReact + Vite · static buildCaddy · single originira-backendASP.NET Core · WolverineMarten · SignalR hubHTTPS · one origin · cookie auth/api + /hubsprivate networkread only tx · app.tenant_idquestion · specNeon PostgreSQLMarten documents + eventsRLS · NOBYPASSRLS app roleGoogle Geminiplans a dashboard specnever authors SQL