Author here. One implementation detail that may be interesting: the drift check is enforced inside the privileged write transaction, not just by the UI watcher.
The daemon receives the fully rendered hosts content, its target hash, the expected hash of the live /etc/hosts file, and a merge ID. Under a transaction lock, it hashes the live file and rejects the request if the expected hash no longer matches. It writes and fsyncs a temporary file, then rereads /etc/hosts immediately before rename; if the bytes changed between those two reads, it aborts instead of overwriting them. Only then does it atomically rename the temporary file and flush DNS.
The daemon exposes only that merge operation over XPC, and the app and daemon verify each other's signing identity. I'm happy to discuss the TOCTOU/drift tradeoffs, SMAppService, or the packaging and notarization side.
Author here. One implementation detail that may be interesting: the drift check is enforced inside the privileged write transaction, not just by the UI watcher.
The daemon receives the fully rendered hosts content, its target hash, the expected hash of the live /etc/hosts file, and a merge ID. Under a transaction lock, it hashes the live file and rejects the request if the expected hash no longer matches. It writes and fsyncs a temporary file, then rereads /etc/hosts immediately before rename; if the bytes changed between those two reads, it aborts instead of overwriting them. Only then does it atomically rename the temporary file and flush DNS.
The daemon exposes only that merge operation over XPC, and the app and daemon verify each other's signing identity. I'm happy to discuss the TOCTOU/drift tradeoffs, SMAppService, or the packaging and notarization side.