Skip to content

Fix macOS pf port forwarding lost after reboot/sleep#98

Merged
jakwinkler merged 6 commits into
mainfrom
osx_improvements
Apr 23, 2026
Merged

Fix macOS pf port forwarding lost after reboot/sleep#98
jakwinkler merged 6 commits into
mainfrom
osx_improvements

Conversation

@jakwinkler
Copy link
Copy Markdown
Contributor

Summary

  • Fixes #95 — PF port forwarding rules (80→8080, 443→8443) were wiped on reboot and sleep/wake, breaking all .test domains
  • Rewrites the LaunchDaemon to use a dedicated helper script (/usr/local/bin/magebox-pf-restore) with proper logging to /var/log/magebox-portforward.log
  • magebox start now auto-restores pf rules if they're missing (self-healing safety net)
  • magebox check now reports Port Forwarding status on macOS

What changed

File Change
internal/portforward/pf.go New EnsureRulesActive() method, helper script instead of inline one-liner, launchctl bootstrap/bootout with legacy fallback, exported AreRulesActive(), bumped daemon version to force upgrade
cmd/magebox/start.go Calls ensurePortForwarding() on macOS before project startup
cmd/magebox/check.go New "Port Forwarding" section showing LaunchDaemon + PF Rules status
VERSION 1.15.1 → 1.16.0
CHANGELOG.md Added 1.16.0 release entry

Why the old approach broke

The LaunchDaemon plist used an inline shell one-liner with 2>/dev/null everywhere and exit 0 at the end, making all failures invisible. Additionally, launchctl load -w (deprecated) could fail silently on modern macOS. Since magebox start never checked pf state, there was no recovery path.

Test plan

  • Run magebox bootstrap on macOS — should install helper script and upgraded LaunchDaemon
  • Verify /usr/local/bin/magebox-pf-restore exists and is executable
  • Verify sudo launchctl list com.magebox.portforward shows the daemon loaded
  • Reboot Mac → domains should resolve within ~5 seconds of boot
  • Sleep/wake Mac → domains should still work
  • Run magebox start after reboot without bootstrap → should auto-restore pf rules
  • Run magebox check → should show Port Forwarding section with LaunchDaemon and PF Rules status
  • Check /var/log/magebox-portforward.log for restore activity after reboot
  • Run go test ./... — all tests pass

🤖 Generated with Claude Code

PF rules (80→8080, 443→8443) were wiped on reboot and sleep/wake,
breaking .test domains until manual intervention. Root causes:

- LaunchDaemon used an inline one-liner that swallowed all errors
  (2>/dev/null + exit 0), making failures invisible
- launchctl load (deprecated) could fail silently on modern macOS
- magebox start never verified pf state, so there was no safety net

Fix:
- Replace inline plist command with dedicated helper script
  (/usr/local/bin/magebox-pf-restore) that logs to
  /var/log/magebox-portforward.log for diagnosability
- Use launchctl bootstrap/bootout with legacy load/unload fallback
- Add EnsureRulesActive() called on every magebox start (macOS only)
  — verifies rules, restores if missing, reports to user
- Add Port Forwarding section to magebox check for diagnostics
- Bump LaunchDaemon version to force upgrade on existing installs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@jakwinkler jakwinkler requested a review from peterjaap April 22, 2026 19:09
jakwinkler and others added 3 commits April 22, 2026 21:18
When PHP is installed via `brew install php` (the default/current
formula), it lives in Cellar/php/8.4.x/ — not Cellar/php@8.4/.
Both PHPBinary/PHPFPMBinary in Go and find_php_binary in the wrapper
script now check the unversioned Cellar path as a fallback, matching
the version prefix (e.g. 8.4.*) to avoid false positives.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Script log() now writes to stdout (captured by launchd) instead of
  double-writing to the log file via >>. Fixes missing log messages.
- pfctl stderr merged to stdout (2>&1) so all output goes to one stream.
- magebox restart now calls ensurePortForwarding() like start does.
- Bump daemon version to 6 to deploy fixed script on next bootstrap.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
macOS pf rules were fundamentally unreliable — Apple's own pf
management would override custom anchors after reboot and sleep/wake,
silently breaking all .test domains.

New approach: a persistent TCP proxy daemon (magebox _portforward)
that listens on 80/443 and forwards to nginx on 8080/8443. Managed
by launchd with KeepAlive:true — survives reboot, sleep/wake, and
process crashes automatically.

- Add internal/portforward/proxy.go: lightweight TCP forwarder
- Add cmd/magebox/portforward_daemon.go: hidden _portforward command
- Rewrite pf.go: LaunchDaemon now runs magebox _portforward instead
  of manipulating kernel pf rules
- Clean up legacy pf files on upgrade (anchor, helper script, pf.conf)
- Update bootstrap message and CHANGELOG

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
jakwinkler and others added 2 commits April 23, 2026 08:22
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@jakwinkler jakwinkler merged commit 94dc0a2 into main Apr 23, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Mac OS: pf rules not added automatically on install and not reloaded after reboot

1 participant