Browse docs
Docs / CLI Reference / swarmfile

swarmfile

swarmfile drives Swarmfile version-control operations against a running engine: commit, checkout, branches, merge, and the rest of the day-to-day project commands. It talks to the engine over its control socket - every subcommand mirrors an operation the tray can do, and every subcommand requires a running engine except completions, which works standalone.

If the engine isn't running, start it (or the tray) first, or use the standalone swarmfile-doctor binary to diagnose connectivity without one.

swarmfile doesn't include a search subcommand - search needs its own hub-authenticated round trip independent of the control socket, so it lives in the standalone swarmfile-search binary instead.

Global flags#

These apply to every subcommand.

FlagDescription
--cache-dir <path>Engine cache dir (where control.json lives). Respects $SWARMFILE_CACHE_DIR.
--jsonPrint raw JSON instead of a human-readable summary.

Version control#

See Version Control and Branches & Merging for the conceptual walkthrough - this section is the flag-by-flag reference.

commit#

Open a changelist (if none is open) and submit it in one step.

FlagDescription
-m, --message <String>Commit message
swarmfile commit -m "Fix layout regression in the deck"

changelist#

Explicit changelist control (Mode B: staged atomic submit) - for when you want to stage multiple saves before committing them atomically, rather than using commit's one-step flow.

SubcommandFlagsDescription
open-m, --message <String>Open a changelist; subsequent saves stage into it until submit/cancel.
submit-Atomically apply the open changelist.
cancel-Abandon the open changelist.
status-Show whether a changelist is open and what's staged.
swarmfile changelist open -m "Reorganize asset folders"
# ...edit and save files...
swarmfile changelist status
swarmfile changelist submit

checkout#

Restore a scope (the whole project, a folder, an entry, or a mount-relative path) to a past commit.

FlagDescription
--at-seq <N>Commit sequence number to restore to
--path <path>Mount-relative path to restore
--folder-idRestore a specific folder by ID
--entry-idRestore a specific entry by ID

changesets list#

The commit log, newest first.

FlagDescription
--limit <N>Default 50
--offset <N>Default 0

branch#

Branch management.

SubcommandFlagsDescription
list-Active branches for this mount's project.
create<name>, --from <branch>, --from-commit <seq>, --from-tag <tag>Fork a new branch (defaults to forking from main's live head). At most one of --from-commit/--from-tag may be given - either forks the source as of that past commit instead of its head. --from is optional with either (the target commit/tag already names its branch); giving both is an error if they disagree.
switch[name]Switch which branch this mount works on. Omit name to switch back to main.
archive<name>Soft-archive a branch (main cannot be archived).

branch switch is hot - no engine restart. It's refused (exit 2) while a changelist is open; submit or cancel it first. branch create never touches the running mount either, even with --from-commit/--from-tag - creating a branch doesn't change which branch this mount is checked out to.

swarmfile branch create recover --from-commit 482
swarmfile branch create recover --from-tag v1.0

tag#

A project-scoped, immutable named pointer to a commit - "branch from tag" is "branch from commit, resolved via a name." There is no move/repoint subcommand: to repoint a name, delete it and create it again.

SubcommandFlagsDescription
list-Tags for this mount's project.
create<name>, --at-seq <seq>, --from <branch>Tag a commit. --from scopes which branch's commit log --at-seq reads from (default main). Omit --at-seq to tag that branch's CURRENT head instead - the submission-script case: tag what you just committed with no separate lookup of the seq number. Errors if the branch has no commits yet.
delete<name>Delete a tag. Real delete, not soft - a tag has no archived state.
swarmfile commit -m "final render pass"
swarmfile tag create v1.0
swarmfile tag create v0.9 --at-seq 482
swarmfile tag list
swarmfile tag delete v1.0

merge#

Merge this mount's branch into the branch it was forked from - its parent (usually main, but a branch forked from a non-main branch merges back into that one). The target is implicit: a branch already knows its own parent, so there's no target flag.

FlagDescription
--record-conflictsRecord entry_conflicts rows for interactive resolution instead of only listing them on conflict.
swarmfile branch create feature-x
swarmfile branch switch feature-x
# ...work, commit...
swarmfile branch switch
swarmfile merge --record-conflicts

ec-placement#

Deliberate erasure-coded shard placement across LAN peers.

SubcommandDescription
statusWhether this mount currently has deliberate LAN shard placement on.
enableTurn on deliberate LAN shard placement.
disableTurn off deliberate LAN shard placement.

ec-placement enable/disable restart the engine to take effect.

Placement spreads shards across your LAN peers, which are normally found by mDNS. If your network blocks mDNS multicast (common on corporate/VLAN'd networks), the office won't be discovered and every shard falls back to this machine - set lan_from_office (see Engine Config File) so same-office peers count as LAN.

shards <path>#

Where a file's erasure-coded shards live across the office - for each piece, the machines it's assigned to and whether this one holds it. Requires ec-placement enable and a live P2P fabric.

Day-to-day#

status#

This mount's engine, peers, and settings at a glance.

conflicts#

Lists files that are blocked because the same file was changed in two places and the two versions haven't been reconciled yet.

Worth knowing why this command exists: while a file is in that state, writes to it are refused, and an application will report a plain I/O error. That's not Swarmfile being unhelpful - a filesystem has no way to say "someone else changed this too", so there is no error an application could show you instead. This command is where the reason lives.

$ swarmfile conflicts
1 file(s) blocked by an unresolved conflict:

  /Projects/plan.dwg

Writes to these files are refused until the conflict is resolved.
Resolve them in the tray, or in the web dashboard.

Resolving is done from the tray or the web dashboard, where you can see both versions and choose which to keep (or keep both).

throttle#

Bandwidth throttling - applies immediately, no restart.

SubcommandFlagsDescription
status-Current throttle settings.
enable--download-mbps <N>, --upload-mbps <N>Turn throttling on. Omit a flag to leave that direction's rate at whatever it already resolves to.
disable-Turn throttling off.

What the cap covers. Bulk background transfer: uploads, hydrate, and fetch. A read an application is actually waiting on - opening a file through the drive - is not throttled, so a cap set to protect the office link can't turn somebody's file open into a stall. The practical consequence is that a large hydrate or fetch is bounded by this setting: both are pre-fetches filling the cache ahead of use, not somebody sitting in front of a spinner.

cache#

Local disk cache size cap - applies immediately, no restart.

SubcommandFlagsDescription
status-Current cache cap and usage.
set<gib: u64>Set the local disk cache cap, in GiB.

hydrate#

Bulk-materialize a scope of this mount into the local cache, pinned against eviction until released - for a machine that needs to work fully offline, or that wants everything local before a job starts rather than fetching lazily during it.

SubcommandFlagsDescription
start[path], --yesDownload and pin every file under path (default: the whole project). Prints a size estimate and a warning if it exceeds your cache cap, and asks for confirmation, unless --yes. Then polls progress until done.
status-Progress of the current (or most recent) hydrate job.
cancel-Cancel a running hydrate job. A no-op if nothing is running.
release[path]Unpin everything hydrated under path (default: the whole project), making it evictable again.
swarmfile hydrate start ./Projects/shots/seqA --yes
swarmfile hydrate status
swarmfile hydrate release ./Projects/shots/seqA

Pinned blocks are never evicted by the cache's normal LRU sweep, so a large hydrate can push disk usage past your configured cache cap - hydrate start warns about this up front rather than silently growing past it. Pins are permanent until you run hydrate release, not time-limited.

fetch#

Pull a byte range of one file into the local cache - including from a version somebody else is still uploading.

On a slow link a large upload takes hours or days, and until it finishes the new version isn't readable at all. This asks the uploading machine to send the part you need first, then downloads it as it lands. What you get back is the time it takes to send that range, not the time it takes to send the file.

FlagDescription
--tail <bytes>Fetch the last N bytes. The common case for media, where the part you need is at the end. Resolved against the in-flight size, which is why it's a flag rather than arithmetic you do yourself - see below.
--start <bytes>First byte of the range. Default 0. Conflicts with --tail.
--len <bytes>How many bytes from --start. Clamped to the end of the file. Conflicts with --tail.
--version <which>newest (default - the in-flight version if there is one, otherwise the committed one), pending (fail unless something is uploading), or head (the committed version only).
--followTrack a moving reader instead of fetching a fixed range: keep a buffer ahead of wherever it is and re-target when it seeks. --start becomes the initial position; --len/--tail don't apply.
# The last 200 MB of a file a colleague is still uploading
swarmfile fetch ./Projects/reel-04.r3d --tail 209715200

# A specific window of the committed version
swarmfile fetch ./Projects/site.rvt --start 0 --len 52428800 --version head

The command polls until every block has landed, printing progress as it goes:

$ swarmfile fetch ./Projects/reel-04.r3d --tail 209715200
19/160 blocks, 25.8 MiB fetched (waiting on the upload)
...
fetch complete: 160/160 blocks, 200.0 MiB now local

"waiting on the upload" is the normal state, not a stall. The blocks you asked for may not exist yet - the other machine is still sending them at its uplink speed - so this command can legitimately run for a long time. It gives up only when nothing is arriving and the hub reports that the upload has stopped, and says so rather than hanging.

Why --tail rather than working out the offset yourself: the size to measure back from is the in-flight version's, and the file's committed size is still the old version's. A number you compute locally would measure from the wrong end of the wrong file.

--version pending fails outright when nothing is uploading, rather than quietly giving you the committed version. That is deliberate: somebody who asked for the version in progress and silently received yesterday's bytes has no way to tell.

Fetched blocks are pinned exactly like hydrate, so they aren't evicted by the cache's LRU sweep before you open the file - and they are released the same way, with swarmfile hydrate release <path>. One job runs at a time; starting a second while one is running is refused rather than queued.

You can also do this without the CLI: reading the file normally through the mount always works, and gets the committed version. fetch is for the case where the version you want is still on its way. (A mount can additionally be opted into opening in-progress uploads directly - see stream_in_flight in the config reference.)

Following a moving reader#

A fixed range is the right shape for "give me the last 200 MB". It's the wrong shape for watching a file as it uploads, because a viewer plays forward and seeks. --follow keeps a buffer ahead of a read position you report as it moves, and re-targets when it jumps.

CommandFlagsDescription
fetch <path> --follow--start <bytes>, --version <which>Start following from --start (default 0). Returns once the job is running; it does not complete on its own.
fetch-seek<position: u64>Report where the reader has got to. The buffer re-targets there immediately. A no-op if nothing is following.
fetch-status-Progress of the current (or last) fetch. The way to watch a --follow job.
swarmfile fetch ./Projects/reel-04.r3d --follow --version pending
swarmfile fetch-seek 5000000
swarmfile fetch-status

For a follow job, fetch-status reports position, bufferedBytes and readRateBps rather than a block count. bufferedBytes is the number worth watching: it counts contiguous bytes from the read position, so it answers "will this play" - where a block total does not. A hole two blocks ahead stalls a reader however full the rest of the window is.

Buffer depth is derived from how fast the reader is actually consuming, not from a fixed byte count: 30 seconds of a 6 Mbit/s proxy and 30 seconds of a 200 Mbit/s master are very different numbers of bytes, and a single figure would be useless for one of them. Set the seconds with SWARMFILE_STREAM_BUFFER_SECS (default 30). A seek is not counted as playback, so scrubbing doesn't inflate the estimate.

A follow job ends when you cancel it, when the upload finishes and the buffer reaches the end of the file, or after five minutes with no fetch-seek - a paused viewer reports nothing, and one job runs at a time, so an abandoned follow would otherwise block every later fetch.

seed#

NAS/seed-node mode.

SubcommandDescription
statusCurrent seed-mode state.
enableTurn on seed mode. Requires the Pro plan or above, and is mutually exclusive with Cloud-only mode.
disableTurn off seed mode.

seed enable/disable restart the engine.

watch / unwatch / watches#

CommandDescription
watch <path>Watch a file or folder.
unwatch <path>Stop watching a file or folder.
watchesEverything you're currently watching in this project.

comment / comments#

CommandFlagsDescription
comment <path>-m, --message <String>Post a comment on a file.
comments <path>-List comments on a file.

presence (alias who)#

Who's currently editing what in this project.

completions <shell>#

Generate a shell completion script. Works without a running engine. Supports bash, zsh, fish, elvish, powershell.

swarmfile completions zsh > ~/.zsh/completions/_swarmfile

doctor#

Connectivity diagnostics - asks the running engine to run its probe suite. For a machine where the engine isn't running, use the standalone swarmfile-doctor binary instead.

log#

The commit log as a compact, colored feed (like git log --oneline).

FlagDescription
--limit <N>Default 20

tail#

Watch this project's activity live - comments, watched-file changes, lock conflicts. Ctrl+C to stop.

Headless engines#

api-key#

Project-scoped API keys, for an engine that runs where nobody can complete a browser sign-in - a render-farm node, a CI runner, a build box. A machine holding one authenticates without an interactive OIDC session at all.

SubcommandDescription
listEvery key for this mount's org. Never shows key material.
create <name> [--project <id>]Mint a key. --project defaults to this mount's own project.
revoke <id>Revoke by id (from list). Immediate - the key stops authenticating right away.

Minting and revoking require an admin or owner role in this mount's org. The same mint/list/revoke lives in the dashboard, under API keys.

create prints the raw secret once. The hub never returns it again - copy it before you scroll. With --json the secret is in the response body's key field, which is what makes it scriptable; treat that output as the credential it is.

A key is walled off to exactly one project. It cannot read or write anything else in the org - not even projects the admin who minted it can see - so a compromised farm node is confined to the project it was built for, and revoking a key stops that one machine without touching anybody's own session.

Using one. Set it as SWARMFILE_API_KEY on the machine, and the engine skips sign-in entirely: no browser, no refresh token, no impersonating whoever happened to mint it. If both SWARMFILE_API_KEY and SWARMFILE_OIDC_REFRESH_TOKEN are set the API key wins, and the engine says so in its log rather than choosing quietly - a leftover personal refresh token on a shared machine is the more likely mistake of the two.

swarmfile api-key create farm-node-12 --project shots

If the mount covers more than one project, --project is required: there is no single obvious answer, and guessing one would scope a credential to the wrong place.

See Deployment Topologies for how this fits with per-node config files and seed nodes.

Scripting against the CLI#

Every subcommand takes the global --json flag and prints the engine's raw response, which is the intended path for automation - commit from CI, hydrate before a render, check conflicts in a pre-flight script. Pair it with an API key and no part of the loop needs a human.

For reacting to changes rather than polling for them, tail streams this project's activity as it happens. There are no outbound webhooks, so an integration that needs to be notified has to hold that connection open or poll on its own schedule.