Watchdog incorrectly terminates sleeping non-admin capsule run loops
Problem
Astrid’s run-loop watchdog can terminate a non-admin capsule that is legitimately sleeping between operations rather than consuming CPU.
Observed with Codewall’s protocol capsule: once the background loop terminates, policy polling and telemetry uploads stop. The separate enforcer may continue enforcing its previously loaded policy.
Reproduction
Reproduced on Linux using Astrid source commit d3026e39 (reports version 2026.9.2) and Codewall 09df66f. The relevant watchdog implementation remains unchanged on Astrid main at 702f07bb.
- Create a principal in the
agentgroup, without resource exemptions. - Install the Codewall protocol capsule under that principal.
- Supply a malformed enrolment token (
cwt_invalid) to exercise its normal error/retry loop without contacting a server. - Leave the capsule running.
Codewall waits approximately five minutes before retrying, using sleep chunks of at most 60 seconds and checking subscriptions between sleeps.
The background loop was terminated after 240 seconds, while sampled container CPU usage was approximately 0.02–0.03%. This was reproduced with both the uninstrumented daemon and a logging-only diagnostic build.
The diagnostic build identified:
no_yield_windows=3 window_ticks=50
WASM background loop failed capsule=codewall-protocol
Caused by: wasm trap: interruptAn earlier test with an enrolled endpoint also reproduced the failure. The minimal reproduction requires no server, collector, scheduler or network connection.
Cause
configure_run_store uses recv_yielded to distinguish cooperative work from a spinning loop. IPC receive and TCP accept mark progress, but sleep_ns does not.
Consequently, completed sleeps and subsequent wake-ups do not reset the watchdog’s no-progress counter.
The relevant implementation remains unchanged on main at 702f07bb.
Expected behavior and fix scope
Legitimate sleeping run loops should remain alive, while genuinely non-yielding CPU loops must still be interrupted.
A temporary local change marking completed, nonzero sleeps as cooperative progress allowed the same capsule to remain alive for seven minutes and complete its next retry, with unchanged permissions.
Please correct the wait accounting and add regressions covering both legitimate sleeping loops and actual CPU-spinning loops. This should remain a focused runtime correction, without granting admin privileges, disabling the watchdog or adding scheduling infrastructure.
Source: astrid-runtime/astrid