Framework-neutral engine for the NodeUI developer console: observability
providers, the REST contract, the safety gate, and the static console server.
The Express, Fastify, NestJS, Koa, Hapi, Hono and node:http adapters build on
this package; you normally consume those instead of using @singhak/nodeui-core
directly. Use core directly to integrate a framework that has no adapter.
Each provider exposes one panel through GET {path}/api/{id}:
| id | Panel | Notes |
|---|---|---|
health |
Health | Event-loop + memory status, plus your healthChecks. |
memory |
Memory | Samples process + OS memory on start. |
cpu |
CPU | Process CPU percent from process.cpuUsage(). |
event-loop |
Event-loop lag | Drift vs. a 1ms setInterval, last 600 samples. |
heap-snapshot |
Heap snapshot | Writes nodeui-heap-<pid>-<ts>.heapsnapshot (owner-only). |
startup |
Startup | Timing marks recorded via server.mark(name). |
requests |
Requests | Ring buffer of app HTTP requests (default 500). |
metrics |
Metrics | Requests/errors per second for the last minute. |
env |
Environment | process.env + your config, masked. |
routes |
Routes | Express router introspection, or server.setRoutes(). |
logs |
Logs | console.* capture plus server.addLogSource(). |
outgoing |
Outgoing HTTP | http/https/fetch calls made by the app. |
queries |
Queries | pg/mysql2/Prisma statements with slow and N+1 flags, plus per-statement aggregates. |
errors |
Errors | Grouped errors with counts, stacks and the linked request. |
timeseries |
Metrics | 1 s / 10 s history of throughput, latency histogram, event loop, memory, CPU, GC, heatmap, annotations, heap trend. |
routemetrics |
Endpoints | Per-route latency percentiles, 5xx rate and trend. |
alerts |
Alerts | Threshold rules, state and fired/resolved history. |
cpu-profile |
Profiler | On-demand CPU profile as a call tree / flame graph. |
loadtest |
Load generator | Worker-thread load against the app, saved as a run. |
| your id | your title | Custom panels via the plugins option (see below). |
All endpoints live under {path}/api and return
{ ok: true, data } | { ok: false, error: { code, message } }:
-
GET /config— effective configuration, panel list and plugin titles. -
GET /{panel-id}— panel data for every id above. -
GET /live?panels=a,b— Server-Sent Events stream (capped bymaxSseClients). -
POST /confirmations— issue a single-use nonce. -
POST /heap-snapshot— capture a snapshot; must sendx-nodeui-confirm: <nonce>from a fresh confirmation. A missing, expired, or replayed nonce returns409with error codeconfirmation-required. -
GET /timeseries?range=<ms>&points=<n>&heatmap=1— metrics history; alsofrom/to.GET /prometheusexposes it as Prometheus text. -
GET /runs,GET /runs/export,POST /runs/{start,stop,save,delete,baseline,import}— saved load-test runs and the pinned baseline (kept in memory, or on disk withrunsFile). -
POST /alerts/{add,remove},POST /annotations/{add,remove}— alert rules and chart markers. -
POST /replay— re-send a captured request to the app itself. -
POST /cpu-profile/{start,stop},POST /loadtest/{start,stop}— profiler and load generator.
GET / serves the bundled React console with an SPA fallback to index.html.
- Activation: active when
NODE_ENVis non-production, or whenNODEUI_ENABLED=trueforces it; fail-closed in production (a warning is logged if forced on). - Loopback-only: remote hosts are rejected with
403. Extra clients can be allow-listed (allowedRemoteAddresses, exact IPs or IPv4 CIDRs, e.g. the Docker bridge). - Host / Origin validation: blocks DNS-rebinding and cross-site requests. Extra names via
allowedHosts/allowedOrigins.X-Forwarded-*requests are rejected unlesstrustProxy. - Access token (optional):
authToken/NODEUI_TOKEN; sendAuthorization: Bearer <token>, or open{path}/?token=<token>once to get an HttpOnly cookie. - Secret masking: values under keys such as
token,key,secret,password,auth,cookie,dsn,database_urlare replaced with[REDACTED]; credentials inside text (URLs, bearer tokens, JWTs,password=...) are scrubbed too, in REST and SSE. Disable withmaskSecrets: false. - Headers: CSP,
X-Content-Type-Options,X-Frame-Options,Referrer-Policyon every response. - Read-only default: actions with side effects (heap snapshot, replay, CPU profile, load test) each need a fresh single-use nonce. Replay and the load generator can only target the app's own listening address, never a user-supplied host.
- Zero-cost when disabled: the middleware is a pass-through; no timers or hooks are created.
Environment variables are read at createNodeUI() time (overridable via the
options.env map for tests); each also has a typed option of the same meaning.
| Variable | Default | Description |
|---|---|---|
NODEUI_ENABLED |
unset | Force activation (true) / disable. |
NODEUI_HOST |
127.0.0.1 |
Loopback host the console trusts. |
NODEUI_PATH |
/nodeui |
URL prefix for console + API. |
NODEUI_REQUEST_LOG_SIZE |
500 |
Request ring-buffer capacity. |
NODEUI_LOG_SIZE |
500 |
Log ring-buffer capacity. |
NODEUI_POLL_INTERVAL_MS |
2000 |
Sampling interval. |
NODEUI_INACTIVITY_TIMEOUT_MS |
60000 |
Provider idle timeout. |
NODEUI_CONFIRM_TTL_MS |
60000 |
Nonce lifetime. |
NODEUI_HEAP_SNAPSHOT_DIR |
<tmp>/nodeui-heap |
Snapshot output directory. |
NODEUI_TOKEN |
unset | Require an access token. |
NODEUI_ALLOWED_HOSTS |
unset | Comma-separated extra Host names. |
NODEUI_ALLOWED_ORIGINS |
unset | Comma-separated extra Origin values. |
NODEUI_ALLOWED_REMOTE |
unset | Comma-separated client IPs / IPv4 CIDRs. |
NODEUI_TRUST_PROXY |
false |
Accept X-Forwarded-* requests. |
NODEUI_MAX_SSE_CLIENTS |
10 |
Concurrent live streams. |
import { createNodeUI, type NodeUIServer } from '@singhak/nodeui-core';
const server: NodeUIServer = createNodeUI({
plugins: [{ id: 'queues', title: 'Queues', get: async () => ({ ok: true, data: await jobs() }) }],
healthChecks: { db: () => pool.query('select 1') },
});
const middleware = server.middleware();
// middleware is (req, res, next) => void; mount it before your routes.
server.mark('listening');
server.shutdown();Plugin ids are lowercase letters, digits and -, and must not collide with a built-in panel.
Express routes are discovered automatically, and the Fastify, Hapi and Hono adapters feed
the panel for you. For anything else (Koa, plain http, a custom router), feed it with
server.setRoutes([...]).
npm run typecheck # tsc --noEmit
npm test # vitest run (unit + e2e)
npm run build # emit dist/