Version. 0.1
Date. 2026-07-19
Author. Claude Code on DUNIN7-M4, on Operator direction.
Status. QUEUED. Two items, not started.
Reads with. security/loomworks-security-incident-2026-07-19-dev-endpoint-exposure-v0_1 §5.1, which is where both limitations were found.
The 2026-07-19 incident sweep concluded that there was no evidence of exploitation. That conclusion is honest and it is weaker than it sounds, for two specific reasons that are both fixable.
The engine's request log records what was asked and what it answered — but not when, and not by whom. So a sweep can count requests and correlate them against surrounding context; it cannot place them in time, and it cannot tell one caller from another. These two items close that gap. Together they change a future incident sweep from correlation into evidence.
Neither is urgent. Both are cheap. Both would have materially strengthened a conclusion that has already had to be qualified once.
What is missing. uvicorn's default access-log line carries method, path, protocol and status, and nothing else:
INFO: 159.250.180.118:0 - "POST /auth/dev/issue-session HTTP/1.1" 204 No Content
Why it mattered. The incident's exposure window was about a day. Five hits on the exposed endpoint were found. None could be bounded to the window, or to any point within it, or ordered against the containment action — only positioned relative to neighbouring lines in the same file. They were ultimately attributed by reading the requests around them, which is inference, not record.
What to do. Configure uvicorn's log formatter to include an ISO-8601 timestamp on every access line. Confirm the launchd service's redirected output preserves it.
Worth deciding at the same time: whether the file should rotate, and how much history is kept. A log that cannot be time-bounded is one problem; a log that has been silently truncated past the window of interest is another, and this one currently starts wherever the service last happened to be created.
CF-Connecting-IP to the engineWhat is missing. cloudflared does not preserve the originating client address. Every external request in the engine log therefore shares one source — 328 of them, in the incident's sweep, from a single apparent address.
Why it mattered. A third party exercising the exposed endpoint would have been indistinguishable from the Operator's own browser. The sweep's fifth mint was attributed to ordinary Operator use on the strength of its request pattern; with a client address it would have been attributed on the strength of a fact.
What to do. Cloudflare sets CF-Connecting-IP at the edge with the true client address. Read it in the engine and include it in the access log.
Two things worth knowing before doing it:
request.headers.get('CF-Connecting-IP'), which is why per-IP limiting genuinely works there and does not in the engine. The header is available; the engine simply does not read it.Both items exist because of a conclusion that had to be qualified. Recorded together so the qualification is not repeated by a future sweep discovering the same two gaps from scratch.
DUNIN7 — Done In Seven LLC — Miami, Florida Loomworks — request-log forensics work items — v0_1 — 2026-07-19