diff --git a/docs/platform-flags.md b/docs/platform-flags.md
index 8baace3..b4a69d7 100644
--- a/docs/platform-flags.md
+++ b/docs/platform-flags.md
@@ -388,6 +388,19 @@ agent of our own plus ProxyJump covers the real use cases.
**The SSH suite pulls `linuxserver/openssh-server` from Docker Hub**, which is rate-limited for
unauthenticated pulls. If CI starts failing on image pulls rather than on tests, that is why.
+**That suite has an intermittent `The connection was closed by the remote host`**, on whichever test
+connects first, within tens of milliseconds. Seen in CI and reproducible locally. *Mitigated, not
+solved:* `SshServerFixture` now raises sshd's `MaxStartups` from its compiled-in `10:30:100`, which
+refuses connections at random past ten unauthenticated ones in flight — reachable because xUnit runs
+test classes in parallel and most of them connect. The fixture comment carries the full argument and
+is explicit that the cure is unproven.
+
+**And the reason it is unproven is a measurement trap worth not falling into twice.** Docker
+throughput on the Windows development machine swings enough to swamp the effect: the identical
+unmodified suite ran 85/85 clean and, an hour later, failed 13 runs out of 15. Any before/after flake
+comparison taken there is noise. Measure this class of thing in CI, or make the server say why —
+raise sshd's `LogLevel`, disable Ryuk so the container outlives the run, and read `docker logs`.
+
**MSIX packaging is ruled out, not merely deprioritised.** A packaged app runs WebView2 in an
AppContainer where loopback connections are blocked without a `CheckNetIsolation` exemption. The
terminal data plane *is* a loopback WebSocket, so MSIX would break the product outright. Velopack
diff --git a/tests/DodoSSH.Client.Ssh.Tests/SshServerFixture.cs b/tests/DodoSSH.Client.Ssh.Tests/SshServerFixture.cs
index 765cf21..22ebd31 100644
--- a/tests/DodoSSH.Client.Ssh.Tests/SshServerFixture.cs
+++ b/tests/DodoSSH.Client.Ssh.Tests/SshServerFixture.cs
@@ -112,6 +112,61 @@ public sealed class SshServerFixture : IAsyncLifetime
/// this image as hardening, not as a behaviour worth reproducing: nothing else here opens a channel of
/// any kind, so allowing it changes what exactly one suite can do and what none of the others see.
///
+ ///
+ /// ◆ MaxStartups is raised here too, against a flake this suite has and that this change is
+ /// a mitigation for rather than a proven cure. The distinction is stated because the evidence
+ /// stops short of the claim, and a later reader deserves to know which.
+ ///
+ ///
+ /// What is established: sshd's compiled-in default is 10:30:100 — past ten
+ /// unauthenticated connections in flight it refuses new ones at random, thirty percent of the
+ /// time, rising to always at a hundred — and the image ships the line commented out, so that default
+ /// was what ran. xUnit runs test classes in parallel and most classes here open a connection, so ten
+ /// in flight is reachable in the opening seconds. A refused connection presents to the client as
+ /// SshConnectionException: The connection was closed by the remote host within tens of
+ /// milliseconds, on whichever test connects at the wrong moment — which is exactly the observed
+ /// failure, seen in CI and reproduced locally.
+ ///
+ ///
+ /// What is not established is that this limit is the only cause, because the flake rate could
+ /// not be measured reliably. On the development machine the identical unmodified suite ran 85/85 clean
+ /// and, an hour later, failed 13 runs out of 15 — Docker throughput on that host swings far enough to
+ /// swamp the effect being measured. Any before/after comparison taken there is noise, and two were,
+ /// before that was noticed.
+ ///
+ ///
+ /// It is committed anyway, on the narrower argument that it is right regardless: a connection throttle
+ /// is hardening this suite has no interest in reproducing. It exists to test an SSH client, not to
+ /// survive a rate limit, and a test server that drops connections at random is a bad test server
+ /// whether or not it is the cause of this particular flake.
+ ///
+ ///
+ /// Not fixed by serialising the suite, which would have hidden it and cost the parallelism, and
+ /// not by retrying the connect, which would have made the client's own reconnect behaviour untestable
+ /// by burying it in the fixture. The limit is a property of a hardened server that this suite has no
+ /// interest in reproducing — it exists to test an SSH client, not to survive a throttle.
+ ///
+ ///
+ /// Replaced in place rather than appended, because sshd_config takes the first value it finds
+ /// for a keyword: an appended line would be dead the day the image ships an uncommented one of its own.
+ ///
+ ///
+ /// ◆ The reload window is the other candidate, and it is deliberately not guarded against.
+ /// SIGHUP makes sshd close its listeners and re-execute itself, and pkill returns when
+ /// the signal is delivered rather than when that has finished — so in principle a connection made
+ /// immediately afterwards is refused, producing this same exception. A wait that opened connections
+ /// until the server answered with its banner three times running was written, and then removed: it
+ /// could not be shown to change anything either, and a fixture carrying two unproven fixes for one
+ /// symptom is worse than one, because the next person has to disprove both.
+ ///
+ ///
+ /// If this flake returns, that is the next thing to try. Two things to know before trying it: the two
+ /// causes are indistinguishable from the client, so a fix can only be judged by a repeat run and never
+ /// by whether the next run passes — and the repeat run has to happen somewhere with stable Docker
+ /// throughput, which the development machine is not. Better still, make sshd say why: raise its
+ /// LogLevel here, disable Ryuk so the container outlives the run, and read
+ /// docker logs. A MaxStartups refusal names itself there; a reload does not.
+ ///
///
private async Task AllowTcpForwardingAsync()
{
@@ -119,14 +174,16 @@ public sealed class SshServerFixture : IAsyncLifetime
"sh",
"-c",
"sed -i 's/^AllowTcpForwarding no/AllowTcpForwarding yes/' /config/sshd/sshd_config"
+ + " && sed -i 's/^#*MaxStartups .*/MaxStartups 200/' /config/sshd/sshd_config"
+ " && pkill -HUP sshd",
]);
if (result.ExitCode != 0)
{
throw new InvalidOperationException(
- $"Could not enable TCP forwarding on the test server: {result.Stderr}");
+ $"Could not reconfigure the test server: {result.Stderr}");
}
+
}
///