Build Hazel
A background agent watching folders you choose, matching files against rules you can read, and moving, renaming, tagging or running a script on them, with a dry run that shows exactly what would happen and a log that lets you undo anything it did.
Before step 1
Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.
- installfree if you own one
Why FSEvents, file coordination, tags and security scoped bookmarks are all platform APIs.
Get it Apple menu, then About This Mac.
Verify
About This Mac reports macOS 14 or higher - installfree
Why Builds and signs the agent.
Get it Mac App Store or developer.apple.com/xcode. open ↗
Verify
xcodebuild -version prints 16 or higher - have readyfree
Why Every phase acts on files, and testing against anything you care about is how a bug becomes a loss.
Get it Generate them with a script so the folder can be recreated instantly after each destructive test.
Verify
A regeneration script recreates the folder from nothing - decidefree with hardware you own
Why This application moves and deletes files. Undo covers what it recorded; it does not cover a bug it did not record.
Get it Confirm Time Machine or your backup tool has a recent snapshot, and that you have restored from it at least once.
Verify
A test file has been restored from backup successfully - account99 USD per year
Why Product phase only. Without it there is no notarization and a Gatekeeper warning elsewhere.
Get it Enrol at developer.apple.com/programs. open ↗
Verify
A Developer ID Application certificate appears in Xcode settings
Data model
Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.
- folders(id, path, recursive, enabled, bookmark_data) · bookmark_data is a security scoped bookmark so access survives a relaunch - rules(id, folder_id, name, priority, enabled, conditions_json, actions_json, stop_on_match) - runs(id, rule_id, file_path, matched_at, action, from_path, to_path, result, error) · the log and the undo record - quarantine(path, reason, at) · files a rule refused to touch, with why - A rule is conditions and actions as data, never code, so a dry run can evaluate it without doing anything.
Environment variables
These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.
| Variable | Needed | Example | Where the value comes from |
|---|---|---|---|
RULES_DB_PATH | required | ~/Library/Application Support/Rules/rules.db | Rules, folders and the run log. |
SETTLE_CHECK_INTERVAL_MS | required | 1000 | How long between size checks before a file counts as settled. Longer is safer for slow copies. |
MAX_ACTIONS_PER_RUN | required | 50 | Above this a run pauses for confirmation. The main protection against an over-broad rule. |
SCRIPT_TIMEOUT_MS | required | 10000 | How long a script action may run before it is killed. |
LOG_RETENTION_DAYS | required | 90 | How long run history is kept. Undo depends on it, so do not set this short. |
The build, in order
Watching a folder without hurting yourself
You are told about new and changed files, once each, after they have finished being written.
One stream per watched folder, with a latency that batches bursts. Persist the event identifier so a restart does not replay history.
Files
Watcher.swiftSize and modification time stable across two checks. Test with a deliberately slow copy.
Files
Settle.swiftSecurity scoped bookmarks so access to a user-chosen folder survives a relaunch without asking again.
Files
Bookmarks.swift
done when · tick each as it passeswatch out- A file being copied into a watched folder generates events while it is still incomplete, and acting on it moves or renames a partial file. Wait for the size to be stable across two checks a second or so apart before treating a file as ready, and be aware that a very slow copy can still fool a naive settle check.
Rules as readable data
A rule is something you can read, and evaluating it does nothing until you say so.
Serialisable structures, not closures. This is what makes dry run and inspection possible.
Files
Rule.swiftOne function decides matches; another performs actions. The dry run calls only the first.
Files
Engine.swiftChecksum every file in the folder before and after a dry run. This test is worth more than it looks.
Files
Tests/DryRun.swift
done when · tick each as it passeswatch out- A rule engine that executes as it evaluates cannot offer a dry run, and a file automation tool without a dry run will eventually move a thousand files somewhere you did not intend. Separate matching from acting from the beginning; retrofitting that separation means rewriting the engine.
Acting safely, with undo
Rules do their work, and anything they did can be reversed.
NSFileCoordinator for moves and writes so applications reading the same files see consistency.
Files
Actions.swiftFrom path and to path for every action. Undo replays them backwards.
Files
Undo.swiftA destination that exists is a conflict, not a decision to make silently.
Files
Actions.swift
done when · tick each as it passeswatch out- Moving a file onto an existing path silently destroys the destination in most naive implementations. Refuse the move, record it as a conflict, and let the user decide. A file automation tool that overwrites will eventually overwrite something irreplaceable.
Scripts, and the blast radius they bring
A rule can run your own script, and a bad script cannot run away with your files.
Off the main thread, process group killed on expiry, output captured into the log.
Files
ScriptAction.swiftA threshold above which the run pauses for confirmation. This is the safety net that matters most.
Files
Engine.swiftIn the rule list and in the dry run. A script action deserves more attention than a move.
Files
RuleList.swift
done when · tick each as it passeswatch out- A rule that matches more broadly than you intended, combined with a script action, is how a folder gets reorganised beyond recognition in seconds. A per-run action cap that requires confirmation above a threshold turns a catastrophe into a question.
Sharing and inspecting rules
Rules can be exported, read and imported without importing a surprise.
A schema version, refused if newer than the application understands.
Files
Export.swiftEvery action listed, scripts individually approved, and the import applied atomically or not at all.
Files
ImportPreview.swiftBy filename, rule and date. Answering what happened to a file is the log's main job.
Files
History.swift
done when · tick each as it passeswatch out- An imported rule set containing a script action is arbitrary code from whoever wrote it, running against your files on a trigger you may not fully understand. List every script prominently in the import preview and require explicit approval for each.
Signing, notarization and updatesproduct builder
Others can install it and trust what it will do to their files.
Hardened runtime, Developer ID, notarytool, stapler.
terminalxcrun notarytool submit Rules.zip --keychain-profile AC --wait
Only when a rule needs the location, and say which rule. A first-launch request for everything trains people to click through.
Files
Permissions.swiftOne paragraph: this moves and deletes files, here is the dry run, here is undo, here is the cap.
Files
README.md
done when · tick each as it passeswatch out- Full Disk Access is required to watch some locations and is a serious permission. Ask for it only when a rule actually needs it, and explain which folder required it, rather than requesting it at first launch for convenience.
That is the whole plan for Hazel. What it deliberately does not cover is below · check the gaps before you call it a replacement.
- The rule depth accumulated by the commercial product.
- Deep Photos and Mail integrations.
- Guaranteed compatibility across macOS releases.
- Cloud sync of rules.
- mature filesystem automation, rule depth, Apple integrations, and reliability
- years of OS-version edge cases
- large extension ecosystem
- cross-device sync
- commercial support and rapid compatibility fixes
- Content-based conditions such as matching text inside a PDF
- A scheduled sweep for folders where watching is impractical
- Rule templates for common cases such as sorting downloads by type
Need the files? The project pack on the verdict page hands your agent the whole brief · more productivity utilities.