Handoff: ContainerNursery #477 kotlin.build red — HttpFacade per-request-HttpClient flake fixed (merged); a *separate* pre-existing URL-facade libp2p entropy flake root-caused but not yet fixed
Original user request (verbatim):
Original user request (verbatim):
Moves it out of every priority list and into ArchiveArea.
Written: 2026-06-30 (authored/paused). Workstream dated 2026-06-30.
⚠️ RE-VERIFY BEFORE ACTING
Every state claim below was true only at write time. PRs merge, branches get force-pushed, CI re-runs, the buildtest droplet restarts, and flakes fire intermittently. Re-verify before you act on anything here. Authoritative checks:
- PRs:
gh pr view <num> --repo CodexCoder21Organization/<repo> --json state,mergeStateStatus,statusCheckRollup,mergedAt,headRefOid- kotlin.build run:
gh api repos/CodexCoder21Organization/ContainerNursery/commits/<sha>/check-runs --jq '.check_runs[] | select(.name=="kotlin.build (remote)") | {status,conclusion,output:.output.summary,details_url}'- buildtest run page:
curl -s "https://buildtest.kotlin.build/run?id=<id>"A fresh query always wins over this document.
Original user request (verbatim):
"Use your flakey test skills to resolve https://github.com/CodexCoder21Organization/ContainerNursery/pull/477/checks?check_run_id=84176919609"
That check was kotlin.build (remote) on ContainerNursery PR #477 (the "Round 2 extreme-condition bug fixes" PR: child-PID-reuse kill, TCP lingering-client leak, metrics CME). The failing run (c6dca45b) reported 287 passed, 1 failed — the one failure was testHttpFacade_xForwardedHeadersReachBackendOverWire with SocketTimeoutException: Read timed out.
The effort then expanded in two ways:
testUrlFacade_autoStartPersistentRouteAndRestartResilience), which the user explicitly asked to fix ("Also fix the P2P flake now"). That second fix is root-caused but not implemented — its approach was awaiting a user decision when this handoff was written.Bottom line at write time: the original task is DONE — #477 is MERGED with kotlin.build (remote) GREEN. The URL-facade libp2p entropy flake is still live (pre-existing, unrelated to #477, unfixed) and will keep firing intermittently on the contended droplet.
testHttpFacade_xForwardedHeadersReachBackendOverWire — FIXED & MERGEDGET … Connection: close, then readBytes() (read-to-EOF) at tests/testHttpFacade_xForwardedHeadersReachBackendOverWire.kts:160 — timed out after the 10 s soTimeout.HttpClient(CIO) per request — real production behaviour, DockerBackedContainer.handle (src/main/kotlin/org/example/docker/DockerBackedContainer.kt:74), and mirrored by the test's fake container. On the contended 2-core droplet the one-time CIO-engine cold-start (~7 s in the failed run's thread timeline) ate most of the 10 s read budget → timeout. Locally in a quiet env it passes (I ran it: scripts/test.bash --test testHttpFacade_xForwardedHeadersReachBackendOverWire → 1/1 PASSED). It is the only test doing a real per-request CIO forward, which is why it was the only one flaking.HttpClient per container, closed at shutdown/kill; the test's fake rebuilt to construct its client once at setup, outside the read window; adds testHttpFacade_concurrentForwardsDoNotStallSharingOneClient). #474 and #477 touched disjoint files, so the fix comes in cleanly by rebasing.main at 2026-06-30T01:02:10Z (merge commit fe1061246).extreme-condition-bugfixes-2) onto the new main — conflict-free — bringing fe1061246 in as an ancestor. Verified the flaky test still passes locally against the rebased tree.2091bfb → 12d97e6, later amended to 1a79fd5).These are the recurring buildtest/kotlin-build-ci infra problems (documented across prior handoffs). Each cost a ~20 min cycle:
12d97e6 (6612c3f7) — Build infrastructure error: SandboxException: SJVM host-side RPC interrupted during method=getBuildRun … InterruptedException at BuildRunner.pollUntilComplete(BuildRunner.kt:1367). The buildMaven() build rule showed FAILED at 0 ms and no tests ran. Orchestrator interrupted/restarted mid-build.1a79fd5) — the kotlin-build-ci-test app created an (empty) check-suite but never a check-run → the push webhook was dropped (the receiver races GitHub's 10 s delivery limit). GHA checks ran fine; kotlin.build simply never started.bld-all-tests (GitHub Actions) flaked once with a kompile test-runner internal NPE Cannot invoke "Object.toString()" because "this.context" is null — cleared by re-running (gh run rerun <id> --failed).testUrlFacade_autoStartPersistentRouteAndRestartResilience — ROOT-CAUSED, NOT FIXEDb83f5536): 287 passed, 1 failed, and the HttpFacade flake (2a) PASSED — confirming the #474 fix works. The new failure was testUrlFacade_autoStartPersistentRouteAndRestartResilience: timed out after 30000 ms (actual 38127 ms).main (added before #472); #477 does not touch the URL facade or the P2P stack. Verified: git log origin/main --oneline -1 -- tests/testUrlFacade_autoStartPersistentRouteAndRestartResilience.kts; and #477's diff touches only EventStore/NurseryPidFile/SystemMetricsProjection/TcpFacade + 3 new tests./dev/random entropy starvation in libp2p host crypto. In the failed console there is a clean ~10 s gap between [UrlFacade] Saved persistent config (…33.416) and [UrlFacade] Local peer ID: (…43.466) — no interleaved log lines, the signature of a single blocking syscall. The chain:
UrlProtocol2(bootstrapPeers = emptyList()) with no key seed → persistentKey = null (UrlResolver.kt:817).Libp2pHostFactory.createHost(keyPair = null) (UrlResolver.kt:3571) → identity { keyPair ?: generateRandomEd25519Key() } (UrlProtocol Libp2pHostFactory.kt:619).generateRandomEd25519Key() → generateKeyPair(ED25519, 0, sharedKeyGenSecureRandom) (Libp2pHostFactory.kt:568).sharedKeyGenSecureRandom is a default new SecureRandom() (Libp2pHostFactory.kt:470) = NativePRNG on Linux. Its own code comment (lines 460-464) admits it "can block on /dev/random for seconds" on entropy-starved hosts. The shared/eager-seed optimisation only avoids repeated blocking; the first seeding in a fresh test JVM still blocks.NoiseXXSecureChannel.localStaticPrivateKey25519 = ByteArray(32).also { Noise.random(it) } (jvm-libp2p libp2p/src/main/kotlin/io/libp2p/security/noise/NoiseXXSecureChannel.kt:60); Noise.random is the external noise-java lib's internal SecureRandom.ContainerNursery/scripts/test.bash:34-42, which applies -Djava.security.egd=file:/dev/./urandom only to the kompile JVM. That flag does not reach the separate test JVM kompile spawns via executeTestInSeparateJvm, so the test JVM seeds from blocking /dev/random. The first seeding blocks ~9-15 s (= the observed 10 s gap). The test starts two hosts → past the 30 s budget under contention./dev/random is non-blocking, so the block cannot fire. I verified that pointing securerandom.source at a blocking FIFO does not cleanly discriminate NativePRNG from NativePRNGNonBlocking (both stall Sun-provider SeedGenerator init). The discriminating behaviour only exists against a real blocking /dev/random. NativePRNGNonBlocking is available on this JDK (java 21.0.11, SUN provider) and reads /dev/urandom only.After I paused (pending the flake-approach decision), kotlin.build (remote) was re-run and PASSED (6d57d979, 11m37s) — the intermittent URL flake simply did not fire that time — and #477 MERGED at 2026-06-30T06:10:34Z (merge commit 4ae4899f). So the original task completed. The URL entropy flake remains latent.
| Ref | State @ write | Link |
|---|---|---|
| ContainerNursery #477 (extreme-condition bugfixes 2) | MERGED 2026-06-30T06:10:34Z, merge commit 4ae4899f; all checks incl. kotlin.build (remote) GREEN (run 6d57d979) | https://github.com/CodexCoder21Organization/ContainerNursery/pull/477 |
| ContainerNursery #474 (reuse one HttpClient per container) | MERGED 2026-06-30T01:02:10Z, merge commit fe1061246 | https://github.com/CodexCoder21Organization/ContainerNursery/pull/474 |
| Original failing run (HttpFacade flake) | 287 pass / 1 fail | https://buildtest.kotlin.build/run?id=c6dca45b |
| Infra-error run (getBuildRun InterruptedException) | build rule FAILED 0ms, 0 tests ran | https://buildtest.kotlin.build/run?id=6612c3f7 |
| URL-facade flake run | 287 pass / 1 fail (testUrlFacade_autoStartPersistentRouteAndRestartResilience 30 s timeout) | https://buildtest.kotlin.build/run?id=b83f5536 |
| Final green run that let #477 merge | PASS | https://buildtest.kotlin.build/run?id=6d57d979 |
No uncommitted code changes exist in any repo. The only code change made (the #474 rebase into #477) is merged. The entropy fix (§4) was intentionally not written as code — its approach was pending a user decision. Clones inspected during investigation (/code/UrlProtocol @ 4d1c220 main, /code/jvm-libp2p @ dc4a571 develop, /code/UrlResolver @ 783a93af main) are unmodified.
Dependency versions relevant to the fix (verify current before acting):
build.kts: foundation.url:protocol:0.0.305 (line ~142), foundation.url:resolver:0.0.557 (line ~219). Tests pin community.kotlin.libp2p:jvm-libp2p:1.3.0-codexcoder21-snapshot-8 (some files still pin -snapshot-5 → the harmless-but-noisy WithArtifactVersionMismatchWarningEffect seen in local builds).main build.kts: foundation.url:protocol:0.0.320, jvm-libp2p:1.3.0-codexcoder21-snapshot-6.Re-verify current state first (the URL flake is intermittent — confirm it is still firing before investing). Search recent kotlin.build failures for testUrlFacade_autoStartPersistentRouteAndRestartResilience timing out, or any test whose console shows a multi-second gap before [UrlFacade] Local peer ID:.
The original ask (#477 kotlin.build green) is complete/merged — no action needed there. The remaining work is the URL-facade libp2p entropy flake, whose fix approach needs a user decision (I asked; it was not yet answered). The three candidate approaches, with tradeoffs:
A. Systemic entropy fix in the build harness (recommended — highest leverage, true root cause).
Make the spawned test JVMs on the remote builder use /dev/urandom. This fixes this flake and every other entropy-sensitive test (TLS handshakes, all libp2p/Noise tests) org-wide, in one place.
kotlin.build builder set -Djava.security.egd=file:/dev/./urandom on its kompile JVM, and does kompile forward it (or system properties) to the per-test JVM in executeTestInSeparateJvm? Local scripts/test.bash:42 sets it on the kompile JVM only.-Djava.security.egd=file:/dev/./urandom). If it does forward it but the remote builder doesn't set it: the fix is in the kotlin-build-ci / buildtest launcher config. Either is shared CI infra — treat with care; use investigate-broken-service / the relevant infra skill./dev/random"), but it touches shared infrastructure, so confirm scope/ownership before changing.B. Upstream crypto fix ("fix at the owning layer" per global rules — but whack-a-mole + multi-repo). Make the key-gen entropy sources non-blocking:
Libp2pHostFactory.kt:470 — change sharedKeyGenSecureRandom from java.security.SecureRandom() to SecureRandom.getInstance("NativePRNGNonBlocking") (with a try/catch NoSuchAlgorithmException fallback to SecureRandom() for non-Unix). This covers the identity key.NoiseXXSecureChannel.kt:60 — replace Noise.random(it) with a non-blocking SecureRandom.getInstance("NativePRNGNonBlocking").nextBytes(it) (same fallback). This covers the Noise static key. (Consider also Key.kt:127, Ed25519.kt:67, TLSSecureChannel.kt:123 defaults for full hardening.)1.3.0-codexcoder21-snapshot-9 (Gradle/Cloudsmith flow); fix + bump UrlProtocol → publish next foundation.url:protocol (0.0.321) with publish-maven-artifact; then bump ContainerNursery's build.kts protocol pin (and test jvm-libp2p pins). NOTE: the protocol change is internal/binary-compatible, so you can likely bump only CN's foundation.url:protocol pin to the new version and keep resolver:0.0.557 (Coursier mediates one protocol version; resolver picks up the fixed Libp2pHostFactory at runtime) — verify this on kotlin.build.SecureRandom's algorithm is a non-/dev/random source (e.g. NativePRNGNonBlocking) rather than the default NativePRNG. Fails on current code, passes after. (Testing .getAlgorithm() is a reasonable proxy since the fix's entire purpose is entropy-source selection; keep it package-level, don't add public API.)C. Do nothing structural / accept intermittency. Not acceptable under the "NEVER accept kotlin.build (remote) red" rule — but note the flake is low-frequency (it did not fire on the passing run that merged #477). If deprioritised, at minimum leave the root cause documented (this handoff / a memory entry) so the next person who sees it doesn't re-derive it.
Decision needed from the user: which of A / B to pursue (I recommended A, with B as the by-the-book upstream alternative). Confirm before committing to the multi-repo publish chain in B.
kotlin.build (remote) when it's dropped/failed on infra:
kotlin-build-ci-test (not a GHA workflow), fired on push / check_suite webhooks. A check_run rerequest does NOT re-trigger it (I tried — returns {} but nothing starts).check_suite rerequest DOES work when an (empty) suite exists: gh api --method POST repos/CodexCoder21Organization/ContainerNursery/check-suites/<suite_id>/rerequest (get the suite via gh api repos/.../commits/<sha>/check-suites --jq '.check_suites[] | select(.app.slug=="kotlin-build-ci-test")'). This started run b83f5536.git commit --amend --no-edit && git push --force-with-lease) reliably re-fires the webhook (but may itself be dropped — the receiver races GitHub's 10 s limit).queued kotlin-build-ci-test check-suite with no check-run for many minutes = dropped/slow webhook, not a wedged orchestrator. Confirm the orchestrator is alive by checking https://buildtest.kotlin.build/ for other repos currently TESTING/COMPLETED.Build infrastructure error … getBuildRun … InterruptedException at BuildRunner.pollUntilComplete = the orchestrator/SJVM was interrupted/restarted mid-build; buildMaven() shows FAILED at 0 ms and no tests run. It is an infra flake, not a code/test failure — re-trigger.gh api repos/CodexCoder21Organization/ContainerNursery/commits/<sha>/check-runs --jq '.check_runs[] | select(.name=="kotlin.build (remote)") | .output.summary' gives the failed-test list / infra error inline. The per-test console (with the [UrlFacade] timestamps that reveal the 10 s gap) is in the run HTML: curl -s "https://buildtest.kotlin.build/run?id=<id>" then grep around the test's data-test-name.origin HTTPS remote needs gh auth setup-git (raw git fetch/push otherwise errors could not read Username). Commit identity used here: codexcoder21 <laserjim@gmail.com> (matches the branch's existing commits). Rebase needs the identity set before git rebase or it aborts mid-replay.cd ContainerNursery && scripts/test.bash --test <name>. First run compiles everything (slow). The WithArtifactVersionMismatchWarningEffect (jvm-libp2p snapshot-5 vs snapshot-8 across test files) is non-fatal noise locally and does not fail the remote builder — separate pre-existing cleanup, out of scope here./dev/random block on a kernel where /dev/random actually blocks (e.g. the droplet's kernel 5.4). On modern kernels (≥5.6) it's non-blocking. A blocking-FIFO securerandom.source does not cleanly separate NativePRNG from NativePRNGNonBlocking (both stall Sun SeedGenerator init) — don't waste time on it.