// 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.
# 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" },
]
// 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.
// 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.
// 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.
$ 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.