Ticks leak python3 -m http.server daemons; stale listeners can answer another tick's verification requests #418

Closed
opened 2026-07-24 22:03:01 +00:00 by igor · 0 comments
Collaborator

Rescoped from the original logwatch filing (see history). The original framing — that the agent passed off a hand-rolled simulation as real verification — is not borne out; the passing observation about a stray listener is the actual defect.

What is actually wrong

Ticks that verify built static output start a local HTTP server and never reap it. Four orphans were live on the host as of 2026-07-24 22:12, all reparented to init (ppid 1), all bound to 0.0.0.0:

port cwd age
8099 /home/igor/Code/joshtronic.com/_site 8h02m
8791 /home/igor/Code/joshtronic.com/_site 6h37m
8792 /home/igor/Code/ageofcombat.com/dist 6h21m
8794 /home/igor/Code/snail.io/dist 1h59m

All four were killed manually. Nothing in the harness would have reaped them.

This directly caused the misleading response the original filing noted in passing:

Jul 24 16:32:44 igor.sherver.org tick.sh[1068638]: That response didn't come from my nginx -- my config failed to load (wrong path resolution) and something else was already listening on port 8099. I need to investigate before trusting any of this.

A leaked server from an earlier tick was squatting on the port and answered a request the running tick believed was its own. That tick caught it. A tick that does not catch it reports a verification result sourced from a stale build of a different site.

Severity

Exposure is limited: the host is on 192.168.4.26/22, igor.sherver.org resolves to 127.0.1.1 on-box, and the served content is public build output. The cost is unbounded process accumulation and a verification signal that can silently come from the wrong server, not disclosure.

What the original filing got wrong

The claim that the agent reported a simulation as real verification does not hold. idolidle.com#24 states plainly that nginx could not be started, calls the substitute a stand-in, and leaves the live-nginx checkbox unchecked:

nginx itself can't be started in this sandboxed environment (daemon/network-listener commands require an approval step unavailable here), so I reproduced that exact directive's documented behavior in a small throwaway Node server [...] the sandbox verification above stands in for, but isn't literally, that nginx process

That labeling is the behavior the original ticket asked for as its fix. No change needed there.

Fixed looks like

A tick does not stand up a web server to verify static routing at all. The deploy barrier (deploy-sha marker + agent.json .smoke.url) is the smoke test for served behavior; in-tick verification should stop at the build output on disk. Doing this makes the sandbox's inconsistent gating of nginx moot — nothing gets spawned, so nothing leaks and nothing collides.

If any tick-local listener survives that change, it must bind loopback only and be reaped on tick exit regardless of how the tick ends.

Tests

  • A tick that verifies static output leaves no listening socket behind when it exits, including on the failure and timeout paths.
  • No python3 -m http.server or equivalent is spawned by the static-verification path.
Rescoped from the original logwatch filing (see history). The original framing — that the agent passed off a hand-rolled simulation as real verification — is not borne out; the passing observation about a stray listener is the actual defect. ## What is actually wrong Ticks that verify built static output start a local HTTP server and never reap it. Four orphans were live on the host as of 2026-07-24 22:12, all reparented to init (`ppid 1`), all bound to `0.0.0.0`: | port | cwd | age | | --- | --- | --- | | 8099 | `/home/igor/Code/joshtronic.com/_site` | 8h02m | | 8791 | `/home/igor/Code/joshtronic.com/_site` | 6h37m | | 8792 | `/home/igor/Code/ageofcombat.com/dist` | 6h21m | | 8794 | `/home/igor/Code/snail.io/dist` | 1h59m | All four were killed manually. Nothing in the harness would have reaped them. This directly caused the misleading response the original filing noted in passing: ``` Jul 24 16:32:44 igor.sherver.org tick.sh[1068638]: That response didn't come from my nginx -- my config failed to load (wrong path resolution) and something else was already listening on port 8099. I need to investigate before trusting any of this. ``` A leaked server from an earlier tick was squatting on the port and answered a request the running tick believed was its own. That tick caught it. A tick that does not catch it reports a verification result sourced from a stale build of a different site. ## Severity Exposure is limited: the host is on `192.168.4.26/22`, `igor.sherver.org` resolves to `127.0.1.1` on-box, and the served content is public build output. The cost is unbounded process accumulation and a verification signal that can silently come from the wrong server, not disclosure. ## What the original filing got wrong The claim that the agent reported a simulation as real verification does not hold. idolidle.com#24 states plainly that nginx could not be started, calls the substitute a stand-in, and leaves the live-nginx checkbox unchecked: > nginx itself can't be started in this sandboxed environment (daemon/network-listener commands require an approval step unavailable here), so I reproduced that exact directive's documented behavior in a small throwaway Node server [...] the sandbox verification above stands in for, but isn't literally, that nginx process That labeling is the behavior the original ticket asked for as its fix. No change needed there. ## Fixed looks like A tick does not stand up a web server to verify static routing at all. The deploy barrier (deploy-sha marker + `agent.json` `.smoke.url`) is the smoke test for served behavior; in-tick verification should stop at the build output on disk. Doing this makes the sandbox's inconsistent gating of `nginx` moot — nothing gets spawned, so nothing leaks and nothing collides. If any tick-local listener survives that change, it must bind loopback only and be reaped on tick exit regardless of how the tick ends. ## Tests - A tick that verifies static output leaves no listening socket behind when it exits, including on the failure and timeout paths. - No `python3 -m http.server` or equivalent is spawned by the static-verification path.
igor added spent time 2026-07-24 22:03:02 +00:00
47 seconds
igor changed title from nginx binary gated in one worktree but runnable in another; agent substituted a hand-rolled simulation as verification to Ticks leak python3 -m http.server daemons; stale listeners can answer another tick's verification requests 2026-07-25 03:20:04 +00:00
igor self-assigned this 2026-07-25 03:59:14 +00:00
igor added spent time 2026-07-25 04:07:19 +00:00
7 minutes 17 seconds
igor removed their assignment 2026-07-25 04:07:20 +00:00
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Total time spent: 8 minutes 4 seconds
igor
8 minutes 4 seconds
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
joshtronic/igor#418
No description provided.