Under the hood

Architecture, no magic.

Four containers, two databases, one license check. That is the whole stack.

What's in the box

Every service mlab IR runs, and what it does. Tap a row to expand.

01 / Service
app

Rust and Actix-web. Serves the UI, exposes the REST API, handles authentication, two-factor verification and the twelve-bit permission system, and talks to both databases. Stateless, so restart it whenever you like. SQL migrations apply at boot.

02 / Service
executor

Background worker. Runs the license challenge, playbook executions, enrichment lookups, SLA recomputation, webhook delivery with retries, and report generation. Writes analytics to ClickHouse. It authenticates to app with a shared secret: INTERNAL_SECRET is required and has no default.

03 / Datastore
MySQL 8

Transactional store: alerts, cases, incidents, observables, users, evidence metadata, playbooks, webhooks and enrichment configuration. Health-checked by Compose, so the app waits for it rather than crash-looping.

04 / Datastore
ClickHouse

Analytics and audit store: the append-only activity timeline, metric rollups and the enrichment cache. Two-year default retention, configurable from 30 to 3 650 days. The client recovers on its own after the server restarts.

05 / External
License check - mlab.sh

The one call that is always on. Once per hour the executor sends a signed HMAC challenge to mlab.sh/api/v1/ir/license/validate, carrying the tier, a timestamp and a nonce. The result is cached locally with a 48-hour grace window. No telemetry, no usage analytics, no error reporting to a SaaS. Everything else that can leave your network →

Request to resolution

  1. Your SIEM posts to app:/api/v1/ingest/alert with an app token.
  2. app maps the fields, deduplicates, writes to MySQL and emits a timeline event to ClickHouse.
  3. Observables routed to an automatic source are enriched by executor, and the merged verdict is cached for six hours.
  4. The analyst loads the queue. app reads MySQL for state and ClickHouse for history and metrics.
  5. Escalation creates a case, then an incident. Subscribed webhooks are delivered by executor, with retries and a delivery log.
  6. On resolution, the report is rendered from the case and stored on the uploads volume.

Topology

Single host: all four containers via Docker Compose. Two gigabytes of RAM and ten of disk to start.

Scaled out: point DB_HOST and CH_HOST at managed instances and drop the database containers. app and executor stay stateless and scale horizontally behind a reverse proxy.

No Kubernetes operator and no Helm chart maintained as a separate product. If you run K8s you convert the compose file: the service contract is plain.

Serving over plain HTTP for a lab? Set COOKIE_SECURE=false, or sign-in will not work.

Everything that leaves your network, on one page.

Authentication, key scoping, egress and the gaps we know about.