- Rust 93.9%
- Shell 6.1%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .github | ||
| config | ||
| packaging/systemd | ||
| src | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| PKGBUILD | ||
| README.md | ||
RustyPerms
RustyPerms is a lightweight daemon that keeps filesystem permissions in sync with your policy. It watches the configured paths, ignores .zfs snapshot directories, and re-applies the expected mode bits and ownership when they drift.
Concepts
- Monitor:
notify::RecommendedWatcherpolls the configured paths (recursively) for modify/create/attribute events. - Debounced reactor: events feed into a short-delay queue so we don’t thrash while a storm of updates is processed.
- Verify & enforce: each path is validated using
std::fs::metadata(Unix-specific mode/owner metadata) and corrected viachmod/chownwhen needed. - Self-change filtering: modifications made by RustyPerms itself are ignored so the watcher doesn’t loop forever.
.zfsguard: any path that contains a.zfscomponent is skipped to avoid touching snapshots.- Network filesystem guard: Linux builds also inspect the mount type and skip NFS/Samba exports so the daemon doesn’t chase every remote metadata update.
Configuration
Copy config/rules.toml to the location you intend to use (the default path is config/rules.toml). Each [[rules]] entry should provide the path to monitor and the expected metadata:
[[rules]]
path = "/etc"
mode = 0755
owner = "root"
group = "wheel"
The mode value is optional; omit it to only police ownership. owner and group are also optional, but supplying them ensures the daemon will chown the target. The daemon resolves user/group names through the system database.
If you need different permissions for directories than files within a rule’s path, use dir_mode and file_mode. Each field is optional—if they’re unset the daemon falls back to mode for both file types—so you can still rely on the legacy mode setting for uniform enforcement.
All mode fields accept either integers (e.g., mode = 644) or strings with an octal prefix (mode = "0o644" or mode = "0777"), so your preferred notation is preserved while the parser still understands the value.
Similarly, owner/group understand both names (root, wheel) and numeric IDs (0, 1000) so you can pin ownership even on systems without matching name databases.
Running
cargo run --release -- --config config/rules.toml
The binary writes logs to stdout. Adjust RUST_LOG to trace or debug the flow.
Development
- Format:
cargo fmt - Check:
cargo check - Build:
cargo build --release
Packaging
- Systemd service unit for RustyPerms lives in
packaging/systemd/rustyperms.service; copy it into/usr/lib/systemd/system(or/etc/systemd/systemfor overrides) and enable it withsystemctl enable --now rustyperms.service. The unit expects a configuration file at/etc/rustyperms/rules.toml, so install your policy there before starting the daemon. - An Arch Linux
PKGBUILDsits at the repository root. Runmakepkgin this directory to build the package, then install it withpacman -U. The build script compiles the binary, installs the sample config into/etc/rustyperms/rules.toml, and drops the service unit under/usr/lib/systemd/system/rustyperms.serviceso the daemon is ready to enable.
Notes
- Because RustyPerms touches the filesystem, run it as a user or service account that has the appropriate privileges (typically root).
- The watcher ignores any directory that contains a
.zfscomponent, so snapshots do not trigger recursive enforcement. - You can customize the debounce window by setting the
debounce_millissetting inconfig/rules.toml. The default is 250 ms.