HostSwitcher

// native · menu bar · no Electron

Switch /etc/hosts environments from the menu bar

Named sets of entries, one click to switch, and the name cache flushed in the same breath. No sudo nano. No 150 MB of Electron.

Download for macOS free & open source · macOS 13+ · Apple silicon
Signed, but not notarized. macOS refuses a downloaded copy without a word — no window, no error, nothing at all. Clear the quarantine attribute before the first launch: it is three commands, and the order matters more than the commands do.
~/Library/Application Support/HostSwitcher/sets
# Yours to edit. The product reads this file and
# writes the managed block of /etc/hosts from it.

[[sets]]
name = "Development"
entries = [
  { on = true,  line = "0.0.0.0 api.example.com" },
  { on = false, line = "0.0.0.0 mail.example.com" },
]

[[sets]]
name = "Staging"
entries = [
  { on = true, line = "0.0.0.0 api.staging.example.com" },
]
The HostSwitcher menu bar panel: four sets — Development, Home, Staging, Production — each a heading with an in-force count and a state dot, their entries below with a switch each.
The menu bar panel. Every set and entry at once; a switch, and the file changes.

// why editing by hand gets old

Why editing /etc/hosts by hand gets old

Developers switch domains between environments several times a day: the same api.example.com has to point now at production, now at staging, now at a local container. Doing that through sudo nano /etc/hosts is slow and error-prone, and after every edit you still have to remember to flush the name cache.

The existing solutions either cap the number of entries, or weigh 150 MB, or have not been updated in a long time. The niche HostSwitcher fills is native, light, and without limits.

// one list, whole picture

Sets of hosts, and entries you switch one at a time

A set is a named group of entries, and every entry has a switch of its own. Order is preserved, the count is not limited, and the panel shows every set you have at once — the editor is where you pick one and work on what is in it.

The toggle shows the state of the file, not an intention: if applying fails, it goes back.

The editor window: a list of sets on the left, and the chosen set's entries on the right — each entry one line with a switch of its own.
The editor — sets on the left, the chosen set's entries on the right.

// it breaks nothing

It does not touch the rest of your hosts file

Everything outside the managed block is preserved byte for byte: spaces, tabs, the line-break style, the presence of a final newline. A backup is written before every change, and an untouchable snapshot is kept of the file as it was before the very first one.

A disabled entry is not deleted. It stays in the file, commented out and labelled, so the file always shows the whole picture:

# >>> host-switcher managed block — do not edit >>>
# >>> host-switcher node: Work
127.0.0.1 api.example.com
# host-switcher off: 10.0.0.1 db.example.com
# <<< host-switcher <<<

This block is the projection of your sets file — the product writes it, and never reads it back.

// updates itself, and says so first

It updates itself, and says so before it does

Once a day the app asks whether a newer release has been published. If there is one, the settings gain a row: press Install, and the product fetches that release, checks it against what its own privileged helper would accept, and swaps itself into place — no browser, no Gatekeeper refusal. The new version starts the next time you open the app.

The check is one anonymous request carrying nothing about you or your sets, and a switch in the settings stops the product going out on its own, leaving a button.

The settings: switches to drop the system and browser name caches after a change, launch at login, and a check for newer versions with an Install button.
The settings say what the product does on its own — including the update check — and let you stop it.

// first launch, once

Installing it, the first time

The package is signed, but not notarized — the certificate behind it is a development one, not a Developer ID. macOS therefore refuses to start it as downloaded, and the refusal is not a message: nothing happens at all.

zsh
$ xattr -dr com.apple.quarantine ~/Downloads/HostSwitcher.zip
$ ditto -x -k ~/Downloads/HostSwitcher.zip /Applications
$ open /Applications/HostSwitcher.app

Clear the attribute before you open it, not after. macOS records a refusal against the path: once a copy at that path has been refused, nothing placed there starts afterwards — not the same copy with the attribute cleared, not a reinstall, not even a correctly signed build. Restarting the machine clears the record; so does installing under a different path.

The app has no icon in the Dock — it lives in the menu bar. Approve the background item when macOS asks, and it can apply changes.

This is the last installation you do by hand. From here the product finds newer versions itself: what it downloads never carries the quarantine attribute, so nothing refuses it.

// honest about the edges

What it does not do

  • It is not notarized yet. Until there is a Developer ID behind it, the first launch takes the three commands above.
  • There is no undo. Editing is direct; a deleted entry is gone, though the app asks before deleting a set or an entry.
  • Sets are a flat list. No groups, no nesting, no exclusive mode.
  • macOS 13 or newer. The privileged helper registers through an API older systems do not have; the published build is Apple silicon.
  • Not for the Mac App Store. The sandbox forbids a privileged helper — the exact requirement that crippled a competitor.
  • Uninstalling leaves one trace that is not ours to remove. The system's record of background items survives; nothing of ours runs, but the entry stays visible in System Settings.