// how it's built

One event. Five stages.
Every step on the record.

Fusebox is a Rust data plane, a ClickHouse store, a Sigma detection engine and a Mastra agent runtime, built as one system by the team that runs it. This is what happens to a single event from the moment it arrives.

01 / Ingest

Every byte arrives with a receipt.

Syslog lands on a dedicated mTLS listener and passes through Vector's disk buffer. Microsoft 365, Entra, Defender, CrowdStrike and CloudTrail arrive through connector workers that lease their work from Convex. Both paths end at the same Rust endpoint on loopback, where each event is normalised to OCSF 1.4 into typed columns with the original bytes kept alongside. Vector is transport and nothing more. It may add a reception id and a receipt time. Anything else is rejected.

stage 01 · ingest live
Ingest path Syslog arrives through an mTLS listener and Vector's disk buffer. Microsoft 365, Entra, Defender, CrowdStrike and CloudTrail arrive through connector workers. Both paths end at the Rust ingest endpoint, which normalises every event to OCSF 1.4 with typed columns, an overflow column and the original bytes, tagging each with a decode tier. Syslog mTLS listener per connector Microsoft 365 · Entra Graph connector Defender · CrowdStrike CloudTrail · API connectors Vector disk buffer · raw transport Connector runtime leases work from Convex only adds a reception id and a receipt time RUST core/ingest loopback POST /v1/events OCSF 1.4.0 NORMALISER 18 typed columns overflow_json raw bytes · verbatim replay-stable id DECODE TIER parsed partial opaque tier travels with it Vector may not parse, filter or mutate. Anything else is rejected.
02 / Store

One table. One tenant at a time.

Events live in ClickHouse with the tenant first in the ordering key, and the original evidence lives in S3 behind a writer that can create but never delete. Every query in Rust or TypeScript passes through a single tenant boundary that fails closed, and ClickHouse enforces it a second time with row policies. Retention is 30, 90, 180 or 365 days per tenant, decided in Convex and applied to ClickHouse and S3 by a separate workload with its own identity.

stage 02 · store
session tenant
live
Store and tenant boundary Events sit in one ClickHouse table with the tenant first in the ordering key. The session's tenant sees only its own rows; row policies deny every other tenant's rows. Original evidence goes to S3 through a create-only writer. Convex holds the retention policy, and each event carries a logical and a physical deadline. CLICKHOUSE detection_events · tenant-first ordering key tenant class category action time tenant_a 4001 network_activity denied 10:14:02 row policy: denied tenant_b 3002 authentication failure 10:14:02 row policy: denied tenant_a 1007 process_activity launch 10:14:03 row policy: denied tenant_a 3002 authentication success 10:14:03 row policy: denied tenant_b 4001 network_activity allowed 10:14:04 row policy: denied tenant_b 2004 detection_finding create 10:14:04 row policy: denied tenant_a 4001 network_activity denied 10:14:05 row policy: denied tenant_b 3005 user_access assign 10:14:05 row policy: denied tenant boundary · fails closed in Rust and TypeScript · row policies enforce it again S3 Original evidence every version kept ingest writer is create-only CONVEX Policy authority retention: 30 to 365 days applied by a separate workload written logical deadline physical deadline reversible until it activates
03 / Detect

Sigma in. ClickHouse SQL out.

Detections are written as standard Sigma v2 rules and compiled to ClickHouse SQL. Values are bound as typed parameters, never interpolated, so a rule cannot become an injection. A micro-batch scheduler runs every rule against newly admitted events every one to five minutes on a bounded pool of 32 workers, and never lets two runs of the same rule overlap. Every generated query dedupes by event identity, enforces retention inline and caps its own memory.

stage 03 · detect live
title: Denied Network Trafficsigma-version: 2logsource:  product: ocsf  category: network_activitydetection:  selection:    class_uid: 4001    activity_id: 6    action_id: 2    status_id: 2  condition: selectionlevel: medium
SELECT event_identity, event_id, processed_at_msFROM (  SELECT event_identity,    argMax(fields, processed_at_ms) AS latest  FROM detection_events_admission  LEFT JOIN active_retention_policy_cutovers    USING (tenant_id)  PREWHERE tenant_id = ?    AND admitted_at_ms > ? AND admitted_at_ms <= ?  WHERE retention_deadline_ms > now64()  GROUP BY event_identity)WHERE class_uid = ?  AND activity_id = ?  AND action_id = ?  AND status_id = ?ORDER BY processed_at_ms, event_identityLIMIT 10001SETTINGS max_memory_usage = 536870912
bindings [4001, 6, 2, 2] typed, never interpolated micro-batch every 1 to 5 min workers 32 with an overlap fence dedupe by event_identity
04 / Investigate

The model is a component. Everything around it is the product.

A Sigma match or a Defender alert opens a case. The Mastra agent works the loop: plan, call a tenant-scoped tool, record the evidence, repeat. Its tools are Defender device context, Entra sign-ins, Fusebox search and threat intelligence, and every call is scoped to the tenant before it runs. The verdict must cite an evidence chain, then passes the policy gate. Allowed actions run. Everything else waits for approval in Teams or email. One provider interface routes Claude, OpenAI and Gemini per task and per tenant, so reasoning goes to the strongest model and classification to the cheapest model that passes our evals.

stage 04 · investigate live
Investigation loop An alert opens a case. The Mastra agent plans, calls a tenant-scoped tool, records the evidence and repeats, within a budget of 30 tool calls. The verdict must cite an evidence chain, then passes a policy gate: allowed actions run, anything else waits for approval in Teams or email. One provider interface routes Claude, OpenAI and Gemini per task and per tenant. Every step is recorded and replayable. Alert Sigma match Defender alert MODEL ROUTER one provider interface Claude OpenAI Gemini per task and tenant CASE tools 00/30 Plan Tool call Evidence Defender device Entra sign-ins Fusebox search Threat intel tenant-scoped before it runs Verdict with evidence chain Policy gate class and severity Act allowed by your policy Approve in Teams or email audit · every tool call, verdict and action recorded · replayable tokens 0
05 / Control

Tenant authority is a certificate, not a claim.

Each syslog connector gets its own mTLS listener with its own port and its own client CA. The tenant is fixed by which listener accepted the connection, before a single byte is parsed. A certificate issued for one tenant is cryptographically invalid on another tenant's listener, so a sender cannot cross tenants even by pointing at the wrong port. Source IP and message content are never tenant authority. The emergency stop halts the agent per tenant or across the whole organisation.

stage 05 · control live
Tenant authority and control Each syslog connector has its own mTLS listener with its own port and client CA. A sender with tenant A's certificate is accepted on tenant A's listener and rejected on tenant B's, because the certificate is cryptographically invalid there. An emergency stop halts the agent per tenant or across the organisation. Replayed receptions keep their identity; duplicate bytes get a new one. Tenant A sender client cert issued by CA-A Tenant B sender client cert issued by CA-B CA mismatch: rejected before a byte is parsed :6514 · tenant A own port · trusts client CA-A only :6515 · tenant B own port · trusts client CA-B only :6516 · tenant C own port · trusts client CA-C only keys: mounted secrets, never in Convex EMERGENCY STOP this tenant whole organisation halts every agent action, always available EVENT IDENTITY replayed reception → same identity byte-identical resend → new identity
// decisions we would make again

Built to be argued with.

No message bus.

Vector's disk buffers give the data plane its durability. The interfaces are seamed so Redpanda drops in the day a second real-time consumer of the raw stream exists, and not a day before.

ClickHouse, not a search index.

Columnar storage over typed OCSF fields turns detection into a SQL problem, keeps a year of retention affordable, and gives every tenant one boundary to reason about.

Two languages, enforced.

Rust owns the data plane. TypeScript owns the control plane and the console. Nothing else gets in, so every engineer can read every line.

Convex is the single policy authority.

Every tenant, role, connector, policy and retention decision has exactly one home. ClickHouse and S3 follow it, never the other way round.

Temporal for anything long-running.

Scans, enrichment, onboarding and durable agent jobs get retries and history for free instead of a hand-rolled job table that fails on a Sunday.

Proof, not slides.

Every release has to pass the same test: a genuine alert in a real tenant, investigated, actioned by policy and closed by the agent, while a second tenant cannot see or influence any of it.

// founding partners

Put Fusebox to work.

Get founding-partner terms and a direct line to the team building Fusebox. Tell us about your environment.

Become a founding partner