Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MenuBarNetToggle

MenuBarNetToggle is a small native macOS menu-bar utility that repeatedly keeps every network interface down except the interfaces you explicitly allow.

The application appears in the menu bar as NetCtl. It provides controls for enabling or disabling enforcement, editing the interface allowlist and enforcement interval, and starting the menu-bar application automatically when you sign in.

Caution

Enabling enforcement can immediately disconnect Wi-Fi, Ethernet, VPNs, virtual machines, AirDrop, device sharing, and other services if their interfaces are not in the allowlist. Identify the interfaces your Mac needs before turning enforcement on. Keep lo0 allowed so local loopback networking continues to work.

What it does

When enforcement is enabled, MenuBarNetToggle installs and starts a root-owned launch daemon named com.netctl.netenforce. The daemon enumerates the Mac's network interfaces with ifconfig -l. On each pass, it runs ifconfig <interface> down for every interface that is absent from the configured allowlist.

The default source configuration is:

ALLOWLIST="en0 utun4 lo0"
INTERVAL=15

Do not assume these defaults match another Mac. In particular, utun numbers can change as VPNs and system networking services start and stop.

MenuBarNetToggle does not:

  • inspect or capture packets;
  • send interface information to a remote service;
  • configure the macOS application firewall or Packet Filter (pf);
  • automatically restore interfaces that it previously brought down;
  • stop the root helper merely because the menu-bar application quits.

Status indicator

The menu-bar title reflects the helper state:

Indicator Meaning
NetCtl ○ The enforcement daemon is not running.
NetCtl ● The enforcement daemon has a live process and enforcement is active.

Open the menu to access:

  • Enforcement: On/Off — installs, starts, or stops the privileged helper. macOS requests administrator authorization.
  • Launch at Login — creates or removes a per-user LaunchAgent for the menu-bar application.
  • Settings… — edits the allowlist and interval. Saving requires administrator authorization because the configuration is root-owned.
  • Quit — exits only the menu-bar application. It does not stop active enforcement.

Architecture

MenuBarNetToggle consists of two independently managed processes:

User session
  MenuBarNetToggle.app
    Optional LaunchAgent: ~/Library/LaunchAgents/com.netctl.MenuBarNetToggle.plist

System session
  LaunchDaemon: /Library/LaunchDaemons/com.netctl.netenforce.plist
    /bin/sh /Library/Application Support/MenuBarNetToggle/enforce.sh
      Configuration: /Library/Application Support/MenuBarNetToggle/config.conf
      PID record: /var/run/com.netctl.netenforce.pid

The menu-bar application runs as the signed-in user. It asks macOS for administrator authorization when it needs to create, update, start, or stop the system helper. The helper and its configuration are then owned by root:wheel.

The launch-at-login setting and enforcement setting are intentionally separate:

  • Launch at Login can be on while enforcement is off.
  • Enforcement can remain on after the menu-bar application quits.
  • Logging out stops the menu-bar application, but the system LaunchDaemon can remain active.

Requirements

  • macOS 13 or later
  • Xcode or Xcode Command Line Tools with the Swift compiler
  • An administrator account to enable enforcement or change its privileged configuration
  • A native build performed on the target Mac

Check the developer tools before building:

xcode-select -p
xcrun --find swiftc
swiftc --version

If the tools are missing, install Apple's Command Line Tools:

xcode-select --install

Install from source

1. Clone the repository

git clone https://github.com/hideouts-io/MenuBarNetToggle.git
cd MenuBarNetToggle

2. Review the source

This application intentionally performs privileged network-interface changes. Review at least these files before running it:

less main.swift
less build.sh
plutil -p Info.plist

The helper script and LaunchDaemon are generated by main.swift only after you enable enforcement.

3. Build and verify the application

./build.sh

The build script:

  1. Compiles main.swift with Apple's Swift compiler and Cocoa framework.
  2. Creates build/MenuBarNetToggle.app.
  3. Applies a local ad-hoc code signature.
  4. Performs strict code-signature verification.

The resulting application targets macOS 13 or later and is built for the current Mac's architecture (arm64 or x86_64). It is not a universal binary, Developer ID signed, or notarized for third-party distribution.

You can repeat the verification manually:

codesign --verify --deep --strict --verbose=2 build/MenuBarNetToggle.app
codesign -d --verbose=4 build/MenuBarNetToggle.app
file build/MenuBarNetToggle.app/Contents/MacOS/MenuBarNetToggle

4. Copy the application into Applications

Quit any older copy of MenuBarNetToggle first. Then copy the new bundle:

ditto build/MenuBarNetToggle.app /Applications/MenuBarNetToggle.app

If /Applications is not writable by your account, run only the copy command with administrator privileges:

sudo ditto build/MenuBarNetToggle.app /Applications/MenuBarNetToggle.app

Verify the installed copy:

codesign --verify --deep --strict --verbose=2 /Applications/MenuBarNetToggle.app

5. Start the menu-bar application

open /Applications/MenuBarNetToggle.app

Look for NetCtl ○ in the menu bar. The application is an agent-style app (LSUIElement) and therefore does not normally appear in the Dock.

6. Configure before enabling

Open NetCtl → Settings… and enter the interfaces that must stay usable. Separate interface names with spaces or commas. The interval must be between 1 and 3600 seconds.

Use these read-only commands to inspect the current Mac:

ifconfig -l
networksetup -listallhardwareports
route -n get default
scutil --nwi

Typical interface names include:

  • lo0 — local loopback; normally keep this allowed.
  • en0, en1, and similar — Wi-Fi, Ethernet, or other hardware ports. Confirm them with networksetup; do not rely only on the name.
  • utun0, utun1, and similar — tunnel interfaces used by VPNs and some Apple networking features. Their numbering is not stable.
  • bridge0 — a software bridge used by features such as Internet Sharing or virtualization.
  • awdl0 and llw0 — Apple peer-to-peer networking used by features such as AirDrop and Continuity.

7. Enable enforcement

Select NetCtl → Enforcement: Off. Approve the administrator prompt. The label changes to Enforcement: On, and the menu-bar indicator becomes NetCtl ● when the helper is running.

Immediately confirm that required connectivity still works. If it does not, turn enforcement off and restore the affected interface before adjusting the allowlist.

8. Optionally enable launch at login

Select NetCtl → Launch at Login. This creates:

~/Library/LaunchAgents/com.netctl.MenuBarNetToggle.plist

The LaunchAgent points to the exact location of the running application. Move the app to /Applications before enabling this setting so the saved path remains valid.

Update an existing installation

  1. Turn Enforcement off if you want to avoid interface changes during the update.
  2. Turn Launch at Login off if the existing LaunchAgent points to a different app location.
  3. Quit MenuBarNetToggle.
  4. Pull and rebuild the source.
  5. Replace the installed application.
  6. Start the new application and re-enable the desired settings.
git pull --ff-only
./build.sh
sudo ditto build/MenuBarNetToggle.app /Applications/MenuBarNetToggle.app
open /Applications/MenuBarNetToggle.app

Replacing the app does not automatically remove the existing root helper or configuration. Enabling enforcement from the rebuilt app rewrites those helper files using the current configuration.

Verify a running installation

Menu-bar process

pgrep -alf MenuBarNetToggle
launchctl print "gui/$(id -u)/com.netctl.MenuBarNetToggle"

The launchctl print command reports an error when Launch at Login is disabled or the LaunchAgent is not loaded.

Enforcement helper

launchctl print system/com.netctl.netenforce
cat "/Library/Application Support/MenuBarNetToggle/config.conf"
cat /var/run/com.netctl.netenforce.pid

Check ownership and permissions:

ls -l "/Library/Application Support/MenuBarNetToggle"
ls -l /Library/LaunchDaemons/com.netctl.netenforce.plist

Expected privileged files are owned by root:wheel. The helper script is executable, while the configuration and daemon property list are not.

Interface state

for interface_name in $(ifconfig -l); do
  ifconfig "$interface_name" | sed -n '1p;/status:/p'
done

An interface without the UP flag has been administratively brought down or is otherwise inactive. Interface status alone does not prove MenuBarNetToggle caused the state; correlate it with the daemon state and configuration.

Logs

The LaunchDaemon writes standard output and standard error to:

/var/log/com.netctl.netenforce.out
/var/log/com.netctl.netenforce.err

Inspect them without modifying them:

sudo tail -n 100 /var/log/com.netctl.netenforce.out
sudo tail -n 100 /var/log/com.netctl.netenforce.err

Empty logs are normal when all helper commands complete successfully.

Stop enforcement safely

Select NetCtl → Enforcement: On and approve the administrator prompt. Confirm that the helper is no longer loaded:

launchctl print system/com.netctl.netenforce

The expected result is an error stating that the service could not be found.

Stopping enforcement does not automatically bring previously disabled interfaces back up. The safest general recovery is to use macOS System Settings → Network to reconnect the required service. If you have positively identified the affected interface, you can bring that exact interface up manually:

sudo ifconfig en0 up

Replace en0 only with an interface name you verified on the current Mac.

Uninstall completely

The Quit command and deleting the .app are not sufficient when enforcement or Launch at Login has been enabled. Use the following sequence to remove the user LaunchAgent, root LaunchDaemon, helper, configuration, PID record, logs, and application.

1. Stop enforcement and quit the app

Use the menu to turn Enforcement off, turn Launch at Login off, and then choose Quit.

2. Remove a remaining user LaunchAgent

if launchctl print "gui/$(id -u)/com.netctl.MenuBarNetToggle" >/dev/null 2>&1; then
  launchctl bootout "gui/$(id -u)/com.netctl.MenuBarNetToggle"
fi
rm -f "$HOME/Library/LaunchAgents/com.netctl.MenuBarNetToggle.plist"

3. Remove the privileged helper

if launchctl print system/com.netctl.netenforce >/dev/null 2>&1; then
  sudo launchctl bootout system/com.netctl.netenforce
fi
sudo rm -f /Library/LaunchDaemons/com.netctl.netenforce.plist
sudo rm -f /var/run/com.netctl.netenforce.pid
sudo rm -f /var/log/com.netctl.netenforce.out
sudo rm -f /var/log/com.netctl.netenforce.err
sudo rm -rf "/Library/Application Support/MenuBarNetToggle"

These commands target only files created by MenuBarNetToggle. The configuration is deleted and cannot be recovered unless it was backed up separately.

4. Remove the application

sudo rm -rf /Applications/MenuBarNetToggle.app

If you ran the application directly from the source tree, also remove its generated build directory if desired:

rm -rf build

5. Verify removal

pgrep -alf MenuBarNetToggle
launchctl print system/com.netctl.netenforce
test ! -e "$HOME/Library/LaunchAgents/com.netctl.MenuBarNetToggle.plist"
test ! -e /Library/LaunchDaemons/com.netctl.netenforce.plist
test ! -e "/Library/Application Support/MenuBarNetToggle"

The process search should return no MenuBarNetToggle process, both launchctl print checks should report that the services are absent, and the test commands should produce no output with a successful status.

Troubleshooting

NetCtl does not appear

Confirm that the process started:

pgrep -alf MenuBarNetToggle
open /Applications/MenuBarNetToggle.app

If the process exits, launch the executable from Terminal to see immediate errors:

/Applications/MenuBarNetToggle.app/Contents/MacOS/MenuBarNetToggle

The administrator prompt was cancelled

No privileged change is completed when macOS reports error -128 for cancelled authorization. Reopen the menu and repeat the operation when ready.

Enforcement says it is on after the app quits

That is expected. The system LaunchDaemon is independent of the menu-bar UI. Reopen the app and turn enforcement off, or stop the exact service from Terminal:

sudo launchctl bootout system/com.netctl.netenforce

A VPN stopped working

Turn enforcement off first. Determine the VPN's current tunnel interface with scutil --nwi and the VPN application's diagnostics. Add the confirmed interface to the allowlist before re-enabling enforcement. Avoid assuming that a previously observed utun number remains correct.

Wi-Fi or Ethernet remains unavailable after enforcement stops

Stopping the helper prevents future enforcement passes but does not reverse prior ifconfig ... down commands. Reconnect the service in System Settings or bring the verified interface up manually. If macOS service state remains inconsistent, toggle the relevant network service off and on or restart the Mac after confirming the helper is unloaded.

The build fails

Confirm that the active developer directory and SDK are valid:

xcode-select -p
xcrun --sdk macosx --show-sdk-path
xcrun --find swiftc

If Xcode was moved or replaced, select the intended installation explicitly:

sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer

Use the path of the Xcode installation actually present on the Mac.

Security model and limitations

  • The GUI is locally ad-hoc signed by the build script. This provides bundle integrity checking but not publisher identity or Apple notarization.
  • Enabling or changing enforcement requires an administrator authorization prompt.
  • The helper, configuration, and LaunchDaemon are written as root-owned files.
  • The helper executes as root because changing arbitrary interface state requires elevated privileges.
  • The configuration is sourced by a root shell. Do not change its ownership or grant untrusted users write access.
  • Interface names are validated by the GUI, but the design remains intentionally small and is not a replacement for a hardened privileged-helper architecture using SMAppService or XPC.
  • The PID file is a lightweight status mechanism. It is not a cryptographic proof of process identity.
  • Allowlisting an interface permits that interface to remain up; it does not restrict destinations, applications, protocols, or traffic content.
  • The project currently has no automatic update mechanism.

Review the code and test on a non-critical Mac before relying on it for operational controls.

Development

The project deliberately avoids third-party dependencies and project generators. The complete application is built from:

MenuBarNetToggle/
├── Info.plist
├── README.md
├── build.sh
└── main.swift

Build artifacts are written beneath build/ and excluded from Git.

Before proposing a change:

./build.sh
plutil -lint Info.plist
codesign --verify --deep --strict --verbose=2 build/MenuBarNetToggle.app

Changes affecting allowlist validation, launchd paths, privilege boundaries, or uninstall behavior should be tested against real macOS launchd behavior. Do not test enforcement remotely unless you have a separate recovery path; an incorrect allowlist can sever the connection used to administer the Mac.

License

No open-source license has been selected yet. Copyright law therefore reserves the code by default. Add an explicit license before inviting third-party reuse or contributions.

About

Native macOS menu-bar utility for explicit network-interface allowlist enforcement

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages