┌─────────────────────┐
│ Browser │
└──────────┬───────────┘
│ HTTP / WS
┌──────────▼───────────┐
│ Mimir Web (React) │
│ nginx :80 / :5173 │
└──────────┬───────────┘
│ REST API
┌──────────▼───────────┐
│ Mimir API (Hono) │
│ :3001 │
├───────────────────────┤
│ Workers: │
│ - Metrics aggregation│
│ - Alert evaluation │
│ - Cost calculation │
│ - Drift detection │
│ - Cron scheduler │
└──┬──────┬──────┬─────┘
│ │ │
┌──────────────▼┐ ┌──▼──┐ ┌▼──────────────────┐
│ TimescaleDB │ │Redis│ │ WebSocket Gateway │
│ (PostgreSQL) │ │ │ │ │
└────────────────┘ └──┬──┘ └────────┬───────────┘
│ pub/sub │ WS
│ ┌▼──────────────────┐
└──────────────│ Draupnir Agents │
│ (per instance) │
└───────────────────┘
mimir/
├── apps/
│ ├── api/ # Hono backend + Prisma + WebSocket gateway
│ └── web/ # React 19 frontend (Vite + TanStack Router)
├── packages/
│ ├── protocol/ # WebSocket protocol contract (channels, envelopes, types)
│ ├── shared/ # Shared TypeScript types
│ └── ui/ # UI component library
├── docker-compose.yml # Full stack (postgres, redis, api, web)
├── docker-compose.dev.yml # Dev overlay (debug logging)
├── turbo.json # Turborepo pipeline config
└── Makefile # Developer workflow targets
| Layer |
Technology |
Version |
| Framework |
Hono |
4.12 |
| ORM |
Prisma |
7.4 |
| Database |
TimescaleDB (PostgreSQL 16) |
— |
| Cache / Pub-Sub |
Redis (ioredis) |
7 |
| WebSocket |
ws |
8.18 |
| Validation |
Zod |
4.3 |
| Logging |
Pino |
10.3 |
| Runtime |
Node.js |
24+ |
The API runs several background worker threads:
| Worker |
Location |
Purpose |
| Metrics Aggregation |
services/metrics/aggregation.worker.ts |
Rolls up raw metrics into time-bucketed aggregates |
| Alert Evaluation |
services/alerts/evaluation.worker.ts |
Evaluates alert rules against current metrics/events |
| Cost Calculation |
workers/cost.worker.ts |
Computes per-instance cost entries from provider pricing |
| Drift Detection |
services/drift/detector.worker.ts |
Compares declared vs actual config, creates drift events |
| Cron Scheduler |
services/scheduler/cron.service.ts |
Executes scheduled tasks on their cron schedules |
- Draupnir agent sends metrics (every 30s) and heartbeats (every 10s) over WebSocket
- API gateway authenticates via API key, stamps
instanceId
- Data written to TimescaleDB hypertables (composite PK:
id + timestamp)
- Aggregation worker rolls up into time buckets
- Redis pub/sub broadcasts to subscribed browser sessions
- Web app renders real-time charts via TanStack Query + Zustand
- Browser opens terminal → sends
terminal:create over WebSocket
- API forwards to target instance's Draupnir agent
- Agent spawns PTY, streams base64-encoded data bidirectionally
- Browser renders via xterm.js
- User dispatches command via REST API (
POST /api/v1/commands)
- API sends
command:exec to target instance over WebSocket
- Agent executes, returns
command:result with stdout/stderr/exit code
- Result stored in
CommandExecution table
- Console API resolves its local Sindri CLI version via
sindri version --json
GET /api/v1/version exposes this + computed min_instance_version (major.minor.0)
- Draupnir agents report
sindri_version and cli_target in heartbeat payloads
- Gateway stores these on the Instance model
- Frontend compares instance version against min_instance_version for compatibility badges
See Versioning for the complete version management guide.
Four roles with hierarchical permissions:
| Role |
Level |
Capabilities |
VIEWER |
0 |
Read-only access to all dashboards and data |
DEVELOPER |
1 |
+ Execute commands, open terminal sessions |
OPERATOR |
2 |
+ Manage instances, alerts, deployments, budgets |
ADMIN |
3 |
+ User/team management, delete resources, view secrets |
Roles are scoped per team via TeamMember.role. The requireRole(minimumRole) middleware enforces minimum role requirements on API endpoints.