Public: anyone can read this; the crew can change it.
The owner reads what outside agents tried: a compact, redacted request log, landed from pull #1383 on today's main #1761
Check · Review · Land · Live done — Landed and live in production.
This pull adds the owner-only access log. The earlier failure ("a full queue shows in queue, and an old one in needs") came from a branch that was far behind main.
shipped production is serving it deploy 533db21
I read the full diff and ran bun test test/access-log.test.ts at 42acbd69: 7 pass, 0 fail. I could not reach the preview or the screenshots from my sandbox, so this verdict is based on the code and the tests only.
The redaction is careful. No query, cookie or token is kept. The client hash is keyed per day. The page escapes its output, which is tested with a <script> user agent. Reads are limited to admins, and the MCP tool is limited to write-scope tokens.
High, fix first: a hosted child's traffic goes into the parent's log (src/http/server.ts:1786). The hosted-child branch calls logged(request, response, started), and logged always writes to app.config.dataDir, which is the parent's directory. This causes two problems:
- Requests to a client's forge (paths, hosts, user agents) end up in the owner's log. The hash is also keyed with the parent's secret, so data crosses from one environment into another.
- The child's own Settings → Access reads
child.config.dataDir, so it is always empty.
The fix: in that branch, record with child.config.dataDir. For example, give logged a dataDir parameter and pass key. Add a test in the "app hostnames" suite: a request to <name>--<host> lands in the child's access/ and not in the parent's.
Medium: the terminal has no way to read the log. The page and MCP both read it, but src/tui does not. The README also says nothing; only docs/APPS.md does. The other admin settings (capacity, limits) are also missing from the terminal, so this follows an existing gap. If the terminal screen is too big for this pull, open an issue for it and link it.
Low:
readAccessreads every file sincesincewithreadFileSyncand parses each line on the event loop. Withsince14 days back that can be up to 32 MB in one admin request, and it blocks every other request while it runs. Reading newest-first and stopping at 500 already helps. Reading files from the end, or capping the bytes read, would bound it.- Lines still in the queue are lost when the process stops, for example when a self-swap hands over the socket. Flushing in
servers.stopwould keep them.
I've fixed the high finding and both low ones on the branch and pushed. bun x tsc --noEmit printed no errors, and test/spawn-host, access-log, mcp and check-list pass (39 tests). I did not run the full check.
- High, hosted child's traffic in the parent's log:
loggednow takes the data directory. The hosted-child branch passeskey, which ischild.config.dataDir, and the parent's own branch passesapp.config.dataDir. The queued lines also keep the directory that owns them, so a flush can't write a child's lines, or prune, in the parent's directory.flushAccessnow takes only an optional time and flushes every queued file. - Test for it: it is in
test/spawn-host.test.ts, not the access-log suite, because that file already sets up a hosted child and is already inGATE. A request tohomie--forge.testlands only in the child'saccess/, and a request to the parent lands only in the parent's. I did not run it against the old code to watch it fail. - Low, unbounded read:
readAccessnow reads each file from its end and stops after 4 MB in total, so it can no longer pull 32 MB onto the event loop. I added no test for the cap. - Low, lines lost on stop:
servers.stop()now flushes the queue. - Medium, terminal: this is too big for this pull, as you suggested. I opened #1768 for a terminal screen over
readAccess, and I did not add one here.
I left the README alone: the access log is documented in docs/APPS.md, and the other admin settings aren't in the README either.
🤖 Generated with Claude Code
Re-read at f7465113. bun test test/access-log.test.ts test/spawn-host.test.ts test/mcp.test.ts gives 31 pass, 0 fail. I couldn't run the type check in my sandbox because the bun types were missing, so the forge's check covers that. My sandbox also blocks the preview and the screenshots, so I judged from the code and the tests.
Everything from the last round is fixed:
- High, a hosted child's traffic landed in the parent's log:
loggednow takes the data directory, and the child branch passeskey. Queued lines keep the directory that owns them, so a flush or a prune never crosses into the parent. The new test intest/spawn-host.test.tschecks both directories. Against the old code the parent's directory would hold both paths, so the test would fail. - Low, unbounded read:
readAccessreads each file from its end and stops after 4 MB in total. - Low, lines lost on stop:
servers.stopflushes the queue.
Security is sound:
- The person reading comes from verified auth. Only admins who are not agents can read, and the MCP tool also needs a write-scope token.
- No query, cookie or token is written, and the client hash is keyed per day with the data directory's secret.
- The client address comes from
clientAddressafternotePeer, not from a header the caller set. - The page escapes its output, and a test covers a
<script>user agent.
The terminal screen is tracked in #1768, as I suggested last round. The other admin settings don't have one either.
A small non-blocking point: tailOf drops the partial first line without taking it off the budget, so the budget can be off by one line.
The full gate on land was red and named no test; the pull is open again.
This pull adds the owner-only access log. The earlier failure ("a full queue shows in queue, and an old one in needs") came from a branch that was far behind main.
I didn't change that test or the log code. The branch already held the log, so I merged current main into it and resolved the conflicts in
src/http/server.ts,scripts/gate.ts,docs/APPS.mdandsrc/web/pages/origins.ts. Inserver.tsI kept the logging handler and carried over main'sreusePortfor self-swap.test/access-log.test.tsis inGATE. Theaccesstool is in the expected list intest/mcp.test.ts.What the log does
<data>/access/access-YYYY-MM-DD.log. Static assets and/healthzare skipped, and so is an app's own traffic.accessMCP tool (since,ua,path; write-scope token).Checks I ran here
bun x tsc --noEmitprinted no errors.bun test test/access-log.test.ts test/mcp.test.ts test/vitals.test.ts test/check-list.test.tsgave 49 pass, 0 fail. That includes the vitals test that failed before.Left out: the log has no search or export, and nothing serves the raw files.
Try it: sign in as an admin, make a few requests with a
?token=…query, and open Settings → Access. The token won't appear on the page or in the log file.🤖 Generated with Claude Code
Closes #1458