Skip to content

Latest commit

 

History

42 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Hosts Switchr

A native macOS menu-bar app for managing /etc/hosts through switchable profiles.

Download page — always points at the latest release.

Requires macOS 26 (Tahoe) or later. Unsigned — no Apple Developer Program needed.


For users

What it does

  • Profiles — create, edit, and instantly switch named /etc/hosts configurations from the menu bar.
  • Blocklist sources — subscribe to hosts-format lists. Built in: StevenBlack (Unified, plus Fake News / Gambling / Porn / Social). Sources auto-refresh with conditional-GET (ETag) caching, and you can add your own by URL.
  • Fragments — reusable hosts snippets you toggle into any profile (e.g. a "Docker" fragment listing your local container hostnames, switched on only in your Dev profile). A draft-based editor lets you save changes without a password prompt; you re-apply once when you're ready.
  • Stale-aware apply — profiles, fragments, and sources show a "needs re-apply" badge when your edits differ from what's live in /etc/hosts, and each editor has Cancel to revert unapplied changes back to the applied state.
  • Drag-to-reorder — reorder profiles, sources, and fragments directly in the sidebars.
  • Import / export — back up your whole setup to a JSON file, restore it on another machine, or import a plain hosts file as a profile or fragment.
  • Four languages — English (US and UK), French and Spanish, following your macOS language setting. Change it per app in System Settings → General → Language & Region.
  • Launch at login & updates — runs quietly as a menu-bar-only app (no Dock icon), can optionally show the active profile name next to the menu-bar icon, and checks GitHub Releases for new versions — installing them in place and relaunching, no drag-and-drop.

Install

The app is unsigned (it deliberately uses no Apple Developer Program, signing, or privileged helper), so macOS blocks it on first launch. To allow it:

  1. Double-click Hosts Switchr; when macOS warns it "can't verify the developer," click Done (not "Move to Trash").
  2. Open System Settings → Privacy & Security and scroll to the Security section.
  3. Click Open Anyway next to "Hosts Switchr" was blocked to protect your Mac, then authenticate and click Open.

macOS only asks once — after that it launches normally.

If you don't have a prebuilt app, build it from source — see For developers.

Using it

  1. Launch the app — it lives in the menu bar (no Dock icon).
  2. Profiles — select a profile and click Apply to write it to /etc/hosts. You'll be asked for your admin password; this is the only privileged step. Switch the active profile anytime straight from the menu-bar menu.
  3. Sources — toggle built-in or custom blocklists on per profile. Right-click a source to refresh it, or use Refresh All Sources in the menu bar.
  4. Fragments — create reusable snippets and toggle them into any profile.
  5. Settings — enable Launch at login and the active-profile name in the menu bar.
  6. Rename & delete — new profiles and fragments open ready to rename; press ⌘⌫ (or right-click → Delete) to remove the selected item. The active profile and built-in sources can't be deleted.

New to it? See Help → Hosts Switchr Help for a built-in guide.

Your data lives in ~/Library/Application Support/Hosts Switchr/. /etc/hosts is only ever changed when you click Apply.


For developers

Requirements

  • macOS 26 (Tahoe)+, Xcode 26+
  • xcodegen (brew install xcodegen)
  • SwiftLint (brew install swiftlint) — enforced in the build phase

Build

# Logic package (no Xcode needed)
cd HostsKit && swift test

# App — generate the project, then open or build
cd App && xcodegen && open HostsSwitchr.xcodeproj

Headless build:

cd App && xcodegen && xcodebuild \
  -project HostsSwitchr.xcodeproj \
  -scheme HostsSwitchr \
  -destination 'platform=macOS' \
  build

App/HostsSwitchr.xcodeproj is generated by xcodegen and git-ignored — edit App/project.yml, not the project file. Run SwiftLint from the repo root.

Packaging a release

./scripts/make-dmg.sh

Builds the Release configuration and produces dist/Hosts-Switchr-<version>.dmg, a drag-to-Applications disk image. The version comes from MARKETING_VERSION in App/project.yml. Requires create-dmg (brew install create-dmg).

Localization

Strings live in String Catalogs (Localizable.xcstrings) in both targets. After adding or changing a user-facing string:

./scripts/sync-localizations.py          # sync keys, mirror en → en-GB
./scripts/sync-localizations.py --check  # fails if anything is untranslated

xcodebuild doesn't write back to a String Catalog (that's an Xcode IDE behaviour), so the script reads the compiler's .stringsdata to catch strings that would otherwise ship as untranslated English. See AGENTS.md for the rest.

Architecture

hosts-switchr/
├── HostsKit/          # Pure Swift package — all logic, zero UI, zero app framework
│   └── Sources/HostsKit/
│       ├── Profile, ProfileStore        # named /etc/hosts configs, ordered, persisted
│       ├── LocalFragment, FragmentStore # reusable local snippets
│       ├── RemoteSource, SourceCatalog  # blocklist subscriptions + built-in catalog
│       ├── SourceFetcher, SourceRefresher # conditional-GET fetch + scheduling
│       ├── MergedHostsComposer          # streams local → fragments → sources to a temp file
│       ├── HostsApplier, PrivilegedRunner # atomic privileged write to /etc/hosts via osascript
│       ├── ConfigBundle                 # import/export bundle model
│       └── …validation, scanning, login-item protocol
└── App/               # SwiftUI macOS app — thin UI layer over HostsKit
    └── Sources/
        ├── HostsSwitchrApp              # @main, MenuBarExtra, Window scenes
        ├── AppModel                     # @Observable coordinator; owns all stores
        └── …views

Key invariants:

  • All persistent state lives in ~/Library/Application Support/Hosts Switchr/.
  • Writes to /etc/hosts go through a unique-named temp file → cp via osascript (no helper daemon, no signing required); the merged content is never interpolated into the shell command.
  • Remote sources require https://; imported URLs go through the same SourceURLPolicy guard.
  • Fail-closed everywhere: corrupt store files are preserved to a .corrupt path before any overwrite.

For the full agent/contributor guide (conventions, security invariants, build/test workflow), see AGENTS.md.

License

MIT

About

A native macOS menu-bar app for managing /etc/hosts through switchable profiles. macOS 26 (Tahoe)+

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages