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.
Under the hood
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.
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.
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.
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.
libraryfolders.vdf, appworkshop_107410.acf and meta.cpp, then matched by size and MD5 of the parts.| Result | Mechanism | Cost |
|---|---|---|
| Full match | junction (or directory symlink) for the whole @mod | 0 bytes |
| Partial match | hardlinks per matching file, download the rest | missing bytes |
| No match | plain files in the workspace | full size |
| Links impossible | copy (exFAT, SMB share) | full size |
| Passthrough | launch straight from the source folder | 0 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.
The downloader is the reason a cold install of three mods dropped from 78 seconds to 8.4.
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.
File-level parallelism follows disk and network pressure instead of a fixed number, and
parallelMods (default 2) installs several mods at the same time.
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.
Transient failures (timeouts, 5xx, truncated bodies) retry automatically. Permanent ones can
abort, retry, skip or ask.
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.
0 done · 1 failed · 3 cancelled (resumable) ·
4 finished with skipped mods. The GUI maps each one to a precise message.
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
}
]
}
}
}
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.
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.
The GUI is a thin client over this CLI, so anything the interface does can be scripted or scheduled. Every
command accepts --json.
| Command | What it does |
|---|---|
nimby doctor | environment 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 install | active repository, configured workspace |
nimby install --mods "@cba,@ace" --parallel 12 --mod-parallel 3 | pick mods and tune concurrency |
nimby install --progress json --on-error ask --control-stdin | what the GUI runs: one JSON event per line, control words on stdin |
nimby update --check / --deep | compare installed mods with the repository, stat-based or hashing |
nimby repair | reinstall 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 status | what is installed in the workspace |
nimby store info|prune | shared 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 publish | generate 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 |
The implementation and the reverse-engineered format are both public.
Every field, the checksum chain and the PBO part layout, reverse-checked against a live repository.
From extracting the zip to launching on a server, with troubleshooting for the usual Windows cases.
Rust core, CLI and the Avalonia front end, plus the build and packaging scripts, under GPL-3.0-or-later.