Architecture
How brakit captures, analyzes, and displays everything your API does.
The Big Idea
When you run npx brakit install, brakit adds a one-line instrumentation file to your project. When your dev server starts, that import hooks into http.Server.prototype.emit to capture every HTTP request and response. No proxy, no second process. Brakit runs inside your existing server.
┌──────────────────────────────────────────────────────────┐
│ Your Dev Server │
│ │
│ ┌───────────────┐ ┌──────────────────────────────┐ │
│ │ Your App │ │ Brakit (in-process) │ │
│ │ Code │ │ │ │
│ │ │ │ ├─ HTTP capture │ │
│ │ routes │ │ │ (http.Server.emit hook) │ │
│ │ controllers │ │ ├─ DB adapters │ │
│ │ models │ │ │ (pg, mysql2, prisma) │ │
│ │ │ │ ├─ Fetch hook │ │
│ │ │ │ │ (diagnostics_channel) │ │
│ │ │ │ ├─ Console & Error hooks │ │
│ │ │ │ ├─ Analysis engine │ │
│ │ │ │ └─ Dashboard (/__brakit) │ │
│ └───────────────┘ └──────────────────────────────┘ │
│ │
│ localhost:3000 │
└──────────────────────────────────────────────────────────┘That's the entire system. One process, one port, and a dashboard that shows everything.
Why In-Process?
Running inside the same process means brakit can see everything without any external setup:
- From the outside (HTTP hook): Every HTTP request and response: method, URL, headers, body, status code, timing. Captured by patching
http.Server.prototype.emit. - From the inside (instrumentation hooks): Database queries, outgoing fetch calls, console output, unhandled errors. Captured via library-level patches and Node.js
diagnostics_channel.
Request Correlation
Every incoming request is wrapped in an AsyncLocalStorage context with a unique UUID. All hooks (database adapters, fetch, console, errors) read this context to tag their events with the originating request ID.
So when the dashboard shows "this request fired 4 database queries," it knows exactly which queries belong to which request.
The Hooks
When import 'brakit' runs, the following hooks are registered:
- HTTP capture: Patches
http.Server.prototype.emitto intercept every 'request' event. Wraps each request in an AsyncLocalStorage context and captures method, URL, headers, body, status, and timing. - Fetch hook: Captures every outgoing
fetch()call via Node.jsdiagnostics_channel. - Console hook: Wraps
console.log,console.error, etc. Records every message. - Error hook: Listens for uncaught exceptions and unhandled promise rejections.
On top of these core hooks, the adapter system auto-detects installed database libraries and patches them to capture queries.
The Dashboard
Brakit intercepts requests to /__brakit/* before they reach your app. These routes serve the dashboard HTML, REST APIs for each data store, and an SSE stream for real-time updates. Everything else passes through to your application unchanged.
Production Safety
Brakit has multiple layers to ensure it never runs in production:
- Activation gate: Checks
NODE_ENVand 15+ cloud/CI environment variables (Vercel, AWS, Railway, Heroku, etc.). If any production signal is detected, brakit silently disables itself. - Dev dependency: Installed as a
devDependency, so it's pruned during production builds. - Safe wrapping: Every hook is wrapped in try/catch. If brakit throws, your app continues normally.
- Circuit breaker: After 10 errors, brakit automatically disables all instrumentation for the rest of the session.
- Localhost only: The dashboard only responds to requests from localhost.
- Manual override: Set
BRAKIT_DISABLE=1to force-disable brakit in any environment.
The Analysis Engine
The analysis engine subscribes to all in-memory stores and recomputes whenever new data arrives. It produces two kinds of results:
- Security findings: 8 rules that scan every response for leaked secrets, stack traces, insecure cookies, and more. See Security Rules.
- Performance insights: N+1 detection, duplicate API calls, slow endpoints, query-heavy routes, error hotspots, and more.
Both systems are plugin-based. Adding a new security rule or insight pattern is one file implementing one interface.
Data Storage
All captured data lives in bounded in-memory arrays. No external database. Each store holds up to 1,000 entries and evicts the oldest when full:
| Store | Contains |
|---|---|
| RequestStore | HTTP requests and responses |
| QueryStore | Database queries from adapters |
| FetchStore | Outgoing fetch calls |
| LogStore | Console output |
| ErrorStore | Uncaught exceptions and unhandled rejections |
| MetricsStore | Per-endpoint statistics, persisted to .brakit/metrics.json |
Every store supports pub/sub. When a new entry is added, all subscribers are notified. This is how the SSE stream and analysis engine stay in sync without polling.