Release v0.51.234 — Release HB (stage-q4) (#3488)
Some checks failed
Release & Docker / release (push) Has been cancelled

## Release v0.51.234 — Release HB (stage-q4)

Two medium-risk backend/infra fixes. All gates green.

### Fixes
| PR | Author | Fix |
|----|--------|-----|
| #3289 | @rodboev | Refuse server startup when a live instance already serves the port (Windows/macOS silent port-sharing hazard). Live-listener probe (`GET /health`, 2s timeout) + Windows `SO_EXCLUSIVEADDRUSE` — **preserves fast restart** (POSIX keeps `allow_reuse_address=True`; a dying socket in the kernel backlog times out → startup proceeds). |
| #3486 | @dso2ng | Allow remote/SSH terminal profiles to use target-side workspace paths under `terminal.cwd` without a server-local `stat()`. Local profiles unchanged — bypass only fires for remote backends and only for paths contained within `terminal.cwd`. |

### History note on #3289
This PR was **held earlier this sweep** — its original form globally disabled `SO_REUSEADDR`, which a Codex gate flagged as breaking fast restart (TIME_WAIT bricks rebind for ~60s). The contributor reworked it along the suggested lines (live-listener probe instead of the global disable). This release ships the reworked version. Unheld → full pickup → full gate.

### Gate results
- **Full pytest suite**: 7458 passed, 8 skipped, 3 xpassed, **0 failed**
- **ruff forward gate**: CLEAN
- **browser-smoke gate**: CLEAN (real server boots fine with the new startup probe)
- **Codex (regression)**: SAFE TO SHIP (verified fast-rebind preserved + remote bypass gated on backend+containment, local validation unchanged)
- **Opus (correctness + security)**: SAFE TO SHIP (probe false-positive, `_is_within` containment, local-profile bypass all hold up; applied its one minor double-call cleanup note)

Closes #3289.

Co-authored-by: rodboev <rodboev@users.noreply.github.com>
Co-authored-by: dso2ng <dso2ng@users.noreply.github.com>
This commit is contained in:
nesquena-hermes
2026-06-03 09:53:32 -07:00
committed by GitHub
parent 7b02883dcf
commit ed46c65719
5 changed files with 269 additions and 9 deletions

View File

@@ -184,6 +184,13 @@ class QuietHTTPServer(ThreadingHTTPServer):
self.accept_loop_requests_total = 0
self.accept_loop_last_request_at = 0.0
def server_bind(self):
if sys.platform == 'win32':
self.allow_reuse_address = False
SO_EXCLUSIVEADDRUSE = getattr(socket, 'SO_EXCLUSIVEADDRUSE', -5)
self.socket.setsockopt(socket.SOL_SOCKET, SO_EXCLUSIVEADDRUSE, 1)
super().server_bind()
def _handle_request_noblock(self):
"""Record accept-loop progress before dispatching a request handler.
@@ -477,6 +484,25 @@ def _log_shutdown_audit(reason: str = "serve_forever_exit") -> None:
)
def _abort_if_already_serving(host: str, port: int) -> None:
"""Refuse to start if a live HTTP server is already responding on this port."""
probe_host = '127.0.0.1' if host in ('0.0.0.0', '', '::') else host
try:
with socket.create_connection((probe_host, port), timeout=2) as s:
s.sendall(b'GET /health HTTP/1.0\r\nHost: localhost\r\n\r\n')
s.settimeout(2)
data = s.recv(512)
if data:
print(
f'[!!] FATAL: Another server is already responding on'
f' {probe_host}:{port}. Stop the existing instance first.',
flush=True,
)
sys.exit(1)
except (ConnectionRefusedError, ConnectionResetError, OSError, socket.timeout):
pass
def main() -> None:
from api.config import print_startup_config, verify_hermes_imports, _HERMES_FOUND
@@ -572,6 +598,7 @@ def main() -> None:
except Exception as e:
print(f'[!!] WARNING: Plugin loading failed: {e}', flush=True)
_abort_if_already_serving(HOST, PORT)
httpd = QuietHTTPServer((HOST, PORT), Handler)
# ── TLS/HTTPS setup (optional) ─────────────────────────────────────────
@@ -597,6 +624,7 @@ def main() -> None:
try:
httpd.serve_forever()
finally:
httpd.server_close()
_log_shutdown_audit()
# Stop the gateway watcher on shutdown
try: