https://github.com/thomasguillot/hosts-switchr
A native macOS menu-bar app for managing /etc/hosts through switchable profiles. macOS 26 (Tahoe)+
https://github.com/thomasguillot/hosts-switchr
blocklist developer-tools etc-hosts hosts hosts-file macos menu-bar swift swiftui tahoe
Last synced: 8 days ago
JSON representation
A native macOS menu-bar app for managing /etc/hosts through switchable profiles. macOS 26 (Tahoe)+
- Host: GitHub
- URL: https://github.com/thomasguillot/hosts-switchr
- Owner: thomasguillot
- License: mit
- Created: 2026-06-26T16:39:56.000Z (14 days ago)
- Default Branch: main
- Last Pushed: 2026-06-26T17:11:38.000Z (14 days ago)
- Last Synced: 2026-06-26T19:09:11.728Z (13 days ago)
- Topics: blocklist, developer-tools, etc-hosts, hosts, hosts-file, macos, menu-bar, swift, swiftui, tahoe
- Language: Swift
- Homepage:
- Size: 210 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Agents: AGENTS.md
Awesome Lists containing this project
README
# Hosts Switchr
A native macOS menu-bar app for managing `/etc/hosts` through switchable profiles.
**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) and **HaGeZi** (Light → Normal → Pro → Pro++ → Ultimate). Sources auto-refresh with conditional-GET (ETag) caching, and you can add your own.
- **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).
- **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.
- **Launch at login** — runs quietly as a menu-bar-only app (no Dock icon), and can optionally show the active profile name next to the menu-bar icon.
### 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](#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/HostsSwitchr/`. `/etc/hosts` is only ever changed when you click **Apply**.
---
## For developers
### Requirements
- macOS 26 (Tahoe)+, Xcode 26+
- [xcodegen](https://github.com/yonaskolb/XcodeGen) (`brew install xcodegen`)
- [SwiftLint](https://github.com/realm/SwiftLint) (`brew install swiftlint`) — enforced in the build phase
### Build
```sh
# 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:
```sh
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
```sh
./scripts/make-dmg.sh
```
Builds the Release configuration and produces `dist/HostsSwitchr-.dmg`, a drag-to-Applications disk image. The version comes from `MARKETING_VERSION` in `App/project.yml`. Requires `create-dmg` (`brew install create-dmg`).
### 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, Settings scenes
├── AppModel # @Observable coordinator; owns all stores
└── …views
```
**Key invariants:**
- All persistent state lives in `~/Library/Application Support/HostsSwitchr/`.
- 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`](AGENTS.md).
## License
MIT