Command Reference ================== .. Page contents are rendered by furo's right-hand sidebar; an explicit .. `.. contents::` directive collides with it (furo TOC JS error), so it is omitted. 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 :doc:`../stability`. Synopsis -------- .. click:: protonfs.cli:main :prog: protonfs :nested: none ``protonfs`` is the command group; every operation is a subcommand documented under :ref:`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 :doc:`../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: Diagnostics & verbosity ------------------------ .. versionadded:: 1.3.0 Layered ``-v`` verbosity, the progress-style flag, and the rotating event log. .. versionchanged:: 1.4.0 Global options became position-independent — they may appear before or after the subcommand (see :func:`~protonfs.argv.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 :doc:`../stability` 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. .. list-table:: :header-rows: 1 :widths: 12 88 * - Level - Console detail * - (none) - Warnings/errors only, plus each command's normal stdout result summary. * - ``-v`` - Adds curated phase narration, progress updates throttled to roughly every 30s, and phase durations. * - ``-vv`` - Adds per-item paths (each file transferred/affected) and sub-steps; progress throttled to roughly every 5s. * - ``-vvv`` - Adds ``INFO``-level diagnostics; progress throttled to roughly every 1s. * - ``-vvvv`` - ``DEBUG`` from every ``protonfs`` module, the ``proton-drive`` subprocess's argv and stderr, and third-party library logging; progress updates continuously (no throttle). 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 :confval:`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 :confval:`defaults.event_log` config key, else off. Configuration ============= Environment variables ---------------------- .. envvar:: PROTONFS_CONFIG Overrides the global config file path outright. .. versionadded:: 1.0.0 .. envvar:: PROTONFS_REMOTE_ROOT Per-key override for :confval:`remote_root`. .. versionadded:: 1.0.0 .. envvar:: PROTONFS_DEVICE_ID Per-key override for :confval:`device_id`. .. versionadded:: 1.0.0 .. envvar:: PROTONFS_ON_CONFLICT Per-key override for :confval:`defaults.on_conflict`. .. versionadded:: 1.0.0 .. envvar:: PROTONFS_LOW_IO Per-key override for :confval:`defaults.low_io`. .. versionadded:: 1.0.0 .. envvar:: PROTONFS_EVENT_LOG Per-key override for :confval:`defaults.event_log`. .. versionadded:: 1.3.0 .. envvar:: PROTONFS_PROGRESS_STYLE Per-key override for :confval:`defaults.progress_style`. .. versionadded:: 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 :doc:`../stability`). .. envvar:: PROTONFS_DRIVE_BIN Path/name of the ``proton-drive`` binary to invoke, in place of the default. .. versionadded:: 1.0.0 .. envvar:: PROTONFS_DRIVE_VERSION Overrides the ``proton-drive`` version ``install-drive`` installs when ``--version`` is not passed. .. versionadded:: 1.0.0 .. envvar:: PROTONFS_DRIVE_SHA512 Explicit SHA-512 to verify a ``proton-drive`` download against, required for versions/platforms without a built-in checksum pin. .. versionadded:: 1.0.0 .. envvar:: PROTONFS_LIST_TIMEOUT Timeout in seconds for a Drive listing call (default ``45``). .. versionadded:: 1.0.0 .. envvar:: PROTONFS_LIST_RETRIES Max retries for a Drive listing call (default ``4``). .. versionadded:: 1.0.0 .. envvar:: PROTONFS_LIST_BACKOFF Base backoff in seconds between Drive listing retries (default ``2``). .. versionadded:: 1.0.0 .. envvar:: PROTONFS_LIST_BACKOFF_CAP Cap in seconds on the Drive listing retry backoff (default ``60``). .. versionadded:: 1.0.0 .. envvar:: PROTONFS_TRANSFER_TIMEOUT Timeout in seconds for a Drive upload/download call (default ``300``). .. versionadded:: 1.0.0 .. envvar:: PROTONFS_TRANSFER_RETRIES Max retries for a Drive upload/download call (default ``4``). .. versionadded:: 1.0.0 .. envvar:: PROTONFS_TRANSFER_BACKOFF Base backoff in seconds between Drive upload/download retries (default ``2``). .. versionadded:: 1.0.0 .. envvar:: PROTONFS_TRANSFER_BACKOFF_CAP Cap in seconds on the Drive upload/download retry backoff (default ``60``). .. versionadded:: 1.0.0 .. envvar:: PROTONFS_KEYRING_PASSWORD Supplies the password for the protonfs-owned keyring bootstrap, instead of generating one. .. versionadded:: 1.0.0 .. envvar:: PROTONFS_MANIFEST Per-key override for :confval:`defaults.manifest`. .. versionadded:: 2.1.0 .. envvar:: PROTONFS_NO_MANIFEST Set (to any truthy value) to switch every remote-manifest read and write off on this host, whatever :confval:`defaults.manifest` says. .. versionadded:: 2.1.0 .. envvar:: 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. .. versionadded:: 1.0.0 .. envvar:: PROTONFS_CREDENTIALS_STORE Forces which proton-drive credentials store protonfs prepares and uses: ``keychain`` (freedesktop Secret Service) or ``pass`` (`password-store `_). The default, ``auto``, tries ``keychain`` first and falls back to a protonfs-managed ``pass`` store only when the Secret Service cannot be made ready on this host (see :ref:`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. Native ``PROTON_DRIVE_CREDENTIALS_STORE``, if already set in the environment, is passed straight through to ``proton-drive`` and takes precedence over this variable's resolution. .. versionadded:: 1.9.0 Configuration file and keys ----------------------------- .. confval:: remote_root :type: str Proton Drive path that maps to this repo's ProtonFS root. Overridable via :envvar:`PROTONFS_REMOTE_ROOT`. .. versionadded:: 1.0.0 .. confval:: device_id :type: str Stable identifier for this client device in the index. Overridable via :envvar:`PROTONFS_DEVICE_ID`. .. versionadded:: 1.0.0 .. confval:: defaults.on_conflict :type: str :default: "skip" Action when a file is in :class:`~protonfs.diff.SyncState` conflict. Overridable via :envvar:`PROTONFS_ON_CONFLICT`. .. versionadded:: 1.0.0 .. confval:: defaults.low_io :type: bool :default: false Skip hashing unchanged files. Overridable via :envvar:`PROTONFS_LOW_IO`. .. versionadded:: 1.0.0 .. confval:: defaults.event_log :type: bool :default: false Enable the structured rotating event log. Overridable via :envvar:`PROTONFS_EVENT_LOG`. .. versionadded:: 1.3.0 .. confval:: defaults.progress_style :type: str :default: "inline" Progress display style, ``inline`` or ``lines``. Overridable via :envvar:`PROTONFS_PROGRESS_STYLE`. .. versionadded:: 1.3.0 .. confval:: defaults.manifest :type: bool :default: false Keep the remote manifest (see :ref:`verify `) current on ``push`` and ``rm``. Only an existing manifest is updated; ``protonfs verify --repair`` creates one. Set it in the shared ``config.json`` so every host maintains it. Overridable via :envvar:`PROTONFS_MANIFEST`. .. versionadded:: 2.1.0 .. _reference-subcommands: Subcommands =========== .. _cmd-setup: setup ----- .. click:: protonfs.cli:setup :prog: protonfs setup 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 :confval:`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 .. _cmd-deinit: deinit ------ .. click:: protonfs.cli:deinit :prog: protonfs deinit The inverse of :ref:`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 .. _cmd-status: status ------ .. click:: protonfs.cli:status :prog: protonfs status .. versionchanged:: 1.1.0 Added ``--format`` (``plain``/``json``) and multiple ``PATH`` pathspecs. .. versionchanged:: 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 :doc:`../stability` for the exact mapping and :doc:`../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 .. _cmd-ls: ls -- .. click:: protonfs.cli:ls :prog: protonfs ls Lists tracked files with their sync state, as a table of ``path`` / ``state``. .. versionadded:: 1.1.0 ``--dirs`` per-directory aggregation with sizes, the ``--state`` filter, and ``--format`` on ``ls``/``status``. .. versionchanged:: 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/ .. _cmd-push: push ---- .. click:: protonfs.cli:push :prog: protonfs push .. versionchanged:: 1.1.0 Interactive batch progress on stderr; accepts multiple ``PATH`` pathspecs. .. versionchanged:: 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. .. _push-patterns: ``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 :ref:`cmd-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 :doc:`../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 .. _cmd-pull: pull ---- .. click:: protonfs.cli:pull :prog: protonfs pull .. versionchanged:: 1.1.0 Interactive batch progress on stderr; accepts multiple ``PATH`` pathspecs. .. versionchanged:: 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 :ref:`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 :ref:`cmd-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 next ``push``. - ``both`` — fetch the remote copy alongside the local one under a ``.remote`` suffix (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 .. _cmd-offload: offload ------- .. click:: protonfs.cli:offload :prog: protonfs offload 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 :ref:`pull ` restores the file in full. This is the inverse of :ref:`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 .. _cmd-rm: rm -- .. click:: protonfs.cli:rm :prog: protonfs rm Trashes ``PATH`` on Drive (reversible via :ref:`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/`` (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 :doc:`../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 .. _cmd-restore: restore ------- .. click:: protonfs.cli:restore :prog: protonfs restore 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 :doc:`../guarantees` and in ``DriveClient.restore``'s docstring (``src/protonfs/drive.py``). Examples:: protonfs restore old-dump.ev protonfs restore stale-dir/ .. _cmd-refresh: refresh ------- .. click:: protonfs.cli:refresh :prog: protonfs refresh Walks Drive under the configured :confval:`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 .. versionchanged:: 2.1.0 When the repo maintains a remote manifest (:confval:`defaults.manifest`), a complete whole-root pass records the manifest generation it reconciled against. Nothing under the remote ``.protonfs/`` directory is ever seeded. .. _cmd-verify: verify ------ .. click:: protonfs.cli:verify :prog: protonfs verify .. versionadded:: 2.1.0 The **remote manifest** is one JSON object per synced root, at ``/.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 --repair`` from a full listing, so it starts complete. ``push`` and ``rm`` keep an existing manifest current when :confval:`defaults.manifest` is on. They never start one mid-history, because such a manifest would look complete while missing everything uploaded before it. - Nothing destructive trusts it. ``offload`` always 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. ``verify`` is 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 :envvar:`PROTONFS_NO_MANIFEST` on a host to switch every manifest read and write off there, whatever the repo config says. .. _cmd-install-drive: install-drive ------------- .. click:: protonfs.cli:install_drive_cmd :prog: protonfs install-drive 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 .. _cmd-upgrade: upgrade ------- .. click:: protonfs.cli:upgrade :prog: protonfs upgrade 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 :doc:`../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 .. _cmd-doctor: doctor ------ .. click:: protonfs.cli:doctor :prog: protonfs doctor 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 .. _cmd-shell-init: shell-init ---------- .. click:: protonfs.cli:shell_init :prog: protonfs shell-init 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 .. _cmd-completions: completions ----------- .. click:: protonfs.cli:completions :prog: protonfs completions 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 :ref:`protonfs upgrade `. Global flags complete after a subcommand too, matching the position-independent argv handling. .. versionadded:: 1.5.0 Examples:: protonfs completions bash # print the script to stdout protonfs completions zsh --install # install + wire into ~/.zshrc protonfs completions fish --uninstall .. _cmd-schedule: schedule -------- .. click:: protonfs.cli:schedule :prog: protonfs schedule Installs, lists, and removes cron jobs that run :ref:`push `/ :ref:`pull ` on a schedule. Bare ``protonfs schedule`` lists this machine's jobs (it never installs implicitly); ``--add`` with a cadence (``--every hourly|daily|weekly|h|m``, or a raw ``--cron`` expression, or ``--at`` hours) installs one and prints a short id; ``--uninstall `` (``-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/.log``. Jobs are recorded per-device in ``.protonfs/schedule.local.json`` (gitignored). ``--command sync`` runs pull then push. .. versionadded:: 1.8.0 .. versionchanged:: 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: .. code-block:: console $ 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 .. _cmd-auth: auth ---- .. click:: protonfs.cli:auth :prog: protonfs auth - ``login`` / ``logout`` — passthrough to ``proton-drive auth `` with inherited stdio, so an interactive login URL/prompt reaches your terminal directly. Exit code is whatever ``proton-drive`` returns. - ``status`` — checks for a valid session directly (without invoking ``proton-drive``), printing ``authenticated`` or a reminder to log in. Exit ``0`` if authenticated, ``1`` otherwise. Examples:: protonfs auth login protonfs auth status protonfs auth logout .. _cmd-trash: trash ----- .. click:: protonfs.cli:trash :prog: protonfs trash Groups the ``/trash`` inspection and account-wide emptying operations. .. _cmd-trash-list: trash list ~~~~~~~~~~ .. click:: protonfs.cli:trash_list_cmd :prog: protonfs trash list 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 .. _cmd-trash-empty: trash empty ~~~~~~~~~~~ .. click:: protonfs.cli:trash_empty_cmd :prog: protonfs trash empty 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 :confval:`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 .. _cmd-config: config ------ .. click:: protonfs.cli:config :prog: protonfs config Reads or writes protonfs's layered configuration. Known keys: :confval:`remote_root`, :confval:`device_id`, :confval:`defaults.on_conflict`, :confval:`defaults.low_io`, :confval:`defaults.event_log`, :confval:`defaults.progress_style`, :confval:`defaults.manifest` (each defined in the `Configuration`_ section above). .. _cmd-config-get: config get ~~~~~~~~~~ .. click:: protonfs.cli:config_get_cmd :prog: protonfs config get 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 :doc:`../stability` for the complete precedence list and the environment variables that can override each key. Examples:: protonfs config get remote_root .. _cmd-config-set: config set ~~~~~~~~~~ .. click:: protonfs.cli:config_set_cmd :prog: protonfs config set 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`` (or :envvar:`PROTONFS_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 (:confval:`remote_root` …) and environment variable (:envvar:`PROTONFS_CONFIG` …) as cross-referenceable definitions. * :doc:`../stability` for the frozen exit-code/option contract. * :doc:`../guarantees` for durability and drift-resolution guarantees. * :doc:`../getting-started/index` and :doc:`../getting-started/syncing` for workflow-oriented walkthroughs.