Devtools
@warlock.js/devtools is an in-app dashboard for understanding what a request
did while you develop. It is served at /__warlock and keeps its data only in
memory.
Install
Section titled “Install”warlock add devtoolswarlock dev# open http://localhost:<port>/__warlockThe command adds @warlock.js/devtools as a development dependency. There is
nothing to configure: warlock dev discovers and loads it automatically.
Do not add it to warlock.config.ts. Production installs omit development
dependencies, so importing devtools from application configuration would make
the production process fail to resolve that import.
The dashboard
Section titled “The dashboard”The default dashboard retains the latest 200 requests at /__warlock.
| Panel | What it shows |
|---|---|
| Requests | Status, duration, query count, and a waterfall for router and page middleware, loaders, page-cache activity, rendering, and deferred values. |
| AI | Panoptic runs per HTTP request: model, tokens, cost, provider cache hits, tool timings, retries, truncation and compaction. See AI panel. |
| Queries | SQL with bindings or Mongo commands, duration, rows, and Postgres SELECT query plans through Explain. Explain uses EXPLAIN (FORMAT JSON), never ANALYZE. |
| N+1 | Repeated query shapes within one request, including the source file and line. The dashboard marks the request and prints a terminal warning such as N+1: 12× "select … where post_id = $1" — src/app/posts/services/list-posts.service.ts:18. The fixed threshold is 5. |
| Mail captured by the development mail mode, including headers, sandboxed HTML and text, and attachment names. | |
| Logs | Request-associated log entries, filterable by level and module. |
| Cache | Hit, miss, set, removed, and tag-invalidation activity. |
| Routes | Registered HTTP routes with their method, path, name, and middleware. |
| API | The app’s API reference, rendered from its OpenAPI 3.1 document. See API docs. |
Work that is not part of a request, such as boot work or a scheduler tick, appears separately so it does not distort a request timeline.
Request history
Section titled “Request history”The dashboard keeps the latest 200 requests in memory. With core’s
request log on (the default in
development), the Requests panel also reads the persisted JSON-lines files
in storage/logs/requests, so history survives a restart and the 200-request
limit. A request in both places is shown once, from memory. Requests read only
from disk are marked saved.
- Filter the list by text (URL or id), status (
404or5xx) and method. The API takesstatus,route,method,since,until,qandlimit:GET /__warlock/api/requests?status=5xx&since=1h. - Open a saved request to see its error with stack, page file and site, the redacted request (headers, params, query, body, user), the response, the log entries and the query summary with N+1 findings. Per-query detail and Explain exist only for requests still in memory.
- Copy as curl builds a
curlcommand from the record (GET /__warlock/api/requests/:id/curl). Values the log redacted stay[REDACTED], so the command is safe to paste; replace them before running it.
Clear empties the in-memory list only; the files are not touched. Without the request log, or with a core older than 5.38, the panel shows only the in-memory requests.
Processes
Section titled “Processes”npx warlock add process registers a read-only Processes panel through
src/app/processes/devtools.ts. With @warlock.js/process configured, refresh
the panel to see its current PIDs, workload types, labels, command display,
limits, enforcement mode and peak memory. Environment tokens are not listed.
The endpoint is GET /__warlock/api/processes and uses the same development
and loopback guards. Without registration it returns an empty list. Process
stops remain application APIs; this panel has no stop endpoint. See
process supervision for ownership and platform limits.
API docs
Section titled “API docs”The dashboard has an API tab, and the same page is served on its own at /__warlock/docs. It renders the app’s OpenAPI 3.1 document with Scalar: every API route with its parameters, request body, responses and security.
http://localhost:<port>/__warlock/docsThe document is built in-process, on every request to GET /__warlock/api/openapi.json, from the routes the running dev server has already registered. It is the same generator as warlock generate.openapi, so what you see here is what that command writes (minus page routes), and it follows your edits without a CLI run. What it documents, and how to declare it (validation, description, responseSchema, authMiddleware()), is in the generate.openapi guide.
- If the host provides no document generator,
/__warlock/api/openapi.jsonanswers503. - Scalar’s prebuilt browser bundle is vendored in the package, so apps install nothing extra. The page fetches nothing from a CDN, and Scalar’s web fonts, telemetry, AI agent and MCP are turned off, so it makes no outside request.
- Warnings about routes that cannot be fully described are logged once per process.
- New in 5.28:
/__warlock/docs/swaggershows the same document in Swagger UI (vendored swagger-ui-dist 5.33.0, no outside requests, the validator badge off). The API tab has a Scalar | Swagger UI switch (remembered per browser) and download links for the OpenAPI document and a Postman collection (GET /__warlock/api/postman.json,503when the host provides no generator). - To get files on disk, run
warlock generate.openapi(OpenAPI, for Insomnia or SDK generators) orwarlock generate.postman(a native Postman collection).
Safe by construction
Section titled “Safe by construction”- The package is not installed in a production dependency install.
- If it is present anyway, devtools refuses to mount outside development.
- Dashboard and API routes answer only loopback clients (
127.0.0.1and::1). The check uses the socket address;X-Forwarded-Forcannot unlock it. - Live data is held in memory and disappears when the process restarts. Only what core’s request log wrote (already redacted) is read back from disk.
Build your own tooling
Section titled “Build your own tooling”The dashboard uses public observability hooks rather than a private telemetry path. You can subscribe to the same request, query, mail, and cache events in your own tooling; see Request tracing.