SaneHosts Development Guide (SOP)
May 5, 2026 · View on GitHub
README · ARCHITECTURE · DEVELOPMENT · PRIVACY · SECURITY
Version 1.0 | Last updated: 2026-01-20
SINGLE SOURCE OF TRUTH for all Developers and AI Agents.
Sane Philosophy
+-----------------------------------------------------+
| BEFORE YOU SHIP, ASK: |
| |
| 1. Does this REDUCE fear or create it? |
| 2. Power: Does user have control? |
| 3. Love: Does this help people? |
| 4. Sound Mind: Is this clear and calm? |
| |
| Grandma test: Would her life be better? |
| |
| "Not fear, but power, love, sound mind" |
| - 2 Timothy 1:7 |
+-----------------------------------------------------+
Full philosophy:
~/SaneApps/meta/Brand/NORTH_STAR.md
THIS HAS BURNED YOU
Real failures from past sessions. Don't repeat them.
| Mistake | What Happened | Prevention |
|---|---|---|
| Guessed API | Assumed API exists. It doesn't. 20 min wasted. | verify_api first |
| Kept guessing | Same error 4 times. Finally checked apple-docs MCP. | Stop at 2, investigate |
| Separate ProfileStore instances | MenuBarProfileStore created separate ProfileStore - state never synced | Use ProfileStore.shared singleton |
| Hardcoded IP filter | RemoteSyncService rejected valid IPs like 192.168.x.x | Use HostsParser.isValidIPAddress() |
| Serial disk writes | Bulk operations wrote to disk per entry - severe lag | Use bulkUpdateEntries() / bulkRemoveEntries() |
| Missing hardened runtime | Notarization rejected - missing entitlement | Add ENABLE_HARDENED_RUNTIME = YES to Shared.xcconfig |
| 1-second polling for sync | MenuBarProfileStore polled ProfileStore - wasteful | Use NotificationCenter instead |
| OISD URLs 404 | OISD discontinued HOSTS format Jan 2024 | URL liveness preflight checks |
The #1 differentiator: Skimming this SOP = 5/10 sessions. Internalizing it = 8+/10.
"If you skim you sin." - The answers are here. Read them.
Quick Start for AI Agents
New to this project? Start here:
- Read Rule #0 first (Section "The Rules") - It's about HOW to use all other rules
- All files stay in project - NEVER write files outside
~/SaneApps/apps/SaneHosts/unless user explicitly requests it - Use SaneMaster for builds/tests - never raw
xcodebuild - Self-rate after every task - Rate yourself 1-10 on SOP adherence (see Self-Rating section)
SaneMaster (DO THIS FIRST)
Key Commands:
# Build + Test
./scripts/SaneMaster.rb verify
# Build + Launch
./scripts/SaneMaster.rb test_mode
# Stream logs
./scripts/SaneMaster.rb logs --follow
The Rules
#0: NAME THE RULE BEFORE YOU CODE
DO: State which rules apply before writing code DON'T: Start coding without thinking about rules
RIGHT: "Uses Apple API -> Rule #2: VERIFY BEFORE YOU TRY"
RIGHT: "New file -> Rule #9: NEW FILE? GEN THAT PILE"
WRONG: "Let me just code this real quick..."
#1: STAY IN YOUR LANE
DO: Save all files inside ~/SaneApps/apps/SaneHosts/
DON'T: Create files outside project without asking
#2: VERIFY BEFORE YOU TRY
DO: Check API exists before using (apple-docs MCP, context7 MCP) DON'T: Assume an API exists from memory or web search
Critical APIs for SaneHosts:
NSAppleScript- privilege elevation for /etc/hosts writesSMAppServiceand XPC helper interfacesdscacheutil- DNS cache flush
#3: TWO STRIKES? INVESTIGATE
DO: After 2 failures -> stop, follow Research Protocol (see section below) DON'T: Guess a third time without researching
#4: GREEN MEANS GO
DO: Fix all test failures before claiming done DON'T: Ship with failing tests
#5: SANEMASTER OR DISASTER
DO: Use ./scripts/SaneMaster.rb for all build/test operations
DON'T: Use raw xcodebuild commands
# Right
./scripts/SaneMaster.rb verify
./scripts/SaneMaster.rb test_mode
# Wrong
xcodebuild -workspace SaneHosts.xcworkspace ...
#6: BUILD, KILL, LAUNCH, LOG
DO: Run full sequence after every code change DON'T: Skip steps or assume it works
# Kill existing process
killall -9 SaneHosts 2>/dev/null; sleep 1
# Build + launch via SaneMaster
./scripts/SaneMaster.rb test_mode
#7: NO TEST? NO REST
DO: Every bug fix gets a test that verifies the fix
DON'T: Use placeholder or tautology assertions (#expect(true))
Test location: SaneHostsPackage/Tests/SaneHostsFeatureTests/
#8: BUG FOUND? WRITE IT DOWN
DO: Document bugs in TodoWrite immediately DON'T: Try to remember bugs or skip documentation
#9: NEW FILE? UPDATE THE PACKAGE
DO: After creating new Swift files, ensure they're in the right SPM target DON'T: Create files and forget to add them to Package.swift if needed
Note: SaneHosts uses SPM package structure (SaneHostsPackage/). Files added to existing directories are auto-included. New directories may need Package.swift updates.
#10: FIVE HUNDRED'S FINE, EIGHT'S THE LINE
| Lines | Status |
|---|---|
| <500 | Good |
| 500-800 | OK if single responsibility |
| >800 | Must split |
#11: TOOL BROKE? FIX THE YOKE
DO: If SaneMaster fails, debug the issue DON'T: Work around broken tools
#12: TALK WHILE I WALK
DO: Use subagents for heavy lifting, stay responsive to user DON'T: Block on long operations
Self-Rating (MANDATORY)
After each task, rate yourself. Format:
**Self-rating: 7/10**
Verified API before using, ran full test cycle
Forgot to run tests after first change
| Score | Meaning |
|---|---|
| 9-10 | All rules followed |
| 7-8 | Minor miss |
| 5-6 | Notable gaps |
| 1-4 | Multiple violations |
Research Protocol (STANDARD)
This is the standard protocol for investigating problems. Used by Rule #3, Circuit Breaker, and any time you're stuck.
Tools to Use (ALL of them)
| Tool | Purpose | When to Use |
|---|---|---|
| Task agents | Explore codebase, analyze patterns | "Where is X used?", "How does Y work?" |
| apple-docs MCP | Verify Apple APIs exist and usage | Any Apple framework API |
| context7 MCP | Library documentation | Third-party packages |
| WebSearch/WebFetch | Solutions, patterns, best practices | Error messages, architectural questions |
| Grep/Glob/Read | Local investigation | Find similar patterns, check implementations |
| memory MCP | Past bug patterns, architecture decisions | "Have we seen this before?" |
Research Output -> Plan
After research, present findings in this format:
## Research Findings
### What I Found
- [Tool used]: [What it revealed]
- [Tool used]: [What it revealed]
### Root Cause
[Clear explanation of why the problem occurs]
### Proposed Fix
[Rule #X: NAME] - specific action
[Rule #Y: NAME] - specific action
...
### Verification
- [ ] Tests pass (`./scripts/SaneMaster.rb verify`)
- [ ] Manual test: [specific check]
Circuit Breaker Protocol
The circuit breaker is an automated safety mechanism that blocks Edit/Bash/Write tools after repeated failures.
When It Triggers
| Condition | Threshold | Meaning |
|---|---|---|
| Same error 3x | 3 identical | Stuck in loop, repeating same mistake |
| Total failures | 5 any errors | Flailing, time to step back |
Recovery Flow
CIRCUIT BREAKER TRIPS
|
v
+---------------------------------------------+
| 1. READ ERRORS |
| Check what failed and why |
+---------------------------------------------+
| 2. RESEARCH (use ALL tools above) |
| - What API am I misusing? |
| - Has this bug pattern happened before? |
| - What does the documentation say? |
+---------------------------------------------+
| 3. PRESENT SOP-COMPLIANT PLAN |
| - State which rules apply |
| - Show what research revealed |
| - Propose specific fix steps |
+---------------------------------------------+
| 4. USER APPROVES PLAN |
+---------------------------------------------+
|
v
EXECUTE APPROVED PLAN
Key insight: Being blocked is not failure - it's the system working. The research phase often reveals the root cause that guessing would never find.
Plan Format (MANDATORY)
Every plan must cite which rule justifies each step. No exceptions.
Format: [Rule #X: NAME] - specific action with file:line or command
DISAPPROVED PLAN
## Plan: Fix Bug
### Steps
1. Clean build
2. Fix the issue
3. Rebuild and verify
Approve?
Why rejected:
- No
[Rule #X]citations - can't verify SOP compliance - No tests specified (violates Rule #7)
- Vague "fix" without file:line references
APPROVED PLAN
## Plan: Fix [Bug Description]
### Bugs to Fix
| Bug | File:Line | Root Cause |
|-----|-----------|------------|
| [Description] | [File.swift:50] | [Root cause] |
### Steps
[Rule #5: USE SANEMASTER] - Clean build if needed
[Rule #7: TESTS FOR FIXES] - Create tests:
- SaneHostsPackage/Tests/SaneHostsFeatureTests/[TestFile].swift
[Rule #6: FULL CYCLE] - Verify fixes:
- ./scripts/SaneMaster.rb verify
- ./scripts/SaneMaster.rb test_mode
- Manual: [specific check]
[Rule #4: GREEN BEFORE DONE] - All tests pass before claiming complete
Approve?
Project Structure
SaneHosts/
+-- SaneHosts.xcworkspace # OPEN THIS (workspace with app + SPM)
+-- SaneHosts/ # App target (minimal entry point)
| +-- SaneHostsApp.swift
| +-- Assets.xcassets/
+-- SaneHostsPackage/ # SPM package with all feature code
| +-- Sources/
| | +-- SaneHostsFeature/
| | +-- Models/ # Profile, HostEntry
| | +-- Services/ # HostsService, ProfileStore, DNSService
| | +-- Views/ # SwiftUI views
| +-- Tests/
| +-- SaneHostsFeatureTests/
+-- Config/ # Build configurations
+-- scripts/ # Release scripts
+-- docs/ # Documentation
+-- website/ # Landing page
Key Services
| Service | Purpose |
|---|---|
HostsService | Reads/writes /etc/hosts via AppleScript with admin privileges |
ProfileStore | Profile CRUD and persistence (UserDefaults + JSON files) |
DNSService | Flushes DNS cache after hosts file changes |
RemoteSyncService | Imports hosts from remote URLs (blocklists) |
HostsParser | Parses hosts file format |
Singleton Pattern: Use ProfileStore.shared for app-wide state sharing.
Security Considerations
| Aspect | Details |
|---|---|
| Admin Authentication | Hosts file modifications require admin password |
| AppleScript Elevation | Uses do shell script with administrator privileges |
| No Sandbox | Required to write /etc/hosts (system file) |
| Hardened Runtime | Required for notarization |
| Entitlements | com.apple.security.automation.apple-events for AppleScript |
Data Locations
| Data | Location |
|---|---|
| Profiles | ~/Library/Application Support/SaneHosts/Profiles/*.json |
| UserDefaults | Standard UserDefaults for settings |
| Derived Data | ~/Library/Developer/Xcode/DerivedData/SaneHosts-*/ |
MCP Tool Optimization (TOKEN SAVERS)
SaneMaster (Required)
Use the unified SaneMaster workflow for build/test:
./scripts/SaneMaster.rb verify
./scripts/SaneMaster.rb test_mode
./scripts/SaneMaster.rb logs --follow
Memory MCP 3-Layer Workflow (10x Token Savings)
1. search_nodes(query, project: "SaneHosts") -> Get index with node IDs
2. Use node IDs to fetch specific context
3. read_graph() -> Full graph only if needed
Always filter by project for isolation.
apple-docs Optimization
compact: trueworks onlist_technologies,get_sample_code,wwdc(NOT onsearch_apple_docs)analyze_api analysis="all"for comprehensive API analysis
context7 for Library Docs
resolve-library-idFIRST, thenquery-docs- SwiftUI ID:
/websites/developer_apple_swiftui(13,515 snippets!)
Distribution
See docs/DISTRIBUTION.md for full release checklist.
Critical Credentials (DO NOT REGENERATE)
| Credential | Value/Location |
|---|---|
| Sparkle Public Key | 7Pl/8cwfb2vm4Dm65AByslkMCScLJ9tbGlwGGx81qYU= |
| Sparkle Private Key | macOS Keychain (account: "EdDSA Private Key", service: "https://sparkle-project.org") |
| Notarytool Profile | notarytool (in system keychain) |
| Team ID | M78L6FXD48 |
Release Scripts
# Preflight direct and App Store lanes
./scripts/SaneMaster.rb release_preflight
./scripts/SaneMaster.rb appstore_preflight
# Build, sign, notarize, upload, generate appcast, and deploy website
bash ~/SaneApps/infra/SaneProcess/scripts/release.sh \
--project "$(pwd)" --full --version X.Y.Z --notes "..." --deploy
Toolbelt & Credentials (Available for Agents)
The following tools and API keys are configured in the environment. Use them.
| Tool | Capability | Keychain Location | Account / Key |
|---|---|---|---|
| Resend | Send transactional/outreach emails | Service: resend | api_key |
| LemonSqueezy | Query sales, orders, license keys | Service: lemonsqueezy | api_key |
| Cloudflare | Deploy sites, manage R2 storage | Service: cloudflare | (Check specific item) |
| GitHub | PRs, Issues, Outreach (gh CLI) | Server: github.com | (Auto-managed by gh) |
| Notarytool | Sign & notarize macOS apps | Profile: notarytool | (Stored in system keychain) |
How to Access in Scripts:
# Example: Fetch Resend Key
RESEND_KEY=$(security find-generic-password -s "resend" -a "api_key" -w)
Troubleshooting
| Problem | Fix |
|---|---|
| Ghost beeps / no launch | Ensure workspace is open, rebuild |
| Phantom build errors | ./scripts/SaneMaster.rb clean --nuclear, delete DerivedData |
| "File not found" after new file | Check SPM package includes the file |
| Tests failing mysteriously | Clean build, check for stale derived data |
| Password prompt not appearing | Check NSAppleScript entitlements |
| DNS not flushing | Check dscacheutil -flushcache works manually |
| State not syncing | Use ProfileStore.shared singleton |
| Notarization fails | Check hardened runtime is enabled |