A listener on all interfaces silently receives a test node's connections on macOS; the conflict canary cannot see it
@dawsontoth ya está trabajando en esto.
Desde el 28/9/2026.
Evaluación
Este issue todavía no se ha evaluado.
Descripción
On macOS, a process listening on Harper's fixed ports on all interfaces, most commonly a local Harper instance running with its default config, receives a test node's connections whenever the node has no listener of its own on its loopback address. That happens before the node binds, during an HTTP-worker restart (non-overlapping on macOS), and after teardown. Clients with keep-alive, such as fetch, then stay connected to that process, so a suite fails, or passes, against the wrong server. The loopback pool's conflict canary does not detect this.
Reproduction
Machine: macOS with a Harper instance listening on *:9925, *:9926, *:1883, *:8883 (IPv6 dual-stack, started with harper run).
A suite starts a node through startHarper (it got 127.0.0.8), runs restart_service with service: 'http_workers', then sends GET /nonexistent with the harness admin credentials:
fetch(keep-alive):404 Not foundbefore the restart, then401 {"error":"Login failed"}on every later probe (20 over 10 s).- One fresh connection per probe:
404→ about 350 ms of401→404for the remaining 5 s.
lsof -nP [email protected] after the restart shows the probe connection's server end owned by the other Harper's PID, while the node is listening on 127.0.0.8:9926 again. The other instance has no admin / Abc1234! user, so it rejects the credentials. This was first investigated as a credential-store regression in Harper, because only restarts handed from a worker or job thread showed it. Those operations return before the replacement worker binds; a main-thread deploy_component with restart: true waits for it.
Why the canary misses it
findConflictingPort (src/loopbackAddressPool.ts) probes the canary ports with an exclusive bind. On macOS, Node/libuv sets SO_REUSEADDR, and BSD semantics let a bind to 127.0.0.x:P succeed next to another socket's wildcard listener on P. The bind probe therefore reports the address free. Measured with plain Node, Node 24.21.0:
| OS | wildcard listener | bind 127.0.0.1:P |
connect 127.0.0.1:P |
|---|---|---|---|
| macOS 26 (Darwin 25.6.0) | 0.0.0.0:P or [::]:P |
succeeds | accepted by the wildcard |
| Linux (node:24-slim container) | 0.0.0.0:P or [::]:P |
EADDRINUSE |
accepted by the wildcard |
When nothing else listens, a connection opened during the node's gap gets ECONNREFUSED. With a wildcard listener present, the kernel falls back to it instead.
On Linux the canary does fire, but it reports each address as still in use by another Harper node and keeps retrying with no deadline. That is a separate, lower-impact problem, tracked in #39.
Proposed fix
After the canary's bind succeeds, getNextAvailableLoopbackAddress also connect-probes the canary ports on the new address. No Harper node runs there yet, so an accepted connection means another process's listener covers the address. It then releases the slot and throws a ForeignListenerError that names the address and port, the likely cause, how to find the holder, and an opt-out: HARPER_INTEGRATION_TEST_ALLOW_FOREIGN_LISTENERS=1 turns the error into a warning. A refused loopback connect costs p50 0.03 ms / p99 0.6 ms (Linux) and 0.04 / 0.9 ms (macOS), and on Linux the bind canary catches this case first.
Out of scope, possible follow-ups:
- MQTT (1883/8883) and HTTPS (9927) are not probed. Probing them would fail every suite, MQTT or not, on a host with a resident broker; the host in #28 has one on the pool address. MQTT suites that connect across a restart (for example harper's
integrationTests/mqtt/qa649-mqtt-restart-wedge.test.ts) stay exposed to a broker listening on all interfaces. - A pre-set
ctx.harper.hostnameskips allocation, so it skips the check. - A foreign listener that starts after allocation is not detected.
Measured on
| Component | Version |
|---|---|
| @harperfast/integration-testing | 1.0.0 (origin/main 74ea958) |
| harper (system under test) | origin/main 360c60a72 |
| Node | v24.21.0 |
| OS | macOS 26, Darwin 25.6.0 (arm64); Linux via node:24-slim for the bind table |
- Lenguaje dominante
- TypeScript
- Estrellas
- 1
- Forks
- 0
- Métricas de merge de PR
- Sin PR fusionados en 30 d
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de HarperFast/integration-testing
-
setupHarperWithFixture overwrites ctx.harper, dropping pre-set hostname (breaks multi-node add_node)Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
-
enhancement good first issue
Dificultad 2/5 1-3 horas Aptitud para principiantes 85/100
-
Dificultad 3/5 1-2 días Aptitud para principiantes 74/100
-
Dificultad 4/5 3-5 días Aptitud para principiantes 58/100
-
Detached Harper children are orphaned permanently when the runner dies by SIGKILL/SIGHUP — reap guard only covers exit/SIGINT/SIGTERMQuizá libre de nuevo @kriszyp la tomó hace 41 días y no hay ningún pull request abierto. Abierto
HarperFast/integration-testing#29 · 1 comentario · 1 asignado ·
Todos los issues de HarperFast/integration-testing
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 88/100
callstackincubator/rozenite#518 ·
Los mantenedores suelen responder en 1 día
-
Area/Workflow Priority/Blocker Type/Bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
wso2/product-integrator#2622 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
-
area:bash bug has repro platform:macos
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
anthropics/claude-code#98644 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
allure-framework/allure-js#1603 ·
Los mantenedores suelen responder en 1 día