RUNTIME ATLAS / DOCUMENTATION
Make a real request path
read like architecture.
Runtime Atlas combines TypeScript declarations with bounded runtime traces. It shows a source-backed topology immediately, then animates and replays the causal path of recent Node.js requests.
Runtime Atlas is a developer observability tool. It is not a durable tracing database, metrics/logs platform, cross-replica store, or public multi-tenant service.
Quick start
Install the runtime pinned in .nvmrc, install the
locked dependency graph, and start the collector plus Vite UI
together.
git clone https://github.com/OthmaneBlial/Runtime-Atlas.git
cd Runtime-Atlas
nvm install && nvm use
npm ci && npm run dev
Open the loopback URL printed by Vite — normally
http://127.0.0.1:5173, or the next available port. The
API and collector run at 127.0.0.1:4319.
Local demo
The demo topology appears before traffic arrives. Run one of three deterministic scenarios from the workspace, then select trace history, scrub replay, inspect a node, export, or clear the retained evidence.
/checkout
Successful order path across session, cart, pricing, dependencies, and fulfillment.
/search
Smaller read path through rate limiting, catalog data, and cache.
/payment
Deterministic payment dependency outage with an observable error path.
Instrument with the Node.js SDK
The workspace package @runtime-atlas/sdk emits trace
and span lifecycle events while keeping observed application
requests independent from collector availability. It is currently a
local workspace artifact, not a claimed public registry release.
Build and link the workspace package
npm run build:sdk
Wrap application work
import { createAtlas } from "@runtime-atlas/sdk";
const atlas = createAtlas({
serviceName: "orders-api",
collectorUrl: "http://127.0.0.1:4319",
maxQueueSize: 2_000,
requestTimeoutMs: 3_000,
onError: (error) =>
applicationLogger.warn({ error }, "atlas collector unavailable"),
});
const ordersDb = atlas.database(
{ id: "db.orders", label: "Orders DB", meta: { engine: "PostgreSQL" } },
async () => saveOrder(),
);
const createOrder = atlas.route(
{ id: "route.orders", label: "POST /orders" },
async () => ordersDb(),
);
Use request middleware safely
Connect/Express applications can use
atlas.httpMiddleware(). Normalize user-specific path
segments before telemetry leaves the process; the SDK removes query
strings and URL fragments but cannot know which remaining segments
identify a user.
app.use(
atlas.httpMiddleware({
path: (request) =>
(request.originalUrl ?? request.url ?? "/").replace(
/\/\d+(?=\/|$)/g,
"/:id",
),
ignore: (request) => request.url === "/health",
}),
);
Connect OpenTelemetry
Configure an OTLP/HTTP exporter to send JSON to the exact traces endpoint. Uncompressed and gzip-compressed OTLP JSON are supported. Binary protobuf and gRPC are intentionally not accepted.
http://127.0.0.1:4319/v1/traces
Test the bundled payload
curl --request POST http://127.0.0.1:4319/v1/traces \
--header 'content-type: application/json' \
--data-binary @examples/otlp-trace.json
Span reconciliation order
-
01
Explicit node ID
runtime.atlas.node_id -
02
Code provenance
code.function.name · code.file.path · code.line.number -
03
HTTP evidence
http.request.method · http.route -
04
Bounded inference
database · messaging · service · code allowlist
Malformed or incomplete individual spans are counted in OTLP
partialSuccess. Malformed envelopes and capacity
violations return protobuf-JSON
google.rpc.Status errors.
Point the analyzer at TypeScript
Configure one or more comma-separated source globs. The analyzer
uses
ts-morph without executing the target application.
ATLAS_SOURCE_GLOB='../orders-api/src/**/*.ts' \
ATLAS_PROJECT_NAME='orders-api' \
ATLAS_ENVIRONMENT='development' \
npm start
atlas.routeatlas.middlewareatlas.service atlas.databaseatlas.cacheatlas.externalatlas.queue
Calls may live inside application factories. IDs, labels, metadata, and source locations must remain literal enough to prove. Dynamic descriptors or wrappers that hide these call shapes may appear only as visibly runtime-only nodes.
Architecture
The Express application owns analysis, validation, ingestion, retained trace state, SSE, and the production UI. The process boundary owns configuration, structured logs, sockets, and graceful shutdown.
Ordered runtime model
Each event receives an increasing collector sequence. The UI uses that sequence rather than service clocks to follow the newest evidence and compute replay state.
HTTP surface
Every response receives a request ID and restrictive browser security headers.
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /health |
Lightweight process liveness |
| GET | /ready |
Topology, production UI, and runtime readiness |
| GET | /api/topology |
AST-derived topology and enabled capabilities |
| GET | /api/source |
Analyzer-approved source context window |
| GET | /api/traces |
Recent bounded in-memory history |
| GET | /api/traces/export |
Download retained traces as JSON |
| DELETE | /api/traces |
Clear history when enabled |
| GET | /api/stream |
SSE runtime events and heartbeats |
| POST | /api/ingest |
First-party SDK batches |
| POST | /v1/traces |
OTLP/HTTP JSON traces |
| POST | /api/demo/checkout |
Deterministic success trace |
| GET | /api/demo/search |
Shared-infrastructure trace |
| POST | /api/demo/failure |
Deterministic dependency failure |
Configuration
The server reads environment variables directly and does not
auto-load .env
files. Invalid configuration fails before the listener starts.
| Variable | Default | Purpose |
|---|---|---|
HOST |
127.0.0.1 |
Listener; use non-loopback only in a controlled network |
PORT |
4319 |
API, collector, SSE, and production UI |
ATLAS_SOURCE_GLOB |
demo source | Comma-separated TypeScript source globs |
ATLAS_PROJECT_NAME |
demo / project | Displayed project name |
ATLAS_ENVIRONMENT |
local |
Displayed environment label |
ATLAS_INGEST_TOKEN |
unset | Optional bearer token, minimum 16 characters |
ATLAS_EXPOSE_SOURCE |
loopback only | Enable analyzer-approved source context |
ATLAS_ALLOW_CLEAR |
local demo only | Enable trace deletion API and control |
ATLAS_TRUST_PROXY |
false |
Trust one reverse-proxy hop |
ATLAS_LOG_LEVEL |
info |
debug, info, warn, error, or silent |
ATLAS_OTLP_BODY_LIMIT |
8mb |
Decompressed OTLP JSON limit |
ATLAS_OTLP_MAX_SPANS |
1000 |
Maximum span groups per OTLP request |
ATLAS_OTLP_MAX_CONCURRENT_REQUESTS |
16 |
OTLP conversion concurrency |
ATLAS_INGEST_RATE_LIMIT |
600 |
Ingest requests per client per minute |
ATLAS_MAX_TRACES |
60 |
In-memory trace retention |
ATLAS_MAX_BUFFERED_EVENTS |
800 |
SSE replay buffer |
ATLAS_MAX_EVENTS_PER_TRACE |
5000 |
Per-trace event retention |
ATLAS_MAX_RETAINED_EVENTS |
50000 |
Aggregate retained events |
ATLAS_MAX_STREAM_CLIENTS |
100 |
Concurrent SSE clients per process |
ATLAS_SHUTDOWN_TIMEOUT_MS |
10000 |
Graceful shutdown deadline |
Privacy and security boundaries
Trace evidence stays in process memory. Restarting the server clears it; authorized users may also clear it through the UI when that capability is enabled.
- Trace and span IDs, parents, timestamps, durations, status
- Bounded method and normalized path
- Configured node IDs and service names
- Allowlisted operation, code, route, database, and messaging evidence
- Bounded error messages
url.full, query strings, or URL fragments- Database statements
- Request or response bodies
- Credentials
- Arbitrary OpenTelemetry attributes
Source inspection
/api/source returns a small context window only when
the exact file and line belong to an analyzed node. It defaults on
for loopback and off for non-loopback bindings.
Shared network rule
Terminate TLS and require user authentication at a trusted reverse proxy. Configure a long ingest token and keep source inspection and trace clearing disabled unless every viewer is authorized.
Production and container deployment
Compiled Node.js process
nvm use
npm ci
npm run build
NODE_ENV=production npm start
Open http://127.0.0.1:4319.
GET /health proves liveness;
GET /ready also analyzes the configured source and
verifies the built UI.
Hardened local container
docker compose up --build
The image runs as the unprivileged node user. Compose
binds the exposed port to loopback, uses a read-only filesystem, and
enables demo-only source and clear controls.
Shared network checklist
- Terminate TLS and require user authentication at a reverse proxy.
-
Generate an
ATLAS_INGEST_TOKENof at least 16 characters. - Keep source inspection and trace clearing disabled unless explicitly needed.
- Trust a proxy hop only when the deployment actually uses that trusted topology.
- Tune body, span, concurrency, rate, event, trace, and SSE limits for available memory.
- Use one process per instance; in-memory trace state is not coordinated across replicas.
On SIGINT or SIGTERM, the server closes
SSE clients, stops accepting new connections, waits for active
sockets, then enforces the configured shutdown deadline. Rollback
requires no persistent schema migration.
Validation
The repository exposes one complete local gate:
npm run check
Browser tests fail on unexpected console errors, page exceptions, failed requests, HTTP errors, accessibility violations, or mobile horizontal overflow. The production smoke enforces 100 KiB JavaScript and 20 KiB CSS gzip entry-asset budgets.
npm test
npm run test:e2e
npm run typecheck
npm run lint
npm run build
npm run smoke
npm audit --omit=dev --audit-level=high
docker build --tag runtime-atlas:local .
Troubleshooting
The UI shows a connection error
In development, Vite and the API must both run. Start them with
npm run dev, check
http://127.0.0.1:4319/health, then use Retry
connection.
Readiness returns 503
Check the structured readiness.failed log. Common
causes are an empty or excessive source glob, duplicate node
IDs, or a missing dist/index.html. Run
npm run build before npm start.
Static nodes are missing
Keep the declaration shape literal:
atlas.service({ id: "…", label: "…" }, handler).
Dynamic descriptors and calls hidden behind wrappers cannot be
proven statically.
The collector returns 401
The server has ATLAS_INGEST_TOKEN configured. Send
the exact value as Authorization: Bearer …. Never
put it in source, URLs, or logs.
The OTLP collector returns 415
Use OTLP/HTTP JSON at /v1/traces with
Content-Type: application/json. Binary protobuf and
gRPC are not supported; gzip content encoding is.
The OTLP response reports partialSuccess
The envelope was valid, but one or more resource/span groups were malformed, incomplete, or over a configured bound. Use the rejected count to inspect exporter configuration without logging sensitive payloads.
A trace does not appear
Confirm first-party ingest returned 202 or OTLP returned 200. Check retention limits and service clocks. If the browser disconnected, reconnect so the bounded server event buffer can replay.
Port 4319 is already in use
Use another server port such as
PORT=4320 npm run dev:server, then update the Vite
proxy target, or stop the conflicting process.
Playwright cannot launch Chrome
Install or update Google Chrome, confirm it launches, and rerun
npm run test:e2e. The tests use the installed
Chrome channel rather than a downloaded browser binary.
Compatibility, limits, and support
Node.js ^22.22.2, ^24.15.0, or
≥26. Node 24.15.0 is pinned for development and CI.
Evergreen browsers with ES modules, SVG
foreignObject, SSE, and color-mix().
Chrome is automated; Firefox and Safari remain release checks.
Telemetry is in memory, per process, and deliberately not durable or replicated.
The analyzer favors provable TypeScript call shapes over speculative inference.
Use GitHub Issues for reproducible bugs and focused feature requests. Security reports belong in the repository’s private vulnerability process, not a public issue.
Try a broader term such as “OTLP”, “privacy”, “Docker”, or “SDK”.