ntfsmnt
Read-write NTFS on macOS, from the command line.
macOS mounts NTFS read-only through its built-in FSKit driver. ntfsmnt replaces
that mount for one volume at a time with an ntfs-3g mount served by
FUSE-T, so ordinary user files can be written without
a kernel extension, without disabling SIP, and without a reboot.
$ uv run ntfsmnt list
DEVICE LABEL SIZE STATE MOUNT
/dev/disk4s1 HD1 1000296415232 ro /Volumes/HD1
/dev/disk4s2 HD2 1000065728512 ro /Volumes/HD2
$ uv run ntfsmnt mount HD1
mounted HD1 at /Volumes/HD1
$ printf hello > /Volumes/HD1/hello.txt # no sudo
$ rm /Volumes/HD1/hello.txt
$ uv run ntfsmnt unmount HD1
unmounted HD1
The tool reports what it actually did. It never guesses a volume, never repairs a filesystem on its own, and puts Apple's read-only mount back when an attempt fails.
Requirements
Verified on Apple Silicon, macOS 26.5.2 (25F84).
| Component | Version / location | Notes |
|---|---|---|
| macOS | 26.x on Apple Silicon | Intel untested |
| Python | ≥ 3.12 | no runtime third-party dependencies |
| uv | any recent version | for install and development |
| FUSE-T | 1.2.9, /usr/local/lib/libfuse-t.dylib |
installed by scripts/setup.sh |
| ntfs-3g | 2022.10.3 (built from edge), /usr/local/bin/ntfs-3g |
FUSE-T fork, must link libfuse-t; same script |
| Terminal | Full Disk Access granted | see TCC / Full Disk Access |
scripts/setup.sh installs both native prerequisites: the
FUSE-T cask with brew install --cask fuse-t, and the FUSE-T-linked ntfs-3g
built from https://github.com/macos-fuse-t/ntfs-3g (branch edge, overridable
with NTFS3G_REF) into /usr/local/bin/ntfs-3g. It installs the build tooling
(autoconf, automake, libtool, pkgconf) through Homebrew when missing and
prints the resolved ntfs-3g commit. Run ./scripts/setup.sh --check first to
see what is missing without changing anything.
To build ntfs-3g by hand instead:
git clone https://github.com/macos-fuse-t/ntfs-3g.git
cd ntfs-3g
./autogen.sh && ./configure && make
sudo make install
otool -L /usr/local/bin/ntfs-3g | grep libfuse-t
ntfs-3g here is the FUSE-T fork, not the macFUSE build and not a Homebrew
build. On macOS the fork hardcodes -I/usr/local/include/fuse and -lfuse-t
in src/Makefile.am, so the build needs no extra flags (configure only uses
pkg-config to locate the library directory). doctor reports a missing
FUSE-T, an ntfs-3g that is not linked against FUSE-T, a missing root, and a
device that cannot be opened.
Install
Three blocks: the native prerequisites once per Mac, the CLI itself, and a checkout for contributors.
1. Prerequisites
From a checkout, run the setup script:
git clone https://github.com/AkshayHarti/ntfs-mounter.git
cd ntfs-mounter
./scripts/setup.sh
It installs FUSE-T and builds ntfs-3g against it, calling sudo only for
make install; the FUSE-T cask asks for an administrator password through its
own installer. It is idempotent — satisfied steps are skipped — and
./scripts/setup.sh --check reports the state without changing anything. It
ends with the Full Disk Access reminder; that grant is manual, see
TCC / Full Disk Access.
2. The CLI from PyPI
uv tool install "ntfsmnt[web]"
ntfsmnt doctor
pipx install "ntfsmnt[web]" is the alternative if you use pipx. Drop [web]
for the CLI without the browser UI. ntfsmnt doctor reports FUSE-T, ntfs-3g,
root, and device access; run unprivileged, the root and device-access checks
fail by design. mount, unmount, and eject re-exec through sudo from your
terminal — see Privileges and the sudo re-exec
— which is why that terminal needs Full Disk Access.
3. From a checkout (development)
git clone https://github.com/AkshayHarti/ntfs-mounter.git
cd ntfs-mounter
uv sync --extra web
uv run ntfsmnt list
uv run ntfsmnt ... runs the checkout's code. uv tool install --editable .
puts that same checkout on your PATH; the installed command re-execs through
sudo using the interpreter of the environment it was installed into, so it
keeps working outside the checkout.
Commands
ntfsmnt list [--json]
ntfsmnt doctor [--json]
ntfsmnt status <volume> [--json]
ntfsmnt mount <volume> [--recover] [--remove-hiberfile] [--json]
ntfsmnt unmount <volume> [--keep-unmounted] [--json]
ntfsmnt eject <volume> [--json]
ntfsmnt web [--port N] [--open] [--owner UID:GID]
A <volume> can be a label (HD1), a device node (/dev/disk4s1), a diskutil
identifier (disk4s1), a volume UUID, or a mount point (/Volumes/HD1).
Identifiers, UUIDs, labels, and mount points are tried in that order. If a
label matches more than one attached volume, the command fails and lists the
candidates rather than picking one.
list
uv run ntfsmnt list
Prints one row per NTFS volume currently known to diskutil:
DEVICE— device node.LABEL— volume name.SIZE— size in bytes.STATE—rwwhen afuse-t:mount serves the volume,rowhen Apple's read-only mount is present,unmountedotherwise.MOUNT— mount point, or-.
Volumes served by FUSE-T are NFS mounts: diskutil list still lists the
partition, but it does not report our NFS mount as a mount point. ntfsmnt list
combines diskutil device information with the live mount table.
doctor
uv run ntfsmnt doctor
Checks, in order: macOS/architecture (informational), FUSE-T library present,
ntfs-3g present and linked against FUSE-T, running as root, and whether the
first NTFS device can be opened read-write with ntfs-3g.probe. The licence
line is informational.
Checks that know a remedy print a fix: line. Exit code is 1 when any hard
check (fuse-t, ntfs-3g, root, device-access) fails, 0 otherwise.
Run it unprivileged to see the environment, and with sudo from the same
terminal to check root and device access:
uv run ntfsmnt doctor # root and device-access FAIL is expected
sudo uv run ntfsmnt doctor # every check should be ok
If device-access fails as root, read the detail before assuming TCC. A busy
result means the probed volume is still mounted: unmount it, re-run doctor,
then remount. A Permission denied / EPERM result is the Full Disk Access
signal.
If sudo cannot find uv, use sudo "$(command -v uv)" run ntfsmnt doctor.
status
uv run ntfsmnt status HD1
# HD1 rw fuse-t /Volumes/HD1 /dev/disk4s1 48213
Prints label, effective state, driver, mount point, device, and the serving process id.
driver—fuse-twhenntfsmntserves the volume read-write,fskitwhen Apple's read-only mount is the one present,-when the volume is unmounted.pid— thentfs-3gprocess serving a read-write mount, found withpgrep -f 'ntfs-3g .*<device>( |$)';-when there is none.
mount
uv run ntfsmnt mount HD1
This is the command that makes a volume writable. It:
- resolves the volume and refuses if the label is ambiguous;
- refuses a volume with no label — the label names the mount — before it touches anyone's mount (see Limitations);
- returns success immediately when the volume already has a FUSE-T mount at
that mount point: with a unique label any
fuse-t:/<label>entry is this volume's, even ifpgrepcannot answer; with a shared label only a mount whose servingntfs-3gprocess names this device counts. A live mount of this label somewhere else is not a success for this path —mounttargets the requested mount point; - creates the mount point if needed;
- unmounts Apple's read-only mount of this device, or Apple's mount at that mount point. It never unmounts a FUSE-T mount;
- waits for the device node to reappear (10 one-second attempts);
- runs
ntfs-3g <device> <mount point>withuid=<you>,gid=<your group>,umask=022,local,auto_xattr,noatime,volname=<label>; - waits up to 30 seconds for the
fuse-t:NFS mount to appear, and evicts a competing Apple mount if it reappeared at a different mount point; - writes, reads back, and deletes
.ntfsmnt-probeinside the volume; - checks with
statthat the mount point is owned by your uid and gid — the probe ran as root, so it cannot prove that you can write; - prints
mounted HD1 at /Volumes/HD1.
If /Volumes/DATA is already a read-write mount of another volume that shares
this volume's label, mount refuses and names the path and the label instead of
evicting the live mount:
ntfsmnt: mount point /Volumes/DATA is already a read-write mount; its volume
shares the label 'DATA' with /dev/disk6s1 — unmount it before mounting this one
Unmount it first (by its mount point, or by the other volume's device node) and retry.
If any step fails, Apple's read-only mount is restored before the command exits
1 with the reason. A mount that exists but cannot be written is a failure, not
a success. When ntfs-3g exits cleanly but the mount never appears, the message
says so and names the timeout instead of reporting an exit code 0 failure.
Options:
--recover— letntfs-3greplay the NTFS journal on a dirty volume. This mutates the filesystem. See Hibernated and dirty volumes.--remove-hiberfile— deletehiberfil.sysand mount a volume left hibernated by Windows Fast Startup. This discards the hibernated Windows session and requires you to type the volume label before anything runs. See the same section.--json— see JSON output.
unmount
uv run ntfsmnt unmount HD1
Tears down the FUSE-T mount, then unmounts Apple's read-only mount for the same
device, then restores Apple's read-only mount. The result is the state you
started in: /Volumes/HD1 present and ro.
It tries diskutil unmount, then umount. If the volume is busy it sends
SIGTERM to the serving ntfs-3g process once and retries. If it is still
busy, it reports which mount point is in use and exits 1; it never forces or
kills with SIGKILL.
Which mounts belong to the volume is decided by the entry's own source first, then
by the serving process. A fuse-t:/<label> entry can only belong to a volume
with that label; an entry naming another label is another volume's mount and is
left alone, whatever process is running. Within the volume's label: when exactly
one NTFS volume carries the label, the entry is that volume's and pgrep is only
corroboration; when two or more share it, only the serving ntfs-3g process can
decide. If the label is shared and pgrep cannot answer, unmount refuses with
a manual instruction rather than guessing, and it never unmounts a fuse-t:
entry that resolves to another volume:
ntfsmnt: The mount at /Volumes/HD1 appears to belong to another volume with the
same label 'HD1'; it was not unmounted. If it should be gone, unmount it
manually: sudo diskutil unmount /Volumes/HD1
A live mount that names a label the volume no longer carries is the relabelled case. It sits at the volume's own path, so it is not reported as unmounted:
ntfsmnt: A read-write mount at /Volumes/HD1-old serves this volume but its label
differs from 'HD1': the entry carries 'HD1-old'. Unmount it manually with:
sudo diskutil unmount /Volumes/HD1-old
eject counts that entry as mounted too: it refuses with "Unmount it first"
instead of ejecting a disk that still has a live ntfs-3g mount on it.
If a busy volume is still mounted after SIGTERM, the read-only restore is
skipped rather than forced through a live mount — mounting then would be
refused or land at /Volumes/HD1 1. The command reports the restore as failed
and leaves the read-write mount in place.
If the read-only mount cannot be put back, the unmount still happened, so the
command says so and exits 1:
ntfsmnt: unmounted HD1 (read-only restore failed; run sudo diskutil mount /dev/disk4s1)
--keep-unmounted stops after the unmount and does not restore the read-only
mount. Use it before eject.
Running unmount on a volume that is already read-only is safe: it unmounts
and remounts Apple's read-only mount.
eject
uv run ntfsmnt unmount HD1 --keep-unmounted
uv run ntfsmnt eject HD1
Ejects the parent disk (for /dev/disk4s1 that is disk4), so every volume
on the same physical drive disappears with it. The guard is per-volume: eject
refuses only when the named volume itself is still mounted — checked against the
live mount table and the serving ntfs-3g process, so a mount served by a
same-label volume does not count, while an entry pgrep cannot resolve refuses
the eject — so a still-mounted sibling such as HD2 is ejected
along with it. Re-plug the drive — or, if it is still attached, run
sudo diskutil mount /dev/disk4s1 and sudo diskutil mount /dev/disk4s2 — to
get both read-only mounts back.
Web UI
An optional local web UI shows the same volumes and performs the same mount,
unmount, and eject operations as the CLI. It is not installed by default; the
web extra adds FastAPI, uvicorn, and Jinja2:
uv sync --extra web # from a checkout
uv tool install 'ntfsmnt[web]' # installed command
Start it explicitly as root:
sudo uv run ntfsmnt web # bind an OS-chosen loopback port
sudo uv run ntfsmnt web --open # also open the browser
sudo uv run ntfsmnt web --port 8765 # bind a fixed port
sudo uv run ntfsmnt web --owner 501:20 # mount owner, when SUDO_UID/GID is absent
It prints one line and keeps running until Ctrl-C:
ntfsmnt web: http://127.0.0.1:<port>/?token=<random>
<port> is the OS-chosen port, or the value of --port.
The URL is loopback-only. The token is a one-time launch secret: opening the URL
exchanges it for an HttpOnly session cookie and redirects to /, so the token
leaves the address bar but remains the capability for that session. Treat the
printed URL as a password for the duration of the run; restarting the command
generates a new token.
Started without root, the server runs read-only: the page shows a read-only
banner, action buttons stay disabled, and the mutation API answers 403. It
still lists volumes and their states. --owner defaults to SUDO_UID/SUDO_GID
from the sudo environment; a root launch with neither those variables nor
--owner is also read-only, because mounts would otherwise be owned by root.
Closing the browser tab does not unmount, eject, or otherwise change any
volume: the server keeps running until Ctrl-C, and the mounts it made persist
after it exits. The CLI remains the source of truth — the page calls the same
mount, unmount, and eject code, and ntfsmnt list is always the authoritative
view of the current state regardless of what a stale page shows.
JSON output
Every command accepts --json. Output goes to stdout as a single JSON value;
diagnostics stay on stderr.
list — array of volume objects:
[
{
"device": "/dev/disk4s1",
"label": "HD1",
"uuid": "E69303DC-BC97-42BA-B95E-98D777D099DC",
"size": 1000296415232,
"state": "ro",
"mount_point": "/Volumes/HD1"
}
]
status — one object with the same six keys plus driver and pid:
{
"device": "/dev/disk4s1",
"label": "HD1",
"uuid": "E69303DC-BC97-42BA-B95E-98D777D099DC",
"size": 1000296415232,
"state": "rw",
"mount_point": "/Volumes/HD1",
"driver": "fuse-t",
"pid": 48213
}
driver is "fuse-t" when ntfsmnt serves the volume read-write, "fskit"
when Apple's read-only mount is present, and "-" when the volume is
unmounted. pid is the serving ntfs-3g process, or null.
doctor — array of checks:
[
{"name": "fuse-t", "ok": true, "detail": "FUSE-T installed at /usr/local/lib/libfuse-t.dylib", "fix": null},
{"name": "root", "ok": false, "detail": "running as uid 501, not root", "fix": "Run via sudo from a terminal that has Full Disk Access"}
]
mount — mount point and the serving process id when it can be found:
{"mount_point": "/Volumes/HD1", "pid": 48213}
pid is null when the volume was already mounted by FUSE-T and no new process
was started, or when pgrep finds nothing.
unmount — {"ok": true, "restored": true}. restored is false when the
teardown happened but Apple's read-only mount could not be put back; the exit
code is then 1, exactly as in the text output. eject — {"ok": true}.
The --remove-hiberfile confirmation prompt is written to stderr, so stdout is
a single JSON value even when you are being prompted.
state is one of "rw", "ro", "unmounted". mount_point is null when
the volume is unmounted. fix is null when no fix is needed.
Privileges and the sudo re-exec
list, status, and unprivileged doctor run as you. Raw /dev/disk* access
needs root, so mount, unmount, and eject re-execute the CLI through sudo:
ntfsmnt mount HD1
→ resolve HD1 → /dev/disk4s1
→ os.execvp("sudo", ["sudo", <python>, "-m", "ntfsmnt", "mount", "/dev/disk4s1", ...])
Consequences worth knowing:
- You type the password in your own terminal once per command. There is no GUI password dialog.
- The volume is resolved and any destructive flag is confirmed before the sudo re-exec, so a typo or a refused label confirmation never costs a password prompt.
- The re-exec uses the same Python interpreter that is running the CLI, so it
works with
uv runand withuv tool install. SUDO_UID/SUDO_GIDare used to set the mount'suid/gidto the user who invoked the command, not to root. That is why writes work without sudo afterwards.- The command never uses
osascript ... with administrator privileges. See the TCC note below.
TCC / Full Disk Access
macOS attributes raw disk access to the responsible process, which is your
terminal application. Root alone is not enough: without Full Disk Access the
kernel returns Operation not permitted (EPERM) even for sudo.
Grant Full Disk Access to the terminal app you use (System Settings → Privacy &
Security → Full Disk Access), then quit and reopen that app. doctor reports
device-access failure with a fix: line when this is missing.
osascript -e 'do shell script ... with administrator privileges' is
deliberately not used. The privileged work is then attributed to osascript,
which cannot be granted Full Disk Access, so that path can never work. Running
sudo from your terminal keeps the terminal's TCC grant.
Safety model
What the tool will not do:
- Never calls
ntfsfix,ntfsresize,mkntfs, orntfswipe. There is no auto-repair. - Never passes
remove_hiberfileunless you asked for--remove-hiberfileand typed the volume label.--recoveris passed only when you ask for it. - Never sends
SIGKILLtontfs-3g. The only signal it sends isSIGTERM, once, after a normal unmount failed as busy. - The unmount command never forces a busy volume: it reports the mount point
and exits
1. At mount time, Apple's read-only mount is evicted withdiskutil unmount, falling back toumount -fif that fails. - Never evicts a read-write FUSE-T mount. A
fuse-t:mount at the target belongs to a volume, possibly one sharing the label;mountrefuses and names the path instead of unmounting it. - Never guesses between volumes with the same label; it lists candidates and
stops.
mount,unmount, andejectrefuse rather than touch a same-label mount they cannot attribute. - Never leaves a failed mount half-applied: it restores Apple's read-only mount before exiting non-zero, including when the run is interrupted. The message says the mount was restored only when the mount table can be re-read and shows it; otherwise it says the restore was attempted.
- Never mounts a volume that has no label: relabel it first.
- Never mounts read-write without proving the mount is writable.
- Never writes to a hibernated or dirty volume automatically. Without the opt-in flag, the attempt fails and the read-only mount is restored.
The tool is not a filesystem checker. If NTFS is damaged, connect the volume to
Windows and let chkdsk handle it.
Hibernated and dirty volumes
Two NTFS states make ntfs-3g refuse a read-write mount:
- Hibernated. Windows Fast Startup and hibernation write memory state to
hiberfil.sysand leave the filesystem marked in use. Mounting read-write would let macOS and Windows disagree about the same files.ntfs-3grefuses unless the hibernation file is removed.--remove-hiberfiledoes that: it discards the hibernated Windows session, so any unsaved work in that session is lost. The CLI therefore prompts you to type the exact volume label before running it, and never enables it implicitly. The safer fix is to boot Windows and shut it down fully (or disable Fast Startup), then reconnect. - Dirty. An unclean shutdown leaves the NTFS journal unreplayed and the
volume marked dirty.
--recoverletsntfs-3greplay the journal and mount read-write; this mutates the filesystem. It is off by default and is not accompanied by a label prompt, so use it only when you understand why the volume is dirty. The safer fix is to let Windows runchkdsk.
Both flags apply only to the command you pass them to. Nothing is persisted.
Troubleshooting
Start with doctor, unprivileged and then with sudo:
| Symptom | Meaning | Fix |
|---|---|---|
fuse-t: ... not found |
FUSE-T is not installed | brew install --cask fuse-t |
ntfs-3g: ... not found |
wrong install prefix | install the FUSE-T fork at /usr/local/bin/ntfs-3g |
ntfs-3g is not linked against FUSE-T |
macFUSE build or broken linkage | rebuild from macos-fuse-t/ntfs-3g |
root: running as uid ... unprivileged |
expected before the sudo re-exec | run with sudo, or let mount re-exec |
device-access: ... Permission denied as root |
terminal lacks FDA | grant Full Disk Access, restart the terminal |
device-access: ... busy |
the probed volume is mounted; not a TCC failure | unmount it before probing |
mount: Permission denied opening the disk |
FDA on the responsible terminal | same as above |
mount: volume is marked dirty |
unreplayed NTFS journal | --recover, or chkdsk on Windows |
mount: volume is hibernated |
Windows Fast Startup/hibernation | --remove-hiberfile (destroys the hibernated session), or boot Windows |
mount: already mounted or files ... open |
busy volume | close files/Finder windows, unmount |
mount: already a read-write mount |
another volume with the same label holds the mount point | unmount it first, then mount this volume |
unmount: still in use |
open files or Spotlight | close them; do not force |
unmount: appears to belong to another volume |
the mount is a same-label volume's; the snapshot misattributed it | unmount that mount point manually if it should be gone |
unmount: Cannot tell whether |
shared label and pgrep cannot identify the server |
check mount, then unmount manually |
eject: is still mounted |
eject refuses only while the named volume is mounted |
unmount --keep-unmounted first; siblings are not checked |
no NTFS volume matching |
wrong label/device | ntfsmnt list |
matches multiple volumes |
duplicate labels | use the device node or UUID |
Only a Permission denied / EPERM device-access failure at root points to
Full Disk Access. A busy result just means the probed volume is mounted.
If a mount attempt fails, check that Apple's read-only mount is back:
mount | grep HD1
# /dev/disk4s1 on /Volumes/HD1 (ntfs, local, ..., read-only, ...)
If it is missing, sudo diskutil mount /dev/disk4s1 restores it. Do not run
ntfsfix or any other repair tool.
Limitations
- FUSE-T licence. FUSE-T is free for non-commercial use only. Commercial
use requires a licence from the FUSE-T project.
doctorprints this as an informational line; the tool does not enforce or check licensing. - The mount is NFS, not a device mount.
mountshowsfuse-t:/HD1 on /Volumes/HD1 (nfs);diskutil liststill lists the partition but does not report the NFS mount as a mount point, and anntfs-3gprocess serves it. Tools that assume a local block-device mount may behave differently. - Apple remount race. macOS can remount the device read-only after the tool has evicted its mount. The tool evicts competitors and proves writability with a real write, but a future macOS release could make this race harder or impossible to win.
- Not tested against BitLocker-encrypted or compressed NTFS volumes. Only plain NTFS volumes were exercised. Treat those layouts as unsupported until tested.
- No force option for unmount. The
unmountcommand reports a busy volume instead of forcing it, there is no--force, andSIGKILLis never used. At mount time Apple's read-only mount is evicted withdiskutil unmount, falling back toumount -f; the mount failure path likewise force-unmounts our own FUSE-T mount ifdiskutil unmountrefuses it, before restoring the read-only mount — it only runs that unmount when a fresh table read shows afuse-t:entry at the mount point (or the table cannot be read). A FUSE-T mount is never evicted to make room for another volume. - Duplicate labels.
listandstatusattribute afuse-t:/<label>entry by label, so when two NTFS volumes share a label the mount may be shown on the wrong row.mount,unmount, andejectresolve it by device and refuse when they cannot; use the device node or UUID to name the right volume. - A relabelled volume's old mount is not claimed. A FUSE-T mount records the
label it was mounted with. If the volume has been relabelled since, its entry
names the old label, so the tool cannot claim it:
mountrefuses at the occupied path,unmountrefuses with the manual instruction above, andejectrefuses rather than ejecting a disk under a live mount. Free it by mount point (sudo diskutil unmount /Volumes/<old>) and mount again. - Spotlight and Finder can hold files open and block deletes or unmounts. The tool does not disable indexing.
- Hibernated and dirty volumes are refused by default; the opt-in flags mutate the filesystem as described above.
- Performance over the NFS indirection has not been benchmarked; USB speed is expected to dominate.
- Unlabeled volumes are refused. The label names the FUSE-T mount source and the default mount point, and two unlabeled volumes cannot be told apart. Relabel the volume, then mount it.
- One volume at a time in practice. Mounting HD2 read-write while HD1 is read-write is supported by the code paths but was not part of the verified acceptance run.
- Intel Macs and other macOS versions are untested. The verified target is Apple Silicon, macOS 26.5.2.
Development
uv sync --extra web
uv run pytest -q
Without --extra web the FastAPI/Jinja API, page, and schema tests skip; the
service, models, and config tests still run.
The suite has 284 tests and never touches hardware. Subprocess effects
(diskutil, ntfs-3g, mount, pgrep, kill, sleep) are injected as
callables, and the mount-point creation, write probe, and ownership stat are
monkeypatched in tests that exercise the orchestration.
Manual acceptance on real hardware is documented in
tests/manual/acceptance.md. It covers HD1 only
and needs root, a real NTFS volume, and a Full Disk Access terminal.
tests/manual/fuset-mount-test.sh is the older root-only probe that predates
the CLI, kept verbatim as reference behaviour.
The mount/write/unmount path is not covered by CI. The stack itself was proven with that root-only probe before the CLI existed; the current acceptance path is the CLI procedure, run by hand and recorded by whoever runs it.
Uninstall
Unmount any read-write volume first, so FUSE-T is not removed under a live NFS mount:
ntfsmnt unmount HD1
Then remove the command, if you installed it with uv tool install or pipx,
and the stack if you no longer need it:
uv tool uninstall ntfsmnt # or: pipx uninstall ntfsmnt
brew uninstall --cask fuse-t
ntfs-3g was built from source and is not managed by this repo; remove it with
the build directory's install rules if you want it gone. The build directory is
a cache, so it survives uninstalling the CLI:
rm -rf ~/Library/Caches/ntfsmnt/ntfs-3g
($XDG_CACHE_HOME/ntfsmnt/ntfs-3g when XDG_CACHE_HOME is set.) Deleting the
checkout removes the CLI source and its virtual environment.
Metadata
Release files for ntfsmnt 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| ntfsmnt-0.1.0.tar.gz | 195.1 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ntfsmnt-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 275.8 kB
Release files / ntfsmnt-0.1.0.tar.gz
| Download URL | ntfsmnt-0.1.0.tar.gz |
|---|---|
| Size | 195.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f6482767ad234df3003b809177e8f25ab3f4ab174eb1a2720f41936b066b5670
|
|
BLAKE2b-256 checksum How to use checksums |
bb9d9c9f157ce12dec72247443c66a5f51312a04e952093b16d5e7ce3ad40c40
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logRelease files / ntfsmnt-0.1.0-py3-none-any.whl
| Download URL | ntfsmnt-0.1.0-py3-none-any.whl |
|---|---|
| Size | 80.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
77050d31666b2450eb6d076f1a7dec60f404528375d45755bebdafb23a06c280
|
|
BLAKE2b-256 checksum How to use checksums |
7ff8956beeb507a31b8de1be37e6eecff9dd3313b46cce65a8539c45be3b8bae
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logSecurity for ntfsmnt 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for ntfsmnt-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.