Six modules built in parallel, then each reviewed by an independent adversarial
pass. 40 OpenAPI paths, an MCP server, a CLI, the operational features, the task
worker and a multi-stage image with production compose.
Three gaps only visible where the modules meet, all found by running the stack
rather than by reading it:
- MCP was never mounted. register_routes now calls mount(), which also chains
the streamable-http session manager onto the application lifespan: a mounted
ASGI app receives no lifespan events of its own, and without that the endpoint
fails with "Task group is not initialized".
- The worker had no definition for the logical "parse" endpoint, so both
POST /api/v1/parse and the MCP parse_url tool - the two entry points a
first-time user is most likely to try - failed with "unknown endpoint".
dtk.worker.parsing expands the short link, identifies the resource and maps it
onto a registered endpoint. Expansion is the security-sensitive half: a short
link cannot be validated before it is followed, so the target is checked
against the allowlist after every hop and the hop count is bounded. A test
asserts a short link redirecting to the cloud metadata address is refused.
- The console was not served. The build output is copied to /app/web/dist and
the image already exported DTK_CONSOLE_DIR; the code simply derived its path
from __file__, which inside the image points into site-packages. It now reads
the variable, with fallbacks that keep a source checkout working. The SPA
fallback declines anything under the API prefixes, so a mistyped endpoint
still returns a JSON envelope rather than the console's HTML with status 200.
Verified against the real stack, not a mock: docker compose up, all services
healthy, migrations producing 12 tables, 3 hypertables and 2 continuous
aggregates, the setup token read from the container log, the admin created and
the endpoint then permanently closed, login, the error envelope, three rejected
SSRF shapes, both languages, OpenAPI, the docs and the console. 25 of 25.
1316 unit and replay tests pass.
Brings the compose project up, waits for health, and walks the contract a real
deployment has to satisfy: migrations produce 11 tables, 3 hypertables and both
continuous aggregates; the setup token appears in the container log and the
endpoint closes permanently afterwards; the failure envelope carries a stable
code and a request_id; three SSRF shapes are rejected, including the metadata
address and a lookalike host; messages differ between ?lang=en and ?lang=zh;
OpenAPI, the docs and the console are all served.
It always tears down with -v, including on failure, so a run cannot leave stray
containers or volumes behind. `--keep` opts out for inspection, and `make clean`
removes both the test and the smoke projects.
The setup token is read from the log rather than any HTTP response on purpose:
under Docker's userland proxy every request appears to come from the bridge
gateway, so a source-IP check would admit the whole internet.