Under the hood

How Nimby works

Nimby is a small Rust core with two front ends: an Avalonia desktop app and the nimby command line tool the GUI drives. Every decision below is visible from both.

1. It speaks the Swifty repository format

A repository is plain HTTP: one manifest per repository, one manifest per mod, and the payload next to it. Nothing proprietary, nothing to convert.

<base>/repo.json            repository manifest
<base>/repo.png             optional banner (SHA-1 in imageChecksum)
<base>/icon.png             optional icon   (SHA-1 in iconImageChecksum)
<base>/@mod/mod.srf         per-mod manifest
<base>/@mod/<path>          payload, mirrors the on-disk mod tree

Both manifest flavours are read and written: the modern Swifty 3 JSON form (mod.srf with Files and Parts) and the legacy text format. Unknown properties survive a load/save cycle, and property names are matched case-insensitively, so a repository written by any Swifty client keeps working.

One chain, three hashes

  • Part — MD5 of a byte range of a file.
  • File — MD5 over the concatenated uppercase part hashes.
  • Mod — MD5 over fileChecksum + path for every file, sorted by path.

Because the chain is reproduced exactly, nimby verify can print the same mod checksum the repository publishes — for example @cba_ws → DD39F3C34318C8CB5950E0B785FBCB08. Every downloaded part is checked before it is written; a mismatch retries with backoff instead of silently corrupting a PBO.

2. It looks for the bytes before it downloads them

Install is a matching problem: for each file in the manifest, find identical content that is already on the machine, and only fetch what is left.

  1. The workspaceA file that is already installed and still matches its recorded signature is reused as-is.
  2. The shared storeOne copy per file across every repository; hardlinked in, never copied.
  3. The Steam Workshop cacheFound through the registry, libraryfolders.vdf, appworkshop_107410.acf and meta.cpp, then matched by size and MD5 of the parts.
  4. The repositoryOnly the files that are missing anywhere are downloaded.
ResultMechanismCost
Full matchjunction (or directory symlink) for the whole @mod0 bytes
Partial matchhardlinks per matching file, download the restmissing bytes
No matchplain files in the workspacefull size
Links impossiblecopy (exFAT, SMB share)full size
Passthroughlaunch straight from the source folder0 bytes, no install

Junctions and hardlinks need no administrator rights. Symlinks are the fallback when a junction cannot be made; copying is the last resort. The mode is chosen per mod and shown in the install summary.

3. It downloads in parallel and survives interruptions

The downloader is the reason a cold install of three mods dropped from 78 seconds to 8.4.

Chunked transfers

Files are fetched as 8 MiB byte ranges with up to four workers per file. Servers without Range support fall back to one streamed request with the same progress reporting.

Adaptive concurrency

File-level parallelism follows disk and network pressure instead of a fixed number, and parallelMods (default 2) installs several mods at the same time.

Cancel means pause

Cancelling finishes the current chunk and keeps every verified file. The next run reuses them: a real test cancelled at 6 % of a 476 MB mod and the re-run moved 39 MB.

Retry with backoff

Transient failures (timeouts, 5xx, truncated bodies) retry automatically. Permanent ones can abort, retry, skip or ask.

Asking the user

With --on-error ask and --control-stdin the CLI waits for a control word on stdin, so the GUI can show a prompt and flash the taskbar instead of stealing focus.

Exit codes

0 done · 1 failed · 3 cancelled (resumable) · 4 finished with skipped mods. The GUI maps each one to a precise message.

4. It remembers where every byte came from

Each repository folder carries .nimby/tracking.json: one record per installed file with its origin, physical location, link kind, expected checksum and a size+mtime signature.

{
  "mods": {
    "@cba_ws": {
      "checksum": "DD39F3C34318C8CB5950E0B785FBCB08",
      "files": [
        {
          "path": "addons/lxws_cba_compat.pbo",
          "origin": "workshop",        // download · copy · workshop · store · reuse
          "location": "workshop",      // workspace · store · workshop
          "link": "hardlink",          // junction · symlink · hardlink · file
          "checksum": "5670E8F3…",
          "size": 60493,
          "mtime": 1730000000
        }
      ]
    }
  }
}
  • Stats — logical size, bytes on disk, bytes in the workspace, downloaded and saved, per repository and in total.
  • update --check — compares recorded signatures with the files and the repository manifest; milliseconds, because it only stats files.
  • update --check --deep — hashes what is on disk when you want certainty.
  • repair — reinstalls only what no longer matches, from Workshop, store or the repository.
  • track — prints the index, --verify re-checks it, --rebuild backfills it for an old install.

The index is the reason the Stats page can honestly say what was saved, and why an update check does not walk 20 GB of PBOs.

5. What lands on disk

The workspace is a root, not an install target: every repository gets its own isolated subfolder, so two communities never mix.

E:\Nimby\                              workspace root
  Example-Repo\                        one repository, fully isolated
    @cba_ws\                           mod folder (junction, hardlinks or files)
    .nimby\<mod>.json                  provenance: repo, checksum, mode, sources
    .nimby\tracking.json               per-file index (origin, location, size+mtime)
  shared\                              optional shared store, one copy per file
%APPDATA%\Nimby\config.json             config when running non-portable
data\config.json                       config and hash cache in the portable build

Renaming the repository subfolder only changes where the next install and launch look; existing mods stay where they are.

6. Command line reference

The GUI is a thin client over this CLI, so anything the interface does can be scripted or scheduled. Every command accepts --json.

CommandWhat it does
nimby doctorenvironment checks: Steam, Arma, Workshop, link capabilities, version and credits
nimby repo add <url> [--name] [--select]add and activate a repository
nimby repo folder <url> [folder|--default]subfolder under the workspace root
nimby installactive repository, configured workspace
nimby install --mods "@cba,@ace" --parallel 12 --mod-parallel 3pick mods and tune concurrency
nimby install --progress json --on-error ask --control-stdinwhat the GUI runs: one JSON event per line, control words on stdin
nimby update --check / --deepcompare installed mods with the repository, stat-based or hashing
nimby repairreinstall mods that no longer match their manifest
nimby verify <folder>check a mod folder and print its mod checksum
nimby track [--verify] [--rebuild]inspect, re-check or backfill the tracking index
nimby stats [--json]bytes on disk, in the workspace, downloaded and saved
nimby statuswhat is installed in the workspace
nimby store info|pruneshared store size, and removing unreferenced files
nimby launch [--server <name>] [--dry-run]start Arma 3 with every installed mod and the configured parameters
nimby migrate [--layout]import a Swifty installation's settings; move root-level mods into the repository subfolder
nimby srf / nimby publishgenerate a manifest, or build a repository from a folder of mods
nimby inspect <url> [--images]remote repository summary, optionally caching its artwork
nimby config set …Arma path, scratch and workspace folders, shared store, link mode, parallelism, parameters

7. Where to read more

The implementation and the reverse-engineered format are both public.

Repository format

Every field, the checksum chain and the PBO part layout, reverse-checked against a live repository.

docs/FORMAT.md →

Step-by-step tutorial

From extracting the zip to launching on a server, with troubleshooting for the usual Windows cases.

Open the tutorial →

Source code

Rust core, CLI and the Avalonia front end, plus the build and packaging scripts, under GPL-3.0-or-later.

github.com/ffredyk/nimby →