Command Reference¶
Every protonfs command, with its synopsis, arguments/options, what it actually
does, and worked examples. This page describes behavior; for the frozen contract
(exact exit codes, option names, config keys, env vars that will not change without
a major version bump) see Stability Promise (M4.1).
Synopsis¶
protonfs¶
Sync a local directory tree with Proton Drive.
Usage
protonfs [OPTIONS] COMMAND [ARGS]...
Options
- --version¶
Show the version and exit.
- -v, --verbose¶
Increase console detail (-v..-vvvv).
- --progress-inline, --progress-lines¶
Update progress in place (inline) vs. print each poll on a new line. Default: config (defaults.progress_style), else inline on a TTY.
- --event-log, --no-event-log¶
Write a full debug event log to .protonfs/events.log. Default: config (defaults.event_log), else off.
protonfs is the command group; every operation is a subcommand documented under
Subcommands. The global options above (verbosity, progress
style, event log) may appear before or after the subcommand.
Examples:
protonfs setup && protonfs push # first-time setup, then upload
protonfs refresh && protonfs pull # first pull on a new machine
protonfs status; echo "exit=$?" # drift check for scripts
Global behavior¶
Every command that mutates a repo’s index (push, pull, rm, restore,
refresh, offload) takes an exclusive, non-blocking advisory lock on
.protonfs/lock for its duration, so two protonfs processes never interleave
writes to index.json. If another process already holds it, the command fails
fast with an instructive message rather than blocking or racing (see
Durability and Drift-Resolution Guarantees).
Runtime commands that shell out to proton-drive (everything except config
and shell-init) share an error boundary: a Drive/auth failure, a locked
keyring, or a held repo lock all surface as a clean one-line error instead of a
Python traceback. An auth failure additionally suggests protonfs auth login.
Diagnostics & verbosity¶
Added in version 1.3.0: Layered -v verbosity, the progress-style flag, and the rotating event log.
Changed in version 1.4.0: Global options became position-independent — they may appear before or after the
subcommand (see reorder_argv()).
Every protonfs command accepts three global options, which may appear before or
after the subcommand (e.g. protonfs -vv --event-log pull or protonfs pull -vv),
that control how much it narrates and where that narration goes. All narration goes to stderr; each command’s result
summary stays on stdout, so piping/scripting a command’s output is unaffected by
verbosity. A flag, when given, overrides its config key; when unset, the resolved
config value is used, falling back to the built-in default (see Stability Promise (M4.1)
for the frozen option/config contract).
Verbosity ladder (-v)¶
-v is repeatable, from -v up to -vvvv. Each level is a superset of the one
below it.
Level |
Console detail |
|---|---|
(none) |
Warnings/errors only, plus each command’s normal stdout result summary. |
|
Adds curated phase narration, progress updates throttled to roughly every 30s, and phase durations. |
|
Adds per-item paths (each file transferred/affected) and sub-steps; progress throttled to roughly every 5s. |
|
Adds |
|
|
Progress style (--progress-inline / --progress-lines)¶
Controls how progress updates are rendered on stderr:
--progress-inline— one live line, rewritten in place as progress advances. This is the default when stderr is a TTY. An open inline progress line is always closed (a newline written) before any other message, so nothing it printed is overwritten.--progress-lines— every poll gets its own line instead of rewriting in place. Used automatically when stderr is not a TTY (e.g. redirected to a file or a CI log), regardless of the configured/default style.
Default: the defaults.progress_style config key, else inline on a TTY.
Event log (--event-log / --no-event-log)¶
When enabled, protonfs writes a structured log to .protonfs/events.log, rotating
at roughly 5 MB with one backup file (events.log.1). Each line is aligned text:
TIMESTAMP LEVEL component message key=value. The event log always records full
DEBUG-level detail while enabled, independent of the console -v level chosen
for the same run; the proton-drive subprocess’s stderr is included in it only at
-vvvv.
.protonfs/events.log is gitignored automatically — new repos get this from the
setup template, and existing repos pick it up via a protonfs upgrade
migration. protonfs deinit removes it along with the rest of .protonfs/. It is
never treated as sync payload: protonfs excludes .protonfs/ from scans entirely.
Default: the defaults.event_log config key, else off.
Configuration¶
Environment variables¶
- PROTONFS_CONFIG¶
Overrides the global config file path outright.
Added in version 1.0.0.
- PROTONFS_REMOTE_ROOT¶
Per-key override for
remote_root.Added in version 1.0.0.
- PROTONFS_ON_CONFLICT¶
Per-key override for
defaults.on_conflict.Added in version 1.0.0.
- PROTONFS_LOW_IO¶
Per-key override for
defaults.low_io.Added in version 1.0.0.
- PROTONFS_EVENT_LOG¶
Per-key override for
defaults.event_log.Added in version 1.3.0.
- PROTONFS_PROGRESS_STYLE¶
Per-key override for
defaults.progress_style.Added in version 1.3.0.
Operational & tuning environment variables¶
These have no config-key equivalent; they tune how protonfs invokes and installs the
proton-drive binary and how it bootstraps the keyring. All are part of the frozen
contract (see Stability Promise (M4.1)).
- PROTONFS_DRIVE_BIN¶
Path/name of the
proton-drivebinary to invoke, in place of the default.Added in version 1.0.0.
- PROTONFS_DRIVE_VERSION¶
Overrides the
proton-driveversioninstall-driveinstalls when--versionis not passed.Added in version 1.0.0.
- PROTONFS_DRIVE_SHA512¶
Explicit SHA-512 to verify a
proton-drivedownload against, required for versions/platforms without a built-in checksum pin.Added in version 1.0.0.
- PROTONFS_LIST_TIMEOUT¶
Timeout in seconds for a Drive listing call (default
45).Added in version 1.0.0.
- PROTONFS_LIST_RETRIES¶
Max retries for a Drive listing call (default
4).Added in version 1.0.0.
- PROTONFS_LIST_BACKOFF¶
Base backoff in seconds between Drive listing retries (default
2).Added in version 1.0.0.
- PROTONFS_LIST_BACKOFF_CAP¶
Cap in seconds on the Drive listing retry backoff (default
60).Added in version 1.0.0.
- PROTONFS_TRANSFER_TIMEOUT¶
Timeout in seconds for a Drive upload/download call (default
300).Added in version 1.0.0.
- PROTONFS_TRANSFER_RETRIES¶
Max retries for a Drive upload/download call (default
4).Added in version 1.0.0.
- PROTONFS_TRANSFER_BACKOFF¶
Base backoff in seconds between Drive upload/download retries (default
2).Added in version 1.0.0.
- PROTONFS_TRANSFER_BACKOFF_CAP¶
Cap in seconds on the Drive upload/download retry backoff (default
60).Added in version 1.0.0.
- PROTONFS_KEYRING_PASSWORD¶
Supplies the password for the protonfs-owned keyring bootstrap, instead of generating one.
Added in version 1.0.0.
- PROTONFS_MANIFEST¶
Per-key override for
defaults.manifest.Added in version 2.1.0.
- PROTONFS_NO_MANIFEST¶
Set (to any truthy value) to switch every remote-manifest read and write off on this host, whatever
defaults.manifestsays.Added in version 2.1.0.
- PROTONFS_NO_KEYRING_BOOTSTRAP¶
Set (to any truthy value) to disable protonfs’s Secret Service/keyring bootstrap entirely; the caller is responsible for providing one.
Added in version 1.0.0.
- PROTONFS_CREDENTIALS_STORE¶
Forces which proton-drive credentials store protonfs prepares and uses:
keychain(freedesktop Secret Service) orpass(password-store). The default,auto, trieskeychainfirst and falls back to a protonfs-managedpassstore only when the Secret Service cannot be made ready on this host (see Credentials store: automatic pass fallback). The resolved choice is written to a per-host sticky file so later commands never read a different (empty) store. NativePROTON_DRIVE_CREDENTIALS_STORE, if already set in the environment, is passed straight through toproton-driveand takes precedence over this variable’s resolution.Added in version 1.9.0.
Configuration file and keys¶
- remote_root¶
- Type:
- str
Proton Drive path that maps to this repo’s ProtonFS root. Overridable via
PROTONFS_REMOTE_ROOT.Added in version 1.0.0.
- device_id¶
- Type:
- str
Stable identifier for this client device in the index. Overridable via
PROTONFS_DEVICE_ID.Added in version 1.0.0.
- defaults.on_conflict¶
- Type:
- str
- Default:
- "skip"
Action when a file is in
SyncStateconflict. Overridable viaPROTONFS_ON_CONFLICT.Added in version 1.0.0.
- defaults.low_io¶
- Type:
- bool
- Default:
- false
Skip hashing unchanged files. Overridable via
PROTONFS_LOW_IO.Added in version 1.0.0.
- defaults.event_log¶
- Type:
- bool
- Default:
- false
Enable the structured rotating event log. Overridable via
PROTONFS_EVENT_LOG.Added in version 1.3.0.
- defaults.progress_style¶
- Type:
- str
- Default:
- "inline"
Progress display style,
inlineorlines. Overridable viaPROTONFS_PROGRESS_STYLE.Added in version 1.3.0.
- defaults.manifest¶
- Type:
- bool
- Default:
- false
Keep the remote manifest (see verify) current on
pushandrm. Only an existing manifest is updated;protonfs verify --repaircreates one. Set it in the sharedconfig.jsonso every host maintains it. Overridable viaPROTONFS_MANIFEST.Added in version 2.1.0.
Subcommands¶
setup¶
protonfs setup¶
Install/verify the proton-drive CLI, init .protonfs/, migrate off git-lfs if present.
Usage
protonfs setup [OPTIONS]
Options
- --dry-run¶
Preview the LFS migration without making changes.
- --migrate-lfs, --no-migrate-lfs¶
Force or skip the repo-wide git-LFS migration. Default: migrate only when this directory is the git toplevel, so setting up a subdirectory never migrates the enclosing repo off LFS.
Installs/verifies the proton-drive CLI, initializes .protonfs/ in the
current directory (config.json, ignore, a .gitignore that excludes
index.json/refresh-state.json, and a .gitattributes that exempts those
control files from git-LFS), and migrates the repo off git-LFS if it is tracked.
It also creates the configured remote_root on Drive if it does not exist yet,
so the first push has somewhere to land without hand-creating folders.
Default: migrate only when the current directory is the git toplevel, so running
setup in a subdirectory of a larger repo never migrates the enclosing repo off
LFS by surprise.
Examples:
protonfs setup # first-time setup in the current directory
protonfs setup --dry-run # see what setup would do first
protonfs setup --no-migrate-lfs # set up without touching git-LFS either way
deinit¶
protonfs deinit¶
Remove .protonfs/ from this directory: clean teardown of a protonfs root.
Only protonfs’s own bookkeeping under .protonfs/ (config, index, ignore/include, control .gitattributes/.gitignore) is removed – synced payload files, local or remote, are never touched.
Usage
protonfs deinit [OPTIONS]
Options
- --dry-run¶
List what would be removed; delete nothing.
- --yes¶
Skip confirmation prompt.
The inverse of setup: removes every file setup writes under .protonfs/
— the shared config.json, the per-device config.local.json, the index,
the resumable-refresh state, ignore/include, and the control
.gitattributes/.gitignore — after printing a summary and asking for
confirmation. It only ever looks inside .protonfs/: synced payload files,
local or remote, are never touched, so deinit is a clean teardown of protonfs’s own
bookkeeping and nothing else.
Examples:
protonfs deinit --dry-run # see exactly which .protonfs/ files would go
protonfs deinit # remove them after confirming
status¶
protonfs status¶
Summarize sync state as a count per state (locally-indexed, local-only, …).
Without --remote nothing is checked on Drive: every count compares the working
tree against this machine’s index (what protonfs last recorded). locally-indexed
therefore means “matches the index”, not “verified on Drive”; --remote walks
Drive and classifies against it too.
Accepts any number of PATHs (e.g. from a shell glob); counts are combined. Exit code, so an unattended caller can branch without parsing the counts: 0 = clean (every file locally-indexed, metadata-only or an LFS pointer stub), 1 = drift present (something to push/pull/prune), 2 = conflict present (conflict: a local change with no remote view to attribute it; both-modified: changed on both sides). Conflict outranks drift when both are present.
Usage
protonfs status [OPTIONS] [PATH]...
Options
- --format <fmt>¶
Output format: the classic state-per-line counts, or one JSON object.
- Default:
'plain'- Options:
plain | json
- --remote¶
Walk Drive and classify against it as well as the local index (slower, but catches remote changes that refresh hasn’t seen yet). Without it nothing is checked on Drive: ‘locally-indexed’ means the file matches this machine’s index.
Arguments
- PATH¶
Optional argument(s)
Changed in version 1.1.0: Added --format (plain/json) and multiple PATH pathspecs.
Changed in version 2.0.0: synced renamed to locally-indexed; a file deleted locally is
local-deleted rather than remote-only when Drive is not checked; added
--remote; the JSON output gained a remote field.
Scans the local tree (optionally scoped to PATH), compares it against the
local index, and prints a count per sync state (locally-indexed,
local-only, remote-only, metadata-only, conflict,
local-modified, remote-modified, both-modified, local-deleted,
remote-changed, remote-deleted, lfs-pointer).
By default it does not talk to Drive at all, so every count describes the local
files against the index: locally-indexed means “matches what this machine last
recorded”, not “verified on Drive”. --remote walks Drive and classifies against
it as well — slower, and a failed or throttled walk is an error rather than a
silent fall-back to the index-only answer. --format json includes
"remote": true|false so a consumer can tell which answer it got.
Exit code: 0 clean, 1 drift present, 2 conflict present (conflict
outranks drift) — identical in both formats. See Stability Promise (M4.1) for the
exact mapping and Durability and Drift-Resolution Guarantees for how states are classified.
Examples:
protonfs status
protonfs status subdir/
protonfs status; echo "exit=$?"
protonfs status --format json | jq .counts
protonfs status --remote # classify against a live Drive walk
ls¶
protonfs ls¶
List tracked files with their sync state (any number of PATHs).
–dirs summarizes each immediate subdirectory (counts by state, cumulative local/indexed sizes) instead of listing thousands of files; –state filters to the states you care about; –format plain|json makes the output scriptable; –visual treemap|waffle draws a storage-usage chart of those directories.
Usage
protonfs ls [OPTIONS] [PATH]...
Options
- --remote¶
Force a live Drive listing to compute state instead of relying on the local index alone (slower, but catches remote changes that refresh hasn’t seen yet).
- --trash¶
List /trash instead of the working tree (name and type only; no sync-state column, since trashed items aren’t tracked in the index).
- --dirs¶
Aggregate per immediate subdirectory: file counts by state plus cumulative local/indexed sizes, instead of listing every file.
- --state <states>¶
Only show files in this sync state (repeatable).
- Options:
locally-indexed | local-only | remote-only | metadata-only | conflict | local-modified | remote-modified | both-modified | local-deleted | remote-changed | remote-deleted | lfs-pointer
- --format <fmt>¶
Output format: rich table, tab-separated lines, or JSON.
- Default:
'table'- Options:
table | plain | json
- --visual <visual>¶
Draw a per-directory storage-usage chart (by each dir’s apparent footprint) instead of the listing. Implies directory aggregation; terminal-only (not for –format/–trash).
- Options:
treemap | waffle
Arguments
- PATH¶
Optional argument(s)
Lists tracked files with their sync state, as a table of path / state.
Added in version 1.1.0: --dirs per-directory aggregation with sizes, the --state filter, and
--format on ls/status.
Changed in version 1.2.0: --visual treemap/waffle storage charts.
--dirs aggregates per immediate subdirectory instead of listing every file:
one row per directory with its file count, cumulative local size (bytes on
disk; 0 for fully offloaded dirs), cumulative indexed size (what the index
records — the remote-side size), and a per-state count summary. This is the
storage-breakdown view: protonfs ls --dirs on a large tree answers “which
directories are taking space locally vs on Drive” without printing 10,000
metadata-only lines. --state STATE applies before --dirs aggregation, so
the two compose. --state synced is still accepted as a deprecated alias of
locally-indexed (with a warning on stderr) and will be removed in the next
major release. remote-only is only ever reported with --remote; without it
a file deleted locally is local-deleted. The --dirs JSON/columns also carry an apparent_bytes
field: each directory’s true footprint, taking per file whichever of the
local/indexed size is known (they agree when locally-indexed, local for a not-yet-pushed
file, indexed for an offloaded one). --visual {treemap,waffle} draws a
per-directory storage-usage chart instead of the listing, sized by that
apparent_bytes footprint so a fresh local-only tree and a fully-offloaded tree
both chart correctly. treemap gives nested rectangles whose areas are
proportional to size (squarified for readability); waffle gives a
proportional grid of cells. Both print a colour legend with each directory’s size
and percentage. This is a terminal-only view — it cannot be combined with
--format plain/json or --trash (both raise a usage error).
Examples:
protonfs ls
protonfs ls --remote subdir/
protonfs ls --trash
protonfs ls --dirs # per-directory storage breakdown
protonfs ls --remote --state remote-only --format plain | cut -f1
protonfs ls sim/ --dirs --format json # scriptable per-dir sizes/counts
protonfs ls --visual treemap # squarified storage treemap
protonfs ls sim/ --visual waffle # proportional waffle chart of sim/
push¶
protonfs push¶
Upload local-only/changed files to Drive (any number of PATHs).
Each PATH may be a directory, a single file, or a glob pattern. A quoted pattern
('mload*', 'mload*/*.ev') is expanded by protonfs itself against the local
tree at run time; an unquoted one is expanded by the shell first, as before. A PATH
that does not exist locally is a usage error (exit 2): push uploads local files, so a
missing path can only be a typo – this is distinct from the exit 1 used for transfer
failures.
A pattern matching nothing is reported and skipped (it never widens to the whole
repo); pass --strict to make that an error instead.
Changed in version 1.5.2: PATH may now name a single file (previously only directories were scanned, so a
file/glob pathspec silently uploaded nothing). A nonexistent PATH is now a usage
error instead of a silent no-op, and an empty push prints nothing to push.
Changed in version 1.11.0: A quoted PATH glob pattern is now expanded by protonfs against the local tree,
rather than depending on the shell to expand it first (#131) – so a scheduled job
can carry a pattern that keeps matching as new directories appear. Added
--strict.
Usage
protonfs push [OPTIONS] [PATH]...
Options
- --resolve <resolve>¶
How to reconcile a file that changed on BOTH sides since the last sync: remote=keep the remote copy (skip the upload), local=replace the remote with your local copy (the existing remote file is moved to the trash), both=upload your local copy alongside the remote. A file changed only locally needs none of these: it is uploaded as a new revision of the remote file. The proton-drive strategy names merge|keep-both|replace|skip are also accepted (replace=local, skip=remote, keep-both=both; merge adds a revision regardless of what the remote holds).
- Options:
remote | local | both | merge | keep-both | replace | skip
- --dry-run¶
Report what would be pushed without transferring anything.
- --strict¶
Fail (exit 1) if a PATH pattern matches nothing, instead of skipping it.
Arguments
- PATH¶
Optional argument(s)
Changed in version 1.1.0: Interactive batch progress on stderr; accepts multiple PATH pathspecs.
Changed in version 1.11.0: A quoted PATH glob pattern is expanded by protonfs itself against the local
tree, instead of relying on the shell to expand it first. Added --strict.
Uploads local-only and locally-modified files under PATH (or the whole repo)
to Drive. When run interactively (stderr is a terminal), a running
push: N/M file(s) progress line is shown on stderr after each uploaded batch;
scripts and redirected output see only the frozen summary on stdout.
PATH may be a directory, a single file, or a glob pattern. An unquoted
pattern is expanded by your shell before protonfs sees it, as always; a quoted
one ('mload*', 'mload*/*.ev') reaches protonfs intact and is expanded by
protonfs against the local tree at run time. The two are equivalent
interactively, but only the quoted form survives into a scheduled job — see
schedule — where the pattern must be re-expanded on each run so it
keeps matching as new directories appear. A pattern segment never crosses /,
matching shell semantics.
A pattern that matches nothing is reported and skipped, and never widens to the whole repo:
pattern(s) matched nothing, skipped (nothing to push): 'mload*' -- pass --strict to make this an error
Pass --strict to make that an error (exit 1) instead. --strict fails
before anything is transferred, so a run is all-or-nothing rather than partial.
Without --resolve, a genuine remote conflict is reported as a named
per-file failure rather than silently resolved or skipped. A file that changed
only locally since this machine last pushed it is not a conflict: it is uploaded
as a new revision of the existing Drive file, so Drive’s version history keeps
the earlier copy. --resolve local (replace) instead moves the existing
remote file to the trash and uploads a new one in its place. Every batch is
re-verified against a live remote listing after upload (matching each file’s
plaintext claimedSize) before it is recorded in the index — proton-drive can
report a transfer as successful when it did not actually land; an unverified file
is left unindexed and retried on the next push instead of being recorded as delivered.
A file that is an un-smudged git-LFS pointer stub is never pushed, even if
misclassified upstream, because that would overwrite real Drive content with a
131-byte placeholder. See Durability and Drift-Resolution Guarantees for the full mechanism.
Progress is saved after each directory group, so an interrupted push resumes rather than restarting.
Examples:
protonfs push # everything in scope that is new/changed
protonfs push subdir/ --resolve replace
protonfs push --dry-run
pull¶
protonfs pull¶
Download remote-only/changed files from Drive (any number of PATHs).
Each PATH may be a directory, a single file, or a glob pattern. A quoted pattern
('mload*', 'mload*/*.ev') is expanded by protonfs itself, against the paths
the INDEX knows about – not the local filesystem, because pull’s whole purpose is
fetching files that are absent locally (an offloaded file has no local presence to
glob). Run refresh first on a repo whose index has not seen those files yet.
A pattern matching nothing is reported and skipped (it never widens to the whole
repo); pass --strict to make that an error instead.
Diverged files (edited locally AND changed on the remote since the last sync) are left untouched unless you pass –resolve; they are reported and pull exits non-zero.
Changed in version 1.11.0: A quoted PATH glob pattern is now expanded by protonfs against the index, rather
than depending on the shell to expand it first (#131) – so a scheduled job can
carry a pattern that keeps matching as new runs appear, and so a pattern can name
offloaded files that no filesystem glob could find. Added --strict.
Changed in version 2.1.0: With an empty index, the index is seeded from the remote manifest when the root has one, instead of stopping with “run refresh first” (#146).
Usage
protonfs pull [OPTIONS] [PATH]...
Options
- --resolve <resolve>¶
How to reconcile a file that changed on BOTH sides since the last sync: remote=overwrite local, local=keep local (stays queued for push), both=fetch the remote copy under a .remote suffix for a manual merge. replace is an alias for remote (replace the local copy). Without this, pull leaves diverged files untouched and reports them.
- Options:
remote | local | both | replace
- --dry-run¶
Preview transfers and unresolved conflicts without downloading anything.
- --refresh¶
Discover remote files (seed the index) before pulling.
- --strict¶
Fail (exit 1) if a PATH pattern matches nothing, instead of skipping it.
Arguments
- PATH¶
Optional argument(s)
Changed in version 1.1.0: Interactive batch progress on stderr; accepts multiple PATH pathspecs.
Changed in version 1.11.0: A quoted PATH glob pattern is expanded by protonfs itself against the
index, instead of relying on the shell to expand it first. Added --strict.
Downloads remote-only and (with --resolve) remote-modified files under
PATH (or the whole repo). When run interactively (stderr is a terminal), a
running pull: N/M file(s) progress line is shown on stderr after each
transferred batch; scripts and redirected output see only the frozen summary on
stdout.
PATH accepts a glob pattern on the same terms as push,
with one important difference: pull expands a pattern against the paths the
index knows about, not the local filesystem. That is deliberate — pull exists
to fetch files that are absent locally, and an offloaded file has no local
presence for a filesystem glob to find, so matching on disk would silently miss
exactly the files you asked for. The practical consequence is that a pattern only
matches what the index has already seen: run refresh first on a repo
whose index does not yet know about those files (pull --refresh 'mload*' on a
brand-new repo expands the pattern before the refresh seeds the index, so it
matches nothing on that first run).
A pattern that matches nothing is reported and skipped, never widening to the
whole repo; --strict makes it an error (exit 1) instead, failing before
anything is transferred.
This is what makes the small-files case schedulable: 'mload*/*.ev' pulls a
few megabytes of time-series data across every matching run, while leaving the
much larger dumps in those same directories offloaded.
A file edited locally and changed on the
remote since the last sync (a divergence) is left untouched by a bare
pull — it is reported and the command exits non-zero, so a local edit is
never silently overwritten. Choose a side with --resolve:
remote— overwrite the local copy with the remote one.local— keep the local copy; it stays queued for the nextpush.both— fetch the remote copy alongside the local one under a.remotesuffix (untracked) for a manual merge.
--refresh seeds the index from a fresh remote listing before pulling (metadata
only, no download) — useful on a machine whose index doesn’t yet know everything
already on Drive. Without an index yet, plain pull refuses to run and tells
you to run refresh first (or pass --refresh).
Examples:
protonfs refresh && protonfs pull # typical first pull on a new machine
protonfs pull --refresh # equivalent, one command
protonfs pull path/to/file --resolve remote
protonfs pull --resolve both # fetch remote copies as *.remote for merging
offload¶
protonfs offload¶
Delete local bytes of protonfs-tracked files confirmed present on Drive.
Accepts any number of PATHs (e.g. from a shell glob). The inverse of pull: reclaims local disk space while leaving the Drive copy intact. Reversible – a later pull restores the file in full. By default every file is re-verified against a live remote listing (not just the index) before its local copy is deleted; pass –no-verify to skip that check.
Usage
protonfs offload [OPTIONS] [PATH]...
Options
- --no-verify¶
Skip re-verifying files against the remote before deleting local bytes (unsafe).
- --dry-run¶
Preview what would be offloaded; delete nothing.
- --yes¶
Skip confirmation prompt.
Arguments
- PATH¶
Optional argument(s)
Deletes the local bytes of protonfs-tracked files already confirmed present on
Drive, reclaiming disk space while leaving the index entry as
local_state=metadata-only — a later pull restores the file in full. This
is the inverse of pull; it never touches the remote copy.
Before deleting anything, every candidate is (a) checked for unsynced local
edits — a file whose live content hash differs from what the index last recorded
is never offloaded, verify or not, since offloading it would destroy the only
copy of that edit — and (b) by default, re-verified against a live remote
listing (not just the index), requiring the remote’s plaintext claimedSize to
match the local file’s byte size. A file that fails either check is left alone
and reported (skipped_modified / skipped_unverified); this is not treated
as command failure. --no-verify skips the live remote re-verification (the
unsynced-edit guard in (a) always still applies) — unsafe if the remote could
have changed since the index was last updated.
Examples:
protonfs offload subdir/ # prompts for confirmation
protonfs offload --dry-run
protonfs offload --yes --no-verify # unsafe: trust the index alone
rm¶
protonfs rm¶
Remove files/directories from Drive (trash by default, -f for permanent).
Usage
protonfs rm [OPTIONS] PATH...
Options
- -r, --recursive¶
- -f, --force¶
Permanently delete (trash, then delete).
- --yes¶
Skip confirmation prompt.
Arguments
- PATH¶
Required argument(s)
Trashes PATH on Drive (reversible via restore). Requires -r/
--recursive for a directory. The command removes the matching index entries
locally regardless of whether the permanent-delete step below runs.
-f/--force additionally attempts to permanently delete the trashed
node after trashing it. proton-drive addresses a trashed node only by
/trash/<basename> (no working UID addressing), so when two or more trashed
items share a basename, protonfs cannot safely tell which is yours: it leaves
the item trashed (still reversible) and reports the ambiguity rather than
guessing. See Durability and Drift-Resolution Guarantees for the exact boundary.
Examples:
protonfs rm old-dump.ev
protonfs rm -r stale-dir/ --yes
protonfs rm -f duplicate.tmp # trash, then attempt permanent delete
restore¶
protonfs restore¶
Restore trashed files/directories on Drive.
Usage
protonfs restore [OPTIONS] PATH...
Arguments
- PATH¶
Required argument(s)
Restores a previously trashed file/directory on Drive by its original path.
On proton-drive versions that reject original-path restore (0.5.0+), protonfs
falls back to resolving the trashed entry by name — and refuses to act (raising
an error naming the ambiguity) rather than guess when more than one trashed item
shares that name under a different original parent. See the boundary spelled out
in Durability and Drift-Resolution Guarantees and in DriveClient.restore’s docstring
(src/protonfs/drive.py).
Examples:
protonfs restore old-dump.ev
protonfs restore stale-dir/
refresh¶
protonfs refresh¶
Discover remote files and seed the local index (any number of PATHs).
Usage
protonfs refresh [OPTIONS] [PATH]...
Options
- --prune¶
Drop index entries for files deleted on the remote.
Arguments
- PATH¶
Optional argument(s)
Walks Drive under the configured remote_root (or PATH within it) and seeds
the local index with metadata-only entries for anything found there that this
machine’s index doesn’t already know about. This is the cross-client primitive: a
fresh machine (or one that missed files another client pushed) becomes aware of
everything already on Drive without downloading any content, so it will not
re-upload what’s already there. It also reports files that changed or were
deleted on the remote since they were last seen.
The walk is resumable: progress (both the seeded entries and the walk’s own frontier) is saved incrementally, so a run interrupted by an API throttle picks up where it left off on the next invocation instead of restarting from the root. Change/deletion detection only runs after a complete pass, though — a resumed partial pass seeds but does not yet report changes/deletions.
Examples:
protonfs refresh # seed everything not yet known
protonfs refresh subdir/ --prune # scope to a subtree, drop remote-deleted entries
Changed in version 2.1.0: When the repo maintains a remote manifest (defaults.manifest), a
complete whole-root pass records the manifest generation it reconciled against.
Nothing under the remote .protonfs/ directory is ever seeded.
verify¶
protonfs verify¶
Check the remote manifest against a full listing of the remote.
The manifest (.protonfs/manifest.json under the remote root) records every file protonfs verified on Drive, so a host can read what is there without walking the tree. It is a cache: this command walks the whole remote and reports entries Drive contradicts (missing, or a different size/sha1) and files the manifest does not list. –repair rewrites the manifest from that listing, and is how one is first created.
Exit code: 0 when every manifest entry matches Drive (or there is no manifest, or –repair rewrote it); 1 when entries are missing or differ, or on a Drive error.
Usage
protonfs verify [OPTIONS]
Options
- --repair¶
Rewrite the remote manifest to match a full listing of the remote (creating it if the root has none).
Added in version 2.1.0.
The remote manifest is one JSON object per synced root, at
<remote_root>/.protonfs/manifest.json. It records every file protonfs uploaded
(or adopted) and verified on Drive: plaintext size, sha256, sha1, and the uid of the
Drive revision that was verified. A generation counter goes up on every write,
and each write is uploaded as a new revision of the manifest itself, so Drive’s
version history keeps every earlier one.
It is a cache, not an authority:
Entries are written only after the upload they describe was verified, so the manifest can lag Drive but never runs ahead of it. A crash, or a host running an older protonfs, leaves it behind, never ahead.
It is only ever created by
verify --repairfrom a full listing, so it starts complete.pushandrmkeep an existing manifest current whendefaults.manifestis on. They never start one mid-history, because such a manifest would look complete while missing everything uploaded before it.Nothing destructive trusts it.
offloadalways verifies against a live listing and never reads the manifest.Changes made outside protonfs (the web UI, another client) are invisible to it until something walks the remote.
verifyis that walk.
verify reports:
entries the manifest lists that Drive lacks, or holds at a different size or sha1 (faults: exit
1);files on Drive the manifest does not list (tolerated, since the manifest may lag);
entries whose content matches but whose Drive revision has moved on;
files Drive listed without a plaintext size, which cannot be compared.
It also says which manifest generation this machine’s index was last reconciled
with. --repair rewrites the manifest to match the listing. It keeps a sha256
only where this machine’s index, or the manifest itself, recorded one for exactly
that content; otherwise the sha256 is left unknown rather than guessed. It leaves
out any file Drive lists without a plaintext size.
Readers: pull on an empty index (a fresh clone) seeds the index from the
manifest instead of stopping with “run refresh first”, and says that files the
manifest does not list are not included. When the repo maintains a manifest,
pull also notes when the manifest has changed since this index was
reconciled, so another host has pushed files this index may not list yet.
Enabling it on an existing repo:
protonfs verify --repair # build it from a full listing
protonfs config set defaults.manifest true # keep it current on push/rm
git add .protonfs/config.json && git commit -m "maintain the protonfs manifest"
protonfs refresh # on each host: record its generation
Set PROTONFS_NO_MANIFEST on a host to switch every manifest read and write
off there, whatever the repo config says.
install-drive¶
protonfs install-drive¶
Download and verify the official proton-drive CLI binary.
Usage
protonfs install-drive [OPTIONS]
Options
- --version <version>¶
proton-drive version to install (default: pinned).
- --skip-keyring¶
Do not prepare the OS keyring after installing.
Downloads the official proton-drive CLI binary for the current platform
(linux-x64 requires AVX2, linux-arm64, macOS x64/arm64), verifies its SHA-512
against a pinned checksum before installing it (never installs an unverified
binary), and by default also prepares the OS keyring proton-drive will use to
store its session — done here, not at first login, so a keyring failure surfaces
before a browser sign-in is thrown away.
Examples:
protonfs install-drive
protonfs install-drive --version 0.5.0
protonfs install-drive --skip-keyring
upgrade¶
protonfs upgrade¶
Upgrade proton-drive to the highest supported version + migrate repo state.
A protonfs release only ever upgrades proton-drive to its own highest supported version; a newer upstream release requires a newer protonfs (reported, never installed). Inside a protonfs root, pending repo-state migrations run too.
Usage
protonfs upgrade [OPTIONS]
Options
- --check¶
Report what would happen; change nothing. Exit 0 if fully current, 1 if an upgrade or migration is available.
- --drive-only¶
Only the proton-drive binary; skip migrations.
- --repo-only¶
Only repo-state migrations; skip the binary.
Upgrades the installed proton-drive binary to the highest version this
protonfs release supports (SHA-512-verified before an atomic swap; a newer
upstream release is reported but never installed), verifies the session survived
the swap, and – inside a protonfs root – runs any pending repo-state
migrations. See Upgrading for the full upgrade story.
Examples:
protonfs upgrade --check # what would happen?
protonfs upgrade # binary + migrations
protonfs upgrade --repo-only # just bring .protonfs/ current
doctor¶
protonfs doctor¶
Check this host can run proton-drive (binary, session bus, OS keyring).
Usage
protonfs doctor [OPTIONS]
Options
- --fix¶
Repair what protonfs can (bootstrap the keyring).
Checks that this host can actually run proton-drive: the binary is present
and runnable, and on Linux, that a D-Bus session bus and a usable (unlocked)
Secret Service keyring are reachable. Written for headless hosts (SSH, no
desktop), where a graphical login’s sealed login.keyring is the most common
silent failure.
Examples:
protonfs doctor
protonfs doctor --fix
shell-init¶
protonfs shell-init¶
Print shell exports so proton-drive run by hand sees the same keyring.
Usage
protonfs shell-init [OPTIONS]
Prints export VAR=value lines so that running the proton-drive binary by
hand (outside of protonfs) sees the same session bus/keyring environment
protonfs sets up for itself. Every protonfs command does this internally;
this is only needed for manual proton-drive invocations.
Example:
eval "$(protonfs shell-init)"
proton-drive filesystem list /my-files
completions¶
protonfs completions¶
Print or install shell completion (bash|zsh|fish).
Command names, per-subcommand options, and the global flags (in any position) all complete.
Usage
protonfs completions [OPTIONS] {bash|zsh|fish}
Options
- --install¶
Install the completion script (idempotent).
- --uninstall¶
Remove the installed completion script.
Arguments
- SHELL¶
Required argument
Prints, installs, or removes shell completion for bash, zsh, or fish. With
--install it writes the generated script and wires it into your shell config
(idempotent, marker-delimited); --uninstall removes it. Installed completions are
refreshed automatically by protonfs upgrade. Global flags
complete after a subcommand too, matching the position-independent argv handling.
Added in version 1.5.0.
Examples:
protonfs completions bash # print the script to stdout
protonfs completions zsh --install # install + wire into ~/.zshrc
protonfs completions fish --uninstall
schedule¶
protonfs schedule¶
Manage scheduled push/pull cron jobs for this repo.
Bare protonfs schedule lists the jobs (it never installs implicitly). --add
installs a job (needs a cadence: --every/--cron/--at) and prints its id;
--uninstall <id> (or -U, accepting the id or a --list index) removes one,
--uninstall --all removes them all. Each job runs under flock with an absolute
proton-drive path and tuned timeouts, logging to .protonfs/schedule/<id>.log.
--path takes a subtree or a glob pattern. A pattern is stored (and passed to
push/pull) unexpanded, so protonfs re-expands it on every run – one job can cover a
whole family of runs and keeps matching as new ones appear:
protonfs schedule --add --every daily --command pull --path 'mload*/*.ev'
Added in version 1.8.0.
Changed in version 1.11.0: --path accepts a glob pattern, re-expanded at run time (#131). Added
--strict.
Usage
protonfs schedule [OPTIONS]
Options
- --list¶
List this machine’s scheduled jobs.
- --add¶
Install a new job from the options below.
- -U, --uninstall <ID>¶
Remove the job with this id (or –list index). Use –all to remove every job.
- --all¶
Remove every scheduled job.
- --every <SPEC>¶
Cadence: hourly | daily | weekly | <N>h | <N>m.
- --cron <EXPR>¶
Raw 5-field cron expression.
- --at <HOURS>¶
Run daily at these hours (0-23, comma-separated).
- --command <command>¶
What the job runs (sync = pull then push).
- Default:
'push'- Options:
push | pull | sync
- --path <PATHSPEC>¶
Scope the job to a subtree, or to a quoted glob pattern that protonfs re-expands on every run (see the examples below).
- --resolve <sched_resolve>¶
Conflict strategy passed to push/pull.
- --strict¶
Pass –strict to push/pull, so a run whose –path pattern matches nothing fails.
- --label <label>¶
Human label shown in –list.
- --json¶
With –list, emit JSON.
Installs, lists, and removes cron jobs that run push/
pull on a schedule. Bare protonfs schedule lists this machine’s
jobs (it never installs implicitly); --add with a cadence
(--every hourly|daily|weekly|<N>h|<N>m, or a raw --cron expression, or
--at hours) installs one and prints a short id; --uninstall <id> (-U, also
accepting a --list index) removes it, and --all removes them all.
Each job runs a generated wrapper under flock (no overlapping runs), with an
absolute proton-drive path (cron has no useful PATH) and tuned list/transfer
timeouts, logging to .protonfs/schedule/<id>.log. Jobs are recorded per-device in
.protonfs/schedule.local.json (gitignored). --command sync runs pull then push.
Added in version 1.8.0.
Changed in version 1.11.0: --path accepts a glob pattern, re-expanded on every run. Added --strict.
Scoping a job with a pattern¶
--path takes a subtree or a glob pattern. The pattern is stored unexpanded and
written into the wrapper quoted, so cron’s shell leaves it alone and protonfs expands
it fresh on every run. That is the difference that matters for scheduling: a shell
glob is expanded once, when you install the job, and is frozen from then on — a
pattern keeps matching as new runs appear, with no need to reinstall the job.
This makes two otherwise inexpressible setups routine — pulling one family of runs while leaving the rest deliberately offloaded, and pulling only the small time-series files out of directories whose dumps are far too large to want locally:
protonfs schedule --add --every daily --command pull --path 'mload*'
protonfs schedule --add --every daily --command pull --path 'mload*/*.ev'
Note the quotes: without them your shell expands the pattern before protonfs sees it, and you get a job frozen to whatever matched at install time (the older behaviour).
By default a run whose pattern matches nothing is reported in the job log and treated
as a no-op, which is usually what you want — a pattern for runs that do not exist
yet should not fail nightly. Pass --strict when a job’s pattern is expected to
always match something, and a run that matches nothing should fail loudly instead:
$ protonfs schedule --add --every daily --command pull --path 'mload*' --strict
Examples:
protonfs schedule --add --every daily --at 1,3,5 # nightly at 01/03/05h
protonfs schedule --add --cron "0 */6 * * *" --command sync
protonfs schedule --add --every daily --command pull --path 'mload*/*.ev'
protonfs schedule --list
protonfs schedule --uninstall a1d3ae
protonfs schedule --all # remove every job
auth¶
protonfs auth¶
Authenticate the proton-drive CLI: login | logout | status.
Usage
protonfs auth [OPTIONS] {login|logout|status}
Arguments
- ACTION¶
Required argument
login/logout— passthrough toproton-drive auth <action>with inherited stdio, so an interactive login URL/prompt reaches your terminal directly. Exit code is whateverproton-drivereturns.status— checks for a valid session directly (without invokingproton-drive), printingauthenticatedor a reminder to log in. Exit0if authenticated,1otherwise.
Examples:
protonfs auth login
protonfs auth status
protonfs auth logout
trash¶
protonfs trash¶
Inspect and empty Proton Drive’s trash (#70).
Usage
protonfs trash [OPTIONS] COMMAND [ARGS]...
Commands
- empty
Permanently empty /trash for the whole…
- list
List /trash: name, original parent…
Groups the /trash inspection and account-wide emptying operations.
trash list¶
protonfs trash list¶
List /trash: name, original parent (best-effort), same-name duplicate count.
A nonzero duplicate count is the ambiguity restore refuses to resolve on its own (#56): proton-drive resolves /trash paths by name, first match wins.
Usage
protonfs trash list [OPTIONS]
Lists every item currently in /trash: its name, its original parent (resolved
on a best-effort basis — shown as ? when proton-drive can’t resolve it), and
how many other trashed items share the same name. A nonzero duplicate count is
exactly the ambiguity restore can refuse to resolve on its own (#56): proton-
drive resolves /trash paths by name, first match wins, so same-named entries
can silently block a restore or shadow one another.
Examples:
protonfs trash list
trash empty¶
protonfs trash empty¶
Permanently empty /trash for the whole account (irreversible, account-global).
Requires typing an exact confirmation phrase unless –yes is passed. This is NOT scoped to this repo’s remote_root – it empties every trashed item on the account.
Usage
protonfs trash empty [OPTIONS]
Options
- --yes¶
Skip the typed confirmation prompt.
Permanently empties /trash for the whole Proton Drive account by calling
proton-drive filesystem empty-trash. This is irreversible and not
scoped to this repo’s remote_root — it deletes every trashed item on the
account, including ones unrelated to this repo. Without --yes, the command
prints that warning and requires typing an exact confirmation phrase; anything
else aborts without emptying trash.
Deliberately out of scope: permanently deleting a single trashed item by UID.
proton-drive does not accept node UIDs for /trash paths (see rm’s
duplicate-basename limitation above and #56’s analysis), so there is no safe way
to target one item there — use trash list to find and resolve duplicates via
the Drive web UI, or trash empty to clear everything.
Examples:
protonfs trash list # see what's there and any duplicates
protonfs trash empty # prompts for typed confirmation
protonfs trash empty --yes # scripts / non-interactive use
config¶
protonfs config¶
Get/set protonfs config (#21): env > local > shared > global > built-in default.
Usage
protonfs config [OPTIONS] COMMAND [ARGS]...
Commands
- get
Print the RESOLVED value of KEY (e.g.
- set
Set KEY = VALUE.
Reads or writes protonfs’s layered configuration. Known keys:
remote_root, device_id, defaults.on_conflict,
defaults.low_io, defaults.event_log,
defaults.progress_style, defaults.manifest (each defined in the
Configuration section above).
config get¶
protonfs config get¶
Print the RESOLVED value of KEY (e.g. remote_root, defaults.low_io).
Usage
protonfs config get [OPTIONS] KEY
Arguments
- KEY¶
Required argument
Always prints the fully resolved value across every layer (env var > per-device local config > shared per-repo config > global user config > built-in default) — see Stability Promise (M4.1) for the complete precedence list and the environment variables that can override each key.
Examples:
protonfs config get remote_root
config set¶
protonfs config set¶
Set KEY = VALUE. Default scope is the shared per-repo config (committed).
Usage
protonfs config set [OPTIONS] KEY VALUE
Options
- --global¶
Write to the global user config.
- --local¶
Write to the per-device local config.
Arguments
- KEY¶
Required argument
- VALUE¶
Required argument
Writes to exactly one layer:
default (no flag) — the shared per-repo file,
.protonfs/config.json(the file you commit, so every clone syncs to the same place).--local— the per-device file,.protonfs/config.local.json(gitignored).--global— the user-wide file,~/.config/protonfs/config.json(orPROTONFS_CONFIG).
--global and --local are mutually exclusive.
Examples:
protonfs config set defaults.low_io true --local
protonfs config set remote_root /my-files/sim-data --global
See also¶
The Configuration section above for every config key (
remote_root…) and environment variable (PROTONFS_CONFIG…) as cross-referenceable definitions.Stability Promise (M4.1) for the frozen exit-code/option contract.
Durability and Drift-Resolution Guarantees for durability and drift-resolution guarantees.
Getting Started and Syncing across machines for workflow-oriented walkthroughs.