MountWatchdog is a small macOS autofs/SMB mount-state monitor and conservative recovery helper. It manages only mount names selected by the operator and leaves the existing autofs maps in charge of credentials and on-demand mounting.
The current release is v0.1.0.
MountWatchdog expects macOS autofs to already provide the SMB mounts you want to monitor. It does not configure autofs or credentials. The supported setup requires an active /- auto_smb or /- /etc/auto_smb entry and one compatible /etc/auto_smb record for each selected /Users/<current-user>/<mount-name> path; review the detailed setup guide before installing.
Clone and validate the release:
git clone https://github.com/enthouan/mount-watchdog.git
cd mount-watchdog
git switch --detach v0.1.0
/bin/bash tests/run.shReplace Archive Studio with the complete local mount-name set for this Mac, then preview the installation:
/bin/bash ./install_mount_watchdog.sh \
--dry-run \
--local-user "$(/usr/bin/whoami)" \
Archive StudioIf the autofs maps are readable only by root, run the same preview with sudo; do not loosen their permissions. The invoking shell still evaluates $(/usr/bin/whoami) before sudo.
Only after reviewing that plan and authorizing the live lifecycle change:
sudo /bin/bash ./install_mount_watchdog.sh \
--local-user "$(/usr/bin/whoami)" \
Archive StudioThe invoking shell evaluates $(/usr/bin/whoami) before sudo, so the installer receives the current macOS user rather than root. Verify the result through the installed read-only status command:
sudo /bin/bash '/Library/Application Support/MountWatchdog/status.sh' --statusFor prerequisites, expected output, upgrades, verification, and rollback, follow Setup.
The runtime compares the local mount table with credential-free host/share metadata and checks whether the host accepts a TCP connection on port 445. A mounted-reachable result therefore means only:
- the expected
smbfslayer appears at the exact configured path; and - the configured host accepted the limited TCP reachability check.
Every status also reports check_scope=mount-table-and-tcp and readability=not-tested. MountWatchdog does not prove authentication, filesystem readability, server identity, application recovery, or a usable new SMB session.
MountWatchdog observes SMB mounts that macOS autofs already defines. A supported setup has a direct-map entry in /etc/auto_master:
/- auto_smb
The corresponding /etc/auto_smb records use absolute local paths. Historical configurations used this general structural form:
/Users/<local-user>/<local-name> -fstype=smb,soft,noowners,nosuid ://<credentials>@<nas-host>/<remote-share>
<credentials> is a non-secret placeholder that documents prior configurations only, not a recommended map pattern. This usable sanitized example omits credentials, uses a reserved TEST-NET address, and maps the local name Archive to a different remote share name, Vault:
/Users/example/Archive -fstype=smb,soft,noowners,nosuid ://192.0.2.10/Vault
Local and remote names are independent. In this sanitized example, the local name Studio maps to the remote share Workspace; MountWatchdog therefore records both values and never derives the share from the local name. Some historical installations contained embedded SMB credentials in their maps. That is configuration context, not a recommended pattern or something to copy into this repository, logs, or diagnostics.
MountWatchdog never creates or edits /etc/auto_master, /etc/auto_smb, passwords, or Keychain entries. A recovery refresh asks autofs to reload already-defined mapping metadata; it does not browse the mount or proactively remount SMB. The next legitimate user or application access triggers the normal on-demand mount.
Every runtime tick first proves that the supported effective hookup is still present: the trusted master file must contain exactly one active /- auto_smb (or /- /etc/auto_smb) direct-map entry, the selected map must still be a trusted regular file, and each installed credential-free path/host/share tuple must still match it. The same check runs again immediately before every unmount and immediately before a coalesced automount -c. If a macOS update restores the stock master file and removes the direct-map entry, MountWatchdog reports action_state=configuration-drift with last_error=autofs-hook-missing, performs no mount snapshot or network probe, and runs neither umount nor automount -c.
Apple's stock master map imports Directory Service records through +auto_master. Moving the direct-map hookup into a local Open Directory automount record could make it survive updates to /etc/auto_master, while leaving /etc/auto_smb unchanged. That path is research-only future work: this release does not create, modify, or accept such a record as proof of the selected hookup.
MountWatchdog never reads, lists, creates, removes, or probes content below a managed mount path. It never force-unmounts. When a relevant transition makes recovery appropriate, it may request a normal unmount of a revalidated expected SMB layer and a coalesced automount -c refresh. The next legitimate user or application access performs the on-demand mount.
The utility does not edit auto_master, direct maps, SMB settings, Keychain, TCC, or SIP. Unexpected sources, non-SMB layers, ambiguous snapshots, and inspection failures block recovery rather than being guessed away.
Every privileged path is checked using both POSIX metadata and macOS ACL metadata. Canonical deny-only ACLs are accepted only on repository/protected-input ancestors where they can reduce access; any allow entry or noncanonical ACL is rejected. MountWatchdog-owned installed files, backups, locks, and runtime state must have no ACL entries. Newly created managed nodes are normalized to no ACL, while an ACL found later on an existing managed node fails closed rather than being silently removed.
mount_watchdog.shis the maintained periodic runtime.mount_watchdog_status.shis the separate read-only diagnostic entry point.install_mount_watchdog.shanduninstall_mount_watchdog.shown lifecycle operations.config/defaults.confcontains the shared 60-second interval, 120-second scheduling-gap heuristic, 180-second attempt cooldown, and 20-second supervisor wait bound.packaging/contains the LaunchDaemon template.examples/contains fictional, credential-free data.tests/uses synthetic fixtures and command adapters only.
The installation uses these locations:
/Library/Application Support/MountWatchdog/watchdog.sh
/Library/Application Support/MountWatchdog/mounts.conf
/Library/Application Support/MountWatchdog/backups/
/Library/LaunchDaemons/com.antoinemenard.mount-watchdog.plist
/var/run/com.antoinemenard.mount-watchdog/
/var/run/com.antoinemenard.mount-watchdog/heartbeat
/var/run/com.antoinemenard.mount-watchdog/<name>/status
/var/log/mount-watchdog.log
The maintained implementation adds these installed artifacts and explicit state/lock leaves:
/Library/Application Support/MountWatchdog/status.sh
/Library/Application Support/MountWatchdog/lib/common.sh
/Library/Application Support/MountWatchdog/lib/runtime.sh
/Library/Application Support/MountWatchdog/lib/autofs.sh
/Library/Application Support/MountWatchdog/defaults.conf
/Library/Application Support/MountWatchdog/VERSION
/Library/Application Support/MountWatchdog/install-manifest.tsv
/var/run/com.antoinemenard.mount-watchdog/blocked-command
/var/run/com.antoinemenard.mount-watchdog/autofs-refresh
/var/run/com.antoinemenard.mount-watchdog/.tick.lock/
/var/run/com.antoinemenard.mount-watchdog/<name>/unmount-attempt
/private/var/db/MountWatchdog.lifecycle.lock
/var/run resolves through macOS's trusted /var -> private/var alias. The volatile tick lock and durable action records belong to runtime recovery; the /private/var/db lifecycle lock serializes installer and uninstaller mutations. They have different recovery rules; see Troubleshooting and Lifecycle and rollback.
MountWatchdog installs nothing beneath /usr/local or /opt/homebrew. This keeps both Intel Homebrew's customary /usr/local prefix and Apple Silicon Homebrew's customary /opt/homebrew prefix outside its ownership and validation boundary. Launchd invokes the canonical root-owned runtime under /Library/Application Support/MountWatchdog directly.
The installed watchdog.sh runs a normal tick. It is not a status command and must not be invoked merely to inspect an installation.
From this directory, the canonical nonprivileged check is:
/bin/bash tests/run.shThe test command syntax-checks maintained shell sources, validates generated artifacts when available, and runs fixture tests without sudo, live mounts, launchd mutation, system-directory writes, or NAS access. See Testing for evidence boundaries and the direct harness command.
Read-only installed diagnostics are provided by the dedicated status script; established root-only file modes require the owner to run:
sudo /bin/bash '/Library/Application Support/MountWatchdog/status.sh' --statusRead-only means it performs no tick, state creation, TCP probe, unmount, refresh, or service mutation.
The installed runtime also has an explicit, state-mutating owner command for a narrow class of resolved manual-attention latches. It is not a diagnostic shortcut; follow the review and eligibility procedure in Troubleshooting before using --acknowledge-manual-attention.
Before installing, review Configuration, Lifecycle and rollback, and Troubleshooting. Always inspect each lifecycle script's --help output before running it.
MountWatchdog is available under the MIT License.