← Back to PRs

#13798: fix(macos): prevent PortGuard from killing Docker Desktop in remote mode

by teslamint open 2026-02-11 02:02 View on GitHub →
app: macos size: S
## Summary - Problem: PortGuardian.sweep() kills Docker Desktop when the gateway runs in a container in remote mode - Why it matters: In remote mode, the gateway port is externally managed (Docker port-forward, SSH tunnel, direct WS, etc.), so any process holding it should be accepted — not terminated - What changed: Added defense-in-depth guard in `sweep()` that skips kill in remote mode; refactored `isExpected()` to accept any process on gateway port in remote mode - What did NOT change (scope boundary): Local mode behavior is fully preserved — unexpected processes on the gateway port are still terminated as before ## Change Type (select all) - [x] Bug fix - [ ] Feature - [ ] Refactor - [ ] Docs - [ ] Security hardening - [ ] Chore/infra ## Scope (select all touched areas) - [x] Gateway / orchestration - [ ] Skills / tool execution - [ ] Auth / tokens - [ ] Memory / storage - [ ] Integrations - [ ] API / contracts - [ ] UI / DX - [ ] CI/CD / infra ## Linked Issue/PR - Closes #6755 ## User-visible / Behavior Changes In remote mode, Docker Desktop (and other port-forwarding processes like Podman, SSH) are no longer killed by PortGuardian. Local mode behavior is unchanged. ## Security Impact (required) - New permissions/capabilities? `No` - Secrets/tokens handling changed? `No` - New/changed network calls? `No` - Command/tool execution surface changed? `No` - Data access scope changed? `No` ## Repro + Verification ### Environment - OS: macOS - Runtime/container: Docker Desktop (remote mode gateway) ### Steps 1. Run the gateway inside a Docker container in remote mode 2. Docker Desktop holds the gateway port via port-forwarding 3. PortGuardian.sweep() fires ### Expected - Docker Desktop is NOT killed; the port-forwarding process is accepted as legitimate ### Actual (before fix) - Docker Desktop is killed because PortGuardian treats it as an unexpected process on the gateway port ## Evidence - [x] Failing test/log before + passing after - [ ] Trace/log snippets - [ ] Screenshot/recording - [ ] Perf numbers (if relevant) | Test | Status | |------|--------| | `portGuardianRemoteModeDoesNotKillDocker` | ✅ Exact #6755 scenario regression | | `portGuardianLocalModeStillRejectsUnexpected` | ✅ Local mode behavior preserved | | `portGuardianRemoteModeReportAcceptsAnyListener` | ✅ buildReport path verified | | All 16 tests in `LowCoverageHelperTests` | ✅ Pass locally | ## Human Verification (required) - Verified scenarios: All 3 new regression tests pass locally; all 16 existing tests in `LowCoverageHelperTests` continue to pass - Edge cases checked: Local mode still rejects unexpected processes; remote mode accepts Docker, SSH, and Podman processes - What you did **not** verify: Production remote mode environment with actual Docker Desktop (tested via unit tests with `_testIsExpected()` helper) ## Compatibility / Migration - Backward compatible? `Yes` - Config/env changes? `No` - Migration needed? `No` ## Failure Recovery (if this breaks) - How to disable/revert this change quickly: Revert the `sweep()` remote-mode guard and `isExpected()` changes in `PortGuardian.swift` - Files/config to restore: `PortGuardian.swift` - Known bad symptoms reviewers should watch for: Unexpected processes surviving on the gateway port in local mode ## Risks and Mitigations - Risk: In remote mode, a rogue (non-Docker/SSH) process holding the gateway port will no longer be killed - Mitigation: This is by design — remote mode assumes the port is externally managed, providing defense-in-depth. Local mode retains full kill behavior. ## Changes | File | Change | |------|--------| | `PortGuardian.swift` | `isExpected()`: refactor to `static`, accept any process on gateway port in remote mode | | `PortGuardian.swift` | `sweep()`: add safety net — skip kill in remote mode | | `PortGuardian.swift` | `buildReport()`: update remote mode description and predicate | | `PortGuardian.swift` | Add `_testIsExpected()` test helper in `#if DEBUG` | | `LowCoverageHelperTests.swift` | Add 3 regression tests | 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- greptile_comment --> <h2>Greptile Overview</h2> <h3>Greptile Summary</h3> Fixed `PortGuardian` from killing Docker Desktop (and other port-forwarding processes) when the gateway runs in remote mode. In remote mode, the gateway port is externally managed (via Docker port-forward, SSH tunnel, or direct WebSocket connection), so any process holding the port should be accepted rather than terminated. **Key changes:** - `isExpected()`: Refactored to `static` method, now returns `true` for any process on gateway port in remote mode (previously only accepted SSH) - `sweep()`: Added defense-in-depth guard that skips kill entirely in remote mode before attempting termination - `buildReport()`: Updated remote mode description from "SSH tunnel to remote gateway" to "Remote gateway (SSH tunnel, Docker, or direct)" and changed predicate to accept any listener - Added comprehensive test coverage with 3 regression tests validating the fix <h3>Confidence Score: 5/5</h3> - This PR is safe to merge with no issues found - The fix is well-designed with defense-in-depth (two separate checks in different code paths), comprehensive test coverage validates all three scenarios (remote mode accepts Docker/SSH/Podman, local mode still rejects unexpected processes, buildReport path works correctly), and the logic is sound - remote mode port forwarding is externally managed so any process holding the port is legitimate - No files require special attention <!-- greptile_other_comments_section --> <sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub> <!-- /greptile_comment -->

Most Similar PRs