| 1 | | Finding | Disposition | Reason | |
| 2 | |---|---|---| |
| 3 | | 10 | **Closed for the original bypass** | The listener now requires a successful self-test, and both listener and proxy are bound to that test’s lifetime. §6.2 also requires scanner-side readiness before wiring. A separate boot-ordering defect remains below. | |
| 4 | | 11 | **Closed** | Both scan limits are fixed at 100 MiB; §3.5 bounds the configurable stream limit so the specified fixtures fit. | |
| 5 | |
| 6 | Reviewed draft 5 at `edee53d4`. No files changed or live-box tests performed. |
| 7 | |
| 8 | 12. **P2 — §§3.2–3.3: The gated socket creates a boot-ordering cycle with systemd’s default dependencies.** |
| 9 | |
| 10 | The [specified socket dependency](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/scanner-host/docs/superpowers/specs/2026-10-04-scanner-host-rollout-design.md:109) orders `scanner-tcp.socket` after `scanner-selftest.service`, but neither unit disables default dependencies. On reboot, this produces the following cycle, where each arrow means “must start before”: |
| 11 | |
| 12 | ```text |
| 13 | scanner-tcp.socket |
| 14 | → sockets.target |
| 15 | → basic.target |
| 16 | → scanner-selftest.service |
| 17 | → scanner-tcp.socket |
| 18 | ``` |
| 19 | |
| 20 | **Code evidence:** systemd adds `Before=sockets.target` to sockets in [`src/core/socket.c:262`](https://github.com/systemd/systemd/blob/v257/src/core/socket.c#L262), adds `After=basic.target` to services in [`src/core/service.c:712`](https://github.com/systemd/systemd/blob/v257/src/core/service.c#L712), and orders `basic.target` after `sockets.target` in [`units/basic.target:13`](https://github.com/systemd/systemd/blob/v257/units/basic.target#L13). |
| 21 | |
| 22 | Systemd resolves an ordering cycle by deleting a dispensable job or rejecting the transaction; it cannot execute the specified startup sequence intact. See [`src/core/transaction.c:351–395`](https://github.com/systemd/systemd/blob/v257/src/core/transaction.c#L351). Consequently, the scanner can remain unavailable after reboot. Initial cloud-init startup and §3.3’s kill/restart checks can miss this because `basic.target` and `sockets.target` have already completed. |
| 23 | |
| 24 | **Fix:** Specify `DefaultDependencies=no` for `scanner-tcp.socket`, restore its initialization and shutdown dependencies explicitly, and arrange activation through clamd or `multi-user.target` without ordering it before `sockets.target`. Retain the self-test dependencies. Add a reboot acceptance check and verify the complete boot transaction for ordering cycles. |
| 25 | |
| 26 | The other requested mechanisms do not produce additional findings: |
| 27 | |
| 28 | - **Self-test state:** `Type=oneshot` delays ordered dependents until successful completion. `RemainAfterExit=yes` retains that success as `active (exited)`; it neither overrides `BindsTo=` nor preserves success across reboot. A failed test cannot authorize the listener. [Service semantics](https://manpages.debian.org/trixie/systemd/systemd.service.5.en.html). |
| 29 | - **Clamd restart and package restart:** `BindsTo=` plus `After=` stops dependents when clamd becomes inactive or fails; explicit restarts also propagate. Applying this to **both** TCP units removes the listener and terminates the proxy holding established connections. `Requires=` alone does not cover unexpected deactivation. [Dependency semantics](https://manpages.debian.org/trixie/systemd/systemd.unit.5.en.html). |
| 30 | - **Automatic recovery:** With the default `RestartMode=normal`, automatic restart creates a new start transaction with dependency restart propagation. That transaction processes `Wants=scanner-tcp.socket`, which pulls in the self-test again. This is supported by [`service.c:2405–2418`](https://github.com/systemd/systemd/blob/v257/src/core/service.c#L2405) and [`transaction.c:937–947`](https://github.com/systemd/systemd/blob/v257/src/core/transaction.c#L937). |
| 31 | - **INSTREAM:** `systemd-socket-proxyd` forwards bidirectional stream bytes between TCP and Unix sockets. The client’s [INSTREAM implementation](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/scanner-host/internal/clamd/clamd.go:195) uses byte framing, without descriptor passing, so the proxy supports its protocol. [Proxy semantics](https://manpages.debian.org/trixie/systemd/systemd-socket-proxyd.8.en.html). |
| 32 | |
| 33 | After fixing the boot cycle, the gating design is sound at spec level. Actual restart timing, connection termination and EICAR transport belong to the real-box acceptance checks. |
| 34 | |
| 35 | **Verdict: accept with fixes.** |