Skip to content

Server

nodeartifact serve receives traces over OTLP/HTTP, stores them in SQLite and serves the web UI. It needs the server extra.

uv run nodeartifact serve

The UI is at http://127.0.0.1:4318/ and traces go to http://127.0.0.1:4318/v1/traces. The spans are kept in nodeartifact.db in the current folder.

Options

Option Default Meaning
--host 127.0.0.1 Address to bind. Clients must name it in the Host header, or name a host given to --allowed-host
--port 4318 Port to bind. 4318 is the standard OTLP/HTTP port
--database ./nodeartifact.db SQLite database file. It is created when missing; its folder must exist
--allowed-host none Another Host header to accept, such as localhost:4318. Repeat it for each name clients use

To keep the traces in one place whatever folder you start in:

uv run nodeartifact serve --database ~/traces/nodeartifact.db

Security

The server has no authentication. Keep it on 127.0.0.1, the default, and do not expose it on a network you do not trust. Traces can hold prompts, model answers and tool results.

It answers only requests whose Host header names the --host address or an --allowed-host, and others get 400. This blocks DNS rebinding. On a loopback address, localhost, 127.0.0.1 and ::1 all work.

Bound to 0.0.0.0, as in a container, pass --allowed-host for every name clients use:

uv run nodeartifact serve --host 0.0.0.0 --allowed-host localhost:4318 --allowed-host nodeartifact:4318

Without any --allowed-host, the server warns at startup and every other name gets 400.

Run in Docker

The image installs nodestep from the sibling checkout, so build it from the folder that holds nodestep/ and nodeartifact/:

docker build -f nodeartifact/docker/Dockerfile -t nodeartifact .
docker run --rm -p 127.0.0.1:4318:4318 -v nodeartifact-data:/data nodeartifact

The UI is at http://localhost:4318/ and traces go to http://localhost:4318/v1/traces. The server runs as a non-root user and keeps its database in the /data volume. The image accepts the Host headers localhost:4318, 127.0.0.1:4318 and nodeartifact:4318; other names or ports need their own --allowed-host.

Change the port

Arguments after the image name replace the whole default command, so repeat all of it. For host port 8080:

docker run --rm -p 127.0.0.1:8080:4318 -v nodeartifact-data:/data nodeartifact \
  serve --host 0.0.0.0 --database /data/nodeartifact.db \
  --allowed-host localhost:8080 --allowed-host 127.0.0.1:8080

Keep port 4318 inside the container and change only the published port: the health check calls http://127.0.0.1:4318/health. Publish on 127.0.0.1 as above, since there is no authentication.

Routes

Route Returns
POST /v1/traces The OTLP/HTTP receiver
GET / The trace list; ?q= searches it
GET /results?q= The rows of the trace list alone, which the search field loads as you type
GET /live The trace list as server-sent events, sent again when new spans arrive
GET /traces/{trace_id} The graph view, or the timeline for a trace without a traced graph
GET /traces/{trace_id}/timeline The timeline
GET /traces/{trace_id}/spans/{span_id} One span
GET /traces/{trace_id}/live The new spans of a trace as server-sent events, resuming after Last-Event-ID. It ends with an end event when the run span arrives, or an idle event after ten minutes without new spans
GET /health ok, for any Host header

Rules and defaults

Rule What happens
Database In WAL mode. A span sent twice is stored once
Database errors The server exits with code 1 when it cannot open the file, when nodeartifact did not create it, or when it has another schema version
--allowed-host format host, host:port, [ipv6] or [ipv6]:port, with no wildcard. Without a port it matches any port. A Host header without a port means port 80
Request body At most 32 MiB, before and after gzip
Content types application/x-protobuf and application/json, plain or gzip. Other types get 415
Content-Security-Policy Every page allows scripts only from the server. The graph view also allows the Mermaid file from jsdelivr, pinned with an integrity hash, and inline styles, which Mermaid needs