# Engine Config File (`config.json`)

The Swarmfile engine reads its startup configuration from these sources, in
this order of precedence:

1. **Environment variables** (`SWARMFILE_*`) - always win, with one exception: `data_namespace` on a packaged install, where the bundled install config wins even over the env var (see "A note on environment-selection keys" below).
2. **A stored OIDC session** *(applies to `oidc_issuer`/`oidc_client_id` only)* - after an interactive sign-in, the engine
   remembers the issuer and client id it signed in against; those beat a
   hand-edit of `oidc_issuer`/`oidc_client_id` in the file. Editing either key
   on a signed-in install can appear to do nothing until you sign in again.
3. **The `config.json` file** - the subset of settings documented below.
4. **Built-in defaults** - a released build ships pointing at Swarmfile's
   hosted service, so an unconfigured engine connects to production rather than
   nothing.

Most people never touch this file: the Desktop App writes the keys it manages
for you, and a normal sign-in fills in the rest. You edit `config.json`
directly for **headless and unattended setups** - a NAS/seed node, a
render-farm worker, a VM, or any machine that boots the engine from autostart
rather than a signed-in Desktop App session - where there's no UI to click and, on
macOS/Windows, no shell environment for the engine to inherit exported
variables from.

## Where the file lives

| Platform | Default path |
|---|---|
| macOS / Linux | `~/.config/swarmfile/config.json` (respects `$XDG_CONFIG_HOME`) |
| Windows | `%LOCALAPPDATA%\swarmfile\config.json` |

That per-user path is only half the picture. A packaged install also ships its
own **install config** - read-only, written by the installer, and living next
to the binary (on macOS, inside the bundle's `Contents/Resources/config.json`;
on Linux's `.deb`, `/etc/swarmfile/config.json`). At startup the engine merges
the two, generally with the per-user file winning per key - except for a
small, fixed set of keys a packaged install manages itself, where the install
config wins instead. See "A note on environment-selection keys" below for
exactly which keys, and why, before hand-editing any of them.

To point the engine at a **specific file** instead of this two-file merge -
e.g. one config per project on a multi-tenant node - set
`SWARMFILE_CONFIG=/path/to/config.json`. That bypasses the merge entirely and
reads only that one file.

## Editing safely

A few things are worth knowing before you hand-edit this file:

- **Restart the engine after editing.** The file is read once at startup. A
  handful of values can also be applied to a running engine through the control
  socket with no restart: `cache_max_bytes`, `readahead_buffer_secs`,
  `throttle_enabled` and the four
  office-hours keys, `office_id`, `lan_from_office`, and `branch`. Everything
  else is read only at startup (including `seed_mode`, `hub_only`,
  `ec_lan_placement`, `changeset_idle_secs`, `attr_cache_secs`, `mount_point`,
  `read_only`, and `session_kind`). `sync_ignore_enabled` updates live and then
  drains and restarts the engine. Either way, a change to the **file itself**
  needs a restart to be read.
- **A single syntax error discards the *whole* file - and says so.** If the JSON
  doesn't parse, the engine logs a warning, ignores that file, and keeps going
  with whatever the other sources provide. On a packaged install that means the
  bundled install config and environment still apply - but a broken per-user
  file still drops your `org_id` and `project_id`, so the drive has nothing
  behind it. Instead of a generic "no project" prompt, the Desktop App's status
  then names the file and the parser's position (for example
  `config.json: expected value at line 1 column 3`), because a project picker
  can't help when the org id lived in the file that failed to parse. (With
  `SWARMFILE_CONFIG`, there is no other file, so a bad file leaves the engine on
  built-in defaults.) A JSON syntax error - a trailing comma, say - silently
  drops those along with everything else in the file. Validate your JSON before
  restarting.
- **Unknown keys are named, not ignored in silence.** A valid file with a
  misspelled key (say `project_idd`) loads, but the engine warns - and when
  there is no project to mount, the Desktop App's status names the
  unrecognized key(s), because a typo like that is otherwise invisible: the
  setting simply never applies. The key list is derived from the engine's own
  parser, so the table above stays current with it.
- **A UTF-8 byte-order mark (BOM) is tolerated.** Windows Notepad and
  PowerShell 5.1's `-Encoding UTF8` prepend one; the engine strips it. Still,
  prefer a plain UTF-8 editor.
- **Environment variables override the file.** If a launcher (systemd unit,
  Docker `-e`, a scheduled task) sets `SWARMFILE_HUB_URL`, editing `hub_url` in
  the file will appear to do nothing. Check what actually took effect with
  `--print-env-vars` (below).
- **Don't fight the Desktop App.** For keys the Desktop App or `swarmfile` CLI manage (mount
  point, seed mode, hub-only, cache size, read-ahead, throttle, LAN placement, branch),
  prefer their controls - the Desktop App may rewrite the file and overwrite a manual
  edit. Hand-editing is for machines with no Desktop App.

## Connecting to the hosted service

A released build ships pointing at Swarmfile's production service, so you do
**not** need to configure the hub or identity provider by hand. With nothing
set, the engine defaults to:

| Setting | Default |
|---|---|
| `hub_url` | `https://hub.swarmfile.com` |
| `oidc_issuer` | `https://id.swarmfile.com` |
| `oidc_client_id` | `swarmfile-desktop` on a packaged install; `swarmfile-engine` is the bare binary's fallback when no bundled config supplies one |

A headless machine on the hosted service therefore only needs to say *which* org
and project it serves and supply a credential - `org_id`, `project_id`, and one
of `SWARMFILE_API_KEY` or `SWARMFILE_OIDC_REFRESH_TOKEN` (see below). Set
`hub_url`, `oidc_issuer`, or `oidc_client_id` only when you run a **self-hosted
or Enterprise hub** on your own domain.

## Keys you can set in the file

Every key is optional. Booleans are JSON booleans (`true` / `false`) in the
file - the `true`/`1` string form is only for the environment-variable
equivalents.

| Key | Type | Default | Env override | What it does |
|---|---|---|---|---|
| `hub_url` | string | `https://hub.swarmfile.com` | `SWARMFILE_HUB_URL` | Hub API base URL. Override only for a self-hosted / Enterprise hub. |
| `org_id` | string | *(none)* | `SWARMFILE_ORG_ID` | Multi-tenant org id; the hub URL is prefixed with `/orgs/{org_id}`. |
| `project_id` | string | *(none)* | `SWARMFILE_PROJECT_ID` | Scope every metadata operation to one project (required for headless/seed nodes). |
| `branch` | string | the project's default branch (each project names it at creation - commonly `main`) | `SWARMFILE_BRANCH` | Branch this mount is checked out to at boot. |
| `office_id` | string | `default` | `SWARMFILE_OFFICE_ID` | Peer-discovery grouping label. Applied live - `swarmfile office set <office_id>` updates a running engine within one peer-discovery interval (~60s), no restart. Editing the file directly still requires a restart to pick it up, same as any other key here. |
| `oidc_issuer` | string | `https://id.swarmfile.com` | `SWARMFILE_OIDC_ISSUER` | OIDC issuer URL. Override only for a self-hosted IdP. |
| `oidc_client_id` | string | `swarmfile-desktop` on a packaged install | `SWARMFILE_OIDC_CLIENT_ID` | OIDC client id. A bare binary with no bundled install config falls back to `swarmfile-engine`; setting this by hand on a signed-in install can appear to do nothing (see the precedence note at the top). |
| `site_url` | string | *(install config)* | `SWARMFILE_SITE_URL` | Public site base URL, used for links the app shows and builds (docs, share URLs), and the links the engine's git-push helper prints. Set by the installer's read-only install config; the env var wins over it (see the note below). |
| `updater_endpoint` | string | *(install config)* | `SWARMFILE_UPDATER_ENDPOINT` | Update-manifest endpoint the Desktop App checks. Set by the installer's read-only install config; the env var wins over it (see the note below). |
| `encryption_project_id` | string | falls back to `project_id` when encryption is enabled and no `SWARMFILE_ENCRYPTION_KEY` is supplied | `SWARMFILE_ENCRYPTION_PROJECT_ID` | Project whose hub-managed key encrypts blocks. |
| `read_only` | bool | `false` | `SWARMFILE_READ_ONLY` | Force a read-only mount (guest sessions). |
| `session_kind` | `owner`\|`member`\|`guest` | `owner` | `SWARMFILE_SESSION_KIND` | Active session role (drives the Desktop App's "Guest (read-only)" badge). |
| `hub_only` | bool | `false` | `SWARMFILE_HUB_ONLY` | Disable all P2P (QUIC/mDNS/gossip); serve every read over HTTPS from the hub. The Desktop App and CLI call this **cloud-only mode**. |
| `seed_mode` | bool | `false` | `SWARMFILE_SEED_MODE` | Run as a NAS/seed node (fetch every block into a warm cache, mirror to cloud storage, no VFS mount). |
| `ec_lan_placement` | bool | `false` | `SWARMFILE_EC_LAN_PLACEMENT` | Deliberately spread erasure-coded shards across LAN peers. |
| `lan_from_office` | bool | `false` | `SWARMFILE_LAN_FROM_OFFICE` | Treat same-office peers as LAN peers even when mDNS discovery is unavailable. LAN peers are normally found by mDNS; on networks that block mDNS multicast (many corporate/VLAN'd networks) that finds nothing, so peers read as WAN and LAN shard placement has no one to spread across. Turn this on when you know your `office_id` members share a LAN. Pairs with `ec_lan_placement`. Applied live via the same `swarmfile office set` path as `office_id` - see above. |
| `node_auth` | bool | `false` | `SWARMFILE_NODE_AUTH` | For orgs that enforce P2P node authentication: refuse every peer until the hub confirms the approved roster. The hub delivers the authoritative enforcement flag; this key sets only the boot-time default to use until that first response arrives. Leave it off unless your org enforces node authentication - it's for headless nodes configured through this file. |
| `sync_ignore_enabled` | bool | `true` | `SWARMFILE_SYNC_IGNORE_ENABLED` | Whether `.gitignore`/`.swarmfileignore` rules exclude paths from sync (the git-like default). The **"sync everything, ignore the ignore rules"** escape hatch: set to `false` and no path is hidden from listings, excluded from upload, or refused on read - for a project that wants everything synced regardless of its ignore files. |
| `iroh_bind_addr` | string | *(OS-assigned ephemeral)* | `SWARMFILE_IROH_BIND_ADDR` | Fixed UDP bind address/port for peer-to-peer (QUIC) transport. Defaults to an OS-assigned ephemeral port; set this only when a firewall requires a predictable port for a specific deployment. File-settable, including on headless/seed nodes. |
| `throttle_enabled` | bool | `false` | `SWARMFILE_THROTTLE_ENABLED` | Enable bandwidth throttling / QoS. Off by default: turning it on with no explicit site bandwidth set applies a default 100 Mbps symmetric cap, so set `site_download_bandwidth_mbps`/`site_upload_bandwidth_mbps` to the real link size first. |
| `site_download_bandwidth_mbps` | number | *(unset)* | `SWARMFILE_SITE_DOWNLOAD_BANDWIDTH_MBPS` | Download bandwidth cap in Mbps. |
| `site_upload_bandwidth_mbps` | number | *(unset)* | `SWARMFILE_SITE_UPLOAD_BANDWIDTH_MBPS` | Upload bandwidth cap in Mbps. |
| `office_hours_start` | string | `08:00` | `SWARMFILE_OFFICE_HOURS_START` | Office-hours window start, `HH:MM` local time. WAN traffic is capped to `office_hours_wan_pct` of the site link inside this window. Also settable per-run via `swarmfile throttle enable --office-hours-start`. |
| `office_hours_end` | string | `18:00` | `SWARMFILE_OFFICE_HOURS_END` | Office-hours window end, `HH:MM` local time. Must differ from `office_hours_start`; a file that sets them equal has no office-hours window, so `off_hours_wan_pct` applies all day. |
| `office_hours_wan_pct` | number | `20` | `SWARMFILE_OFFICE_HOURS_WAN_PCT` | WAN bandwidth percentage (0-100) allowed during office hours. |
| `off_hours_wan_pct` | number | `80` | `SWARMFILE_OFF_HOURS_WAN_PCT` | WAN bandwidth percentage (0-100) allowed outside office hours. |
| `cache_max_bytes` | number | `10737418240` (10 GiB) | `SWARMFILE_CACHE_MAX_BYTES` | Local block-cache size cap, in bytes. |
| `readahead_buffer_secs` | number \| null | *(unset - each file type's own default; 15 s for video containers)* | `SWARMFILE_READAHEAD_BUFFER_SECS` | Read-ahead lead kept ahead of a sequential reader, in seconds of playback. Applies to the next file opened, no restart; blank/null = the file type's default, `0` = that file type's flat ceiling (64 MiB for video containers, 16 MiB otherwise). Normally set from the Desktop App's **Settings → Read-ahead**. |
| `changeset_idle_secs` | number | `300` | `SWARMFILE_CHANGESET_IDLE_SECS` | Idle seconds before a project-scoped changeset auto-commits (`0` disables grouping). |
| `attr_cache_secs` | number | `2` | `SWARMFILE_ATTR_CACHE_SECS` | macOS and Linux (FUSE-based mounts) only, no effect on Windows/WinFsp: kernel attribute-cache ceiling (`0` keeps FUSE-T's 5-60s default on macOS). |
| `mergetool_cmd` | string | *(unset)* | `SWARMFILE_MERGETOOL_CMD` | Command template for the tool `swarmfile conflicts resolve --tool` runs, e.g. `kdiff3 $BASE $LOCAL $REMOTE -o $MERGED` - the same `$BASE`/`$LOCAL`/`$REMOTE`/`$MERGED` placeholder convention `git mergetool` uses. When set, this wins outright - swarmfile uses it directly without consulting git config at all. If unset, swarmfile falls back to your system/global git config's own `[merge] tool` - anyone with `git mergetool` already configured needs nothing here. |
| `mergetool_name` | string | *(unset)* | `SWARMFILE_MERGETOOL_NAME` | A cosmetic display label for the tool `mergetool_cmd` runs - shown in messages/logs, nothing more. Only read when `mergetool_cmd` is also set; it does not affect which tool is used and is never looked up in git config. |
| `data_namespace` | string | *(unset)* | `SWARMFILE_DATA_NAMESPACE` | Per-install data namespace, so two installs (for example, test and production) can coexist on one machine. Set by the installer/build, not something you hand-edit in this file - see "A note on environment-selection keys" below. |
| `auto_open_changelist_for_preference` | bool | `false` | `SWARMFILE_AUTO_OPEN_CHANGELIST_FOR_PREFERENCE` | Commit-mode policy: on a project/branch pinned to "staged changelist," automatically open one on boot so a member doesn't have to remember to run `changelist open` every session. Never overrides an admin lock, and never re-evaluated on a later branch switch. Off by default - unlike most flags here, an unattended auto-open changes a running mount's write behavior with no user action. |
| `deferred_namespace` | bool | `true` | `SWARMFILE_DEFERRED_RENAME` (alias `SWARMFILE_DEFERRED_NAMESPACE`) | Apply non-displacing/displacing renames and file/empty-directory deletes of confirmed entries locally and queue them, while hub confirmation, retraction and refresh run in the background. Set `false` to opt out (env `0`/`false`/`no`/`off` also opts out; any other env value is ignored and logged). Environment beats file; of the two env spellings, `SWARMFILE_DEFERRED_RENAME` wins. |
| `mount_point` | string | `~/Swarmfile` (Unix), `S:` (Windows) | `SWARMFILE_MOUNT_POINT` | Where to mount **the boot mount** - a directory on Unix, a drive letter on Windows. Since this file is only read at startup, it can only configure the one mount an engine boots with; see the note below for opening additional ones at runtime. |
| `mount_point_auto` | *(managed)* | - | - | **Do not set.** The engine writes this to record a drive letter *it* auto-selected for the boot mount; hand-editing it breaks the "don't relocate a letter the user chose" logic. |

> **Additional mounts aren't configured here.** One engine process can hold several full mount points open at once against the same project/org - see [`swarmfile mounts`](https://swarmfile.com/docs/cli/swarmfile#mounts) - but that's an entirely runtime, CLI/Desktop-App-driven operation with no config-file or env-var equivalent: there's no `mount_point_2` key or similar, and no way to declare a second mount up front for the engine to open at boot. `mount_point`/`mount_point_auto` above describe the boot mount specifically, whether or not anything else is opened later in the session. You don't need to edit this file to get a free drive letter for an additional mount: `swarmfile mounts open auto` (or the Desktop App's Open mount form, which defaults to it on Windows) picks one.

### A note on environment-selection keys

`hub_url`, `oidc_issuer`, `oidc_client_id`, `site_url`, `updater_endpoint`, and
`data_namespace` are handled differently from every other key in the table
above. A packaged install (the macOS `.app`, the Windows installer, the Linux
`.deb`) ships its own read-only install config that sets these - that's how an
install stays on the environment it was packaged for, and why one signed binary
set can serve different environments without drifting. When the install config sets one of these six keys, **it wins over
whatever you put in your per-user `config.json`** - a hand-edit of `hub_url`
or `oidc_issuer` at the per-user path documented above will silently appear to
do nothing on a normal packaged install. Your per-user value is only used as a
fallback for a key the install config left unset - which is the normal case
for a from-source/dev build, or an older single-file-layout install. Every
other key in this file follows the ordinary env-var-then-file precedence at
the top of this page; only these six are install-config-first.

## Settings that are environment-variable-only

Many settings can be tuned via `SWARMFILE_*` env vars but are **not** readable
from `config.json` - the file supports only the keys in the table above.
[Environment Variable Index](https://swarmfile.com/docs/reference/environment-variables) is the full list; the
notable ones include:

- **Credentials:** `SWARMFILE_OIDC_REFRESH_TOKEN` (non-interactive OIDC),
  `SWARMFILE_API_KEY` (project-scoped headless key - takes precedence over a
  refresh token), `SWARMFILE_ENCRYPTION_KEY`, `SWARMFILE_SWARM_KEY`. Credentials
  are deliberately kept out of the file.
- **Paths & identity:** `SWARMFILE_CACHE_DIR`, `SWARMFILE_IROH_DIR`,
  `SWARMFILE_USER_ID`, `SWARMFILE_MACHINE_ID`.
- **Encryption toggle/mode:** `SWARMFILE_ENCRYPTION_ENABLED`,
  `SWARMFILE_EC_ENABLED` (`on`/`off`/`auto`).
- **Networking:** `SWARMFILE_IROH_PUBLIC_ADDR`, `SWARMFILE_HEALTH_PORT`.
  `SWARMFILE_MDNS_INTERFACES` (comma-separated interface names) restricts
  LAN discovery to exactly those interfaces and skips the built-in
  VPN/virtual-interface filter. `SWARMFILE_MDNS_EXCLUDE_INTERFACES` removes
  more interfaces from it. See
  [Network Requirements](https://swarmfile.com/docs/reference/network-requirements#lan-peer-discovery).
  (`SWARMFILE_IROH_BIND_ADDR`/`iroh_bind_addr` **is** file-settable - see the
  table above.)
- **QoS:** `SWARMFILE_DSCP_INTERACTIVE`, `SWARMFILE_DSCP_BACKGROUND`,
  `SWARMFILE_SITE_DOWNLOAD_BANDWIDTH_MBPS` / `SWARMFILE_SITE_UPLOAD_BANDWIDTH_MBPS`.
  The office-hours schedule keys (`SWARMFILE_OFFICE_HOURS_START` / `_END`,
  `SWARMFILE_OFFICE_HOURS_WAN_PCT`, `SWARMFILE_OFF_HOURS_WAN_PCT`) **are**
  file-settable - see `office_hours_*`/`off_hours_wan_pct` in the table above.
- **LAN shard placement tuning:** `SWARMFILE_EC_LAN_PLACEMENT_REPLICATION`
  (default `3`) is the replication factor K - each erasure shard is deliberately
  assigned to this many LAN peers. `SWARMFILE_EC_LAN_PLACEMENT_POLL_INTERVAL`
  (default `300`) is the reconciler's safety-net pass interval in seconds, run
  alongside the reactive pass that fires on a peer-roster change. Both only
  matter when `ec_lan_placement` is on.
- **Seed tuning:** `SWARMFILE_SEED_POLL_INTERVAL` (default `60`, seconds),
  `SWARMFILE_SEED_UPLOAD_CONCURRENCY` (default `8`, concurrent cloud uploads).
- **Runner (headless CI):** `SWARMFILE_RUNNER_MODE` - watches
  `SWARMFILE_BRANCH` for commits/tags matching a rule in
  `.swarmfile/runner.yml` and runs it. Env-only by design, no
  `config.json` key or Desktop App toggle - see
  [Runner (Headless CI)](https://swarmfile.com/docs/guides/runner-as-ci). Mutually exclusive
  with `seed_mode`.
- **Streaming an upload in progress:** `SWARMFILE_STREAM_BUFFER_SECS`,
  `SWARMFILE_STREAM_READ_WAIT_SECS`, `SWARMFILE_STREAM_WAIT_CONCURRENCY` -
  env-only, no `config.json` key.
  `STREAM_BUFFER_SECS` (default `30`) is how
  many **seconds** of playback a `fetch --follow` job keeps buffered ahead of
  the reader; it is seconds rather than bytes because 30 s of a proxy and 30 s
  of a full-resolution master are wildly different byte counts and one figure
  would be wrong for both. `STREAM_READ_WAIT_SECS` (default `15`) is how long a
  read of a not-yet-uploaded range waits before giving up - see below.
  `STREAM_WAIT_CONCURRENCY` (default `4`, clamped 1-64) caps how many reads may
  be *waiting* at once, so a burst returns the retryable "try again" promptly
  instead of parking every operation thread; it is sized when the mount starts,
  so a change needs a remount.
- **Peer-to-peer budgets:** `SWARMFILE_IROH_PEER_BUDGET_SECS` (default `30`)
  and `SWARMFILE_IROH_PEER_BUDGET_BACKGROUND_SECS` (default `10`) cap how long a
  peer connection attempt may take for interactive and background work
  respectively.
- **Durable create queue tuning:** `SWARMFILE_CREATE_DISPATCH_CONCURRENCY`
  (default `8`, 1-64 - creates sent to the hub at once),
  `SWARMFILE_PENDING_CREATE_WAIT_SECS` (default `8`, 0-120 - how long a
  lock/ACL/share/comment on a not-yet-confirmed file waits before answering
  `409 pending_create`), and the coalescing/batching knobs
  (`SWARMFILE_CREATE_COALESCE`, `_HOLD_SECS`, `_HOLD_UPLOADING_SECS`,
  `_BATCH`). See [Coding Agents](https://swarmfile.com/docs/guides/coding-agents#4-create-bursts-and-the-pending-create-queue).
- **Worksharing refusal mode:** `SWARMFILE_WORKSHARING_FAIL_CLOSED` (Windows
  only) makes a native worksharing open refuse when the hub can't be reached.
- **mdNS service type:** `SWARMFILE_MDNS_SERVICE_TYPE` overrides the
  `_swarmfile._udp.local.` service type - for isolated test networks, not for
  normal use.
- **Upload diagnostics:** `SWARMFILE_UPLOAD_TRACE=1` promotes per-attempt
  upload logging to `warn`, including the hub's status and response body when a
  block upload or commit is refused - the first thing to set when a save isn't
  landing and you need to know why. Off by default; the lines are noisy for
  ordinary work.
- **Misc:** `SWARMFILE_XREF_PREFETCH`, `SWARMFILE_DOCTOR_PERIOD_SECS`,
  `SWARMFILE_OTEL_ENDPOINT`, `SWARMFILE_OTEL_SERVICE_NAME`.

### How streaming an in-progress upload behaves

Always on: a file that has never finished uploading can be opened through the
mount - it appears at its full eventual size and reads work, waiting briefly
where the bytes have not landed. Worth knowing how it behaves, because a read
into a range that has not been uploaded yet has to wait, and this runs on a
filesystem operation thread. An operation that waits too long does not
stall one read - it takes the whole drive with it. The wait is therefore
strictly bounded (`SWARMFILE_STREAM_READ_WAIT_SECS`, default 15 s, and `0` does
not mean "forever" - it falls back to the default), and a read that outruns the
upload returns a retryable "not yet" rather than an I/O error, because an
application that sees an I/O error part way through a file will usually
conclude the file is damaged and discard your document.

Different NLEs and DCC tools react differently to a delayed read; the bound is tuned to release the drive before an editing application gives up on it. Two consequences worth knowing: only so many reads may
be *waiting* at once (a burst beyond that gets the "try again" immediately
rather than parking every operation thread), and only a file with **no**
committed version streams - a re-save of an existing file is readable once it
commits, never mid-save.

Scope worth knowing:

- It applies **only to files with no committed version yet** - a new file
  landing for the first time. A file you are already reading never changes size
  or content because somebody started a new save.
- A file that *is* streaming reports the size it is going to be, not `0`.
  Without that, nothing could read it: no application asks for bytes past the
  end of an empty file.
- If the uploading machine is on the same **LAN**, it serves those blocks
  directly from its own disk, before they reach the cloud. Over a WAN it does
  not - that would carry every block twice on the same contended uplink.

And the limit that no setting changes: this decides **which** bytes arrive
first, not how fast the link is. A 4 TB file on a 100 Mbit/s uplink delivers
about 12 MB/s however well it is ordered.

## Project-local files (`.swarmfile/`)

Some configuration lives **inside a project** - committed like any other file,
so it versions with the branch and every contributor gets the same behavior -
instead of in the engine's `config.json`:

| File | Keys | What it does |
| --- | --- | --- |
| `.swarmfile/runner.yml` | rule list | Headless CI rules - see [Runner (Headless CI)](https://swarmfile.com/docs/guides/runner-as-ci). |
| `.swarmfile/merge.yml` | `diff3: true \| false` | Project default for `swarmfile merge --diff3` and the tray's "Auto-resolve conflicts": on a merge conflict, try a three-way text merge of each conflicting file before giving up. `true` turns it on by default; `false` turns it off and the CLI/tray name this file as the reason. Absent means off (auto-resolve stays opt-in). An explicit `--diff3` / `--no-diff3` always wins over the file. |
| `.swarmfile/cache.yml` | `caches:` list of `name`, `env`, `subdir` | Tool-cache redirects for `swarmfile run`: each entry sets the `env` variable a tool reads for its cache location (e.g. `PNPM_HOME`, `CARGO_HOME`) to this project's shared machine-local scratch directory plus `subdir`. Every mount of the project on this machine shares those directories, so a second mount does not re-download what the first already fetched. A bad entry is dropped with a warning, never the whole file. See [`scratch`](https://swarmfile.com/docs/cli/swarmfile#scratch). |
| `.swarmfile/publicignore.yml` | `version`, `paths`, `derived.ci_logs` | Path globs (gitignore-style, with `!` re-includes) kept out of a release's public view; `derived.ci_logs` sets CI-log exposure (`member-only` by default). Read from the tree of the tag being published and enforced as the release is built. See [Publishing Releases](https://swarmfile.com/docs/guides/publishing-releases#keeping-paths-out-of-a-release). |

The runner, merge and cache files are read from the branch the operation runs
against, so they can differ per branch; `.swarmfile/publicignore.yml` is read
from the tree of the tag being published. A `.swarmfile/merge.yml` that is
missing or unparseable is treated exactly like `diff3: false` - never as "turn
extra automatic behavior on".

For example, to make text-conflict auto-resolve the default for a project:

```yaml
# .swarmfile/merge.yml
diff3: true
```

## Engine command-line flags

Beyond reading `config.json`, the `swarmfile-engine` binary takes a handful of
one-shot flags - useful on a headless node where there's no Desktop App to click.

**Answered immediately - about this binary, or a running engine:**

| Flag | What it does |
|---|---|
| `--status` | Engine, hub, peers, and pending work at a glance. Queries a running engine; falls back to a one-shot read of local state if none is running. |
| `--print-env-vars` | The resolved `SWARMFILE_*` values this build consumed, secrets redacted, with a footer naming the install-config and per-user config files that fed the merge (or the `SWARMFILE_CONFIG` path). |
| `--capabilities` | The compiled feature set of *this* binary - which optional features it was built with. |
| `--build-info` | The same compiled features plus the target architecture, in a one-line machine-readable form, then exit. |
| `--version`, `-V` | Print the version and exit. |
| `--help`, `-h` | Print the flag list and exit. |

**Lifecycle - against a running engine:**

| Flag | What it does |
|---|---|
| `--pause` | Pause sync without unmounting the drive. |
| `--resume` | Resume after `--pause`. |
| `--quit` | Ask the running engine to drain in-flight work and stop. |

`--help` and `--version` answer *before* the engine reads any config, so they
work on a broken or unconfigured install. For day-to-day version-control and
mount operations, use the [`swarmfile`](https://swarmfile.com/docs/cli/swarmfile) CLI - it drives the
same engine over its control socket - rather than these flags.

The engine's own `--help` also lists offline/pinning flags (`--pin`,
`--prepare-offline`, and related). These currently run only while the engine is
**stopped**, so they can't be driven against a live mount and aren't part of the
normal headless workflow. To force a scope resident for offline or render-farm
use against a *running* engine, use [`swarmfile hydrate`](https://swarmfile.com/docs/cli/swarmfile)
instead.

## Verifying what actually took effect

Because several sources feed the final config, print the **resolved** values the
engine will use - with secrets redacted, and a footer naming the install-config
and per-user files that fed the merge (or the `SWARMFILE_CONFIG` path):

```bash
swarmfile-engine --print-env-vars
```

`swarmfile doctor` (or the standalone `swarmfile-doctor`) then confirms the
resolved hub URL, org, and credentials actually reach the hub. See
[CLI: swarmfile-doctor](https://swarmfile.com/docs/cli/swarmfile-doctor).

## Example: a headless seed node

On the hosted service the hub and IdP are already the defaults, so a seed node
only names its org and project:

```json
{
  "org_id": "org_abc123",
  "project_id": "proj_xyz789",
  "seed_mode": true,
  "cache_max_bytes": 107374182400
}
```

(On a **self-hosted or Enterprise** hub, add `hub_url`, `oidc_issuer`, and
`oidc_client_id` for your own domain.)

Pair this with a credential in the environment - `SWARMFILE_API_KEY=sf_key_…`
for a project-scoped key, or `SWARMFILE_OIDC_REFRESH_TOKEN=…` - since
credentials aren't stored in the file. See
[Deployment Topologies](https://swarmfile.com/docs/guides/deployment-topologies) and
[Self-Hosted Seed Nodes](https://swarmfile.com/docs/admin/self-hosted-seed-nodes) for the full
headless-node walkthrough, [Network Requirements](https://swarmfile.com/docs/reference/network-requirements)
for exactly what a machine using this file needs to reach, and
[Deploying to Your Team](https://swarmfile.com/docs/admin/deploying-to-your-team) for pre-staging
this file across a fleet.
