A local-first desktop application for maintaining an existing InfoCon archive. It detects what changed upstream, builds a reviewable transfer plan, executes it using BitTorrent as the primary mechanism, and produces auditable receipts.
macOS is the primary release target. Windows 10/11 runs from source (see Install (Windows)); native packaging lands after the Windows packaging smoke tests pass. Linux is supported after its packaging smoke tests pass.
- Scans your local archive and compares it to the upstream InfoCon directory listing
- Classifies each collection with an explainable status:
New,Changed,Verified current,Present (unverified),Local only, etc. - Downloads via torrent-first: fetches the published
.torrentfor each collection, inspects its manifest, and transfers only what you selected - Falls back to HTTPS only when no usable torrent exists — never silently, always labelled
- Never downgrades a torrent transfer to HTTP without explicit user approval
- Produces a signed receipt for every completed or failed plan
- Binds only to
127.0.0.1— the UI is never reachable from other machines
- macOS 13 (Ventura) or later, or Windows 10/11
- Python 3.11 or 3.12
- macOS only: Homebrew
- Windows only: Microsoft Visual C++ 2015–2022 Redistributable (x64) — required by the
libtorrentandgreenletnative extensions
brew install libtorrent-rasterbar python@3.12
pip3 install infocon-librarianOr from source:
git clone https://github.com/h0ck3ystyx/InfoconSync
cd InfoconSync
python3 -m venv --system-site-packages .venv
.venv/bin/pip install -e .On Windows, libtorrent is installed as a normal PyPI wheel (there is no
Homebrew), so a plain venv works — no --system-site-packages.
# 1. Install the VC++ redistributable once (needs admin — accept the UAC prompt):
winget install --id Microsoft.VCRedist.2015+.x64 -e
# 2. From the repo root:
py -m venv .venv
.\.venv\Scripts\pip install -e ".[dev]"
.\.venv\Scripts\pip install libtorrentWithout the VC++ redistributable, import libtorrent (and Playwright's
greenlet) fail with ImportError: DLL load failed. The app still launches
without libtorrent — it reports Torrent engine unavailable and disables
torrent transfers — but verification and torrent downloads need it.
# Launch the browser UI (opens automatically)
infocon-librarian --root /Volumes/Archives/InfoCon
# CLI — check what changed upstream
infocon-librarian check --format json
# CLI — build a transfer plan (dry run)
infocon-librarian plan --new --changed --dry-run
# CLI — run a plan
infocon-librarian sync PLAN_ID
# CLI — list receipts
infocon-librarian receipts list
infocon-librarian receipts export RECEIPT_IDBefore every torrent transfer the UI shows:
- Which tracker hosts will be contacted
- That DHT, PEX, and LSD are disabled (always, by default)
- The upload rate cap and post-completion seeding choice (off by default)
Logs redact all routable peer IP addresses and session tokens automatically. See docs/privacy-and-network.md for the full statement.
- Flask binds loopback only; a per-launch cryptographically random capability token bootstraps a session cookie (
HttpOnly,SameSite=Strict) - Every state-changing route requires a same-origin
Originheader, session cookie, and CSRF token - Torrent metadata paths are validated through
SafeArchivePathbefore the engine sees them —.., absolute paths, empty components, and reserved platform names are all rejected - No
unsafe-inlinein the Content Security Policy; all UI assets are served from'self'
python -m pytest tests/unit -q # pure unit tests
python -m pytest tests/integration -q # SQLite + Flask + fake engine
python -m pytest tests/engine -q # real libtorrent, loopback only
python -m pytest tests/e2e -q # Playwright browser tests
python -m ruff check src tests
python -m mypy src217 tests passing across unit, integration, and E2E layers. Coverage ≥ 90% for domain/planner/path code.
Browser UI / CLI
└── Flask API (loopback only, CSRF+Origin middleware)
├── Check / Plan / Verify / Receipt services
│ ├── SQLite state (WAL + FK mode)
│ ├── Remote listing fetcher + fancyindex parser
│ └── Pure transfer planner
└── TransferManager (single worker thread)
├── LibtorrentAdapter (libtorrent 2.0.x bindings)
└── HttpDownloader (.part sidecar, Range resume)
Key invariants:
- Browser input never becomes a remote URL or filesystem path
- The planner decides transfer method before a job starts — no silent switching
- Only
TransferManagercalls the torrent adapter - A torrent is
Piece-verifiedonly after a successful final recheck
./scripts/package-macos.sh # unsigned .app
./scripts/package-macos.sh --sign # signed
./scripts/package-macos.sh --sign --notarize TEAM_ID # signed + notarized DMGRequires pip install pyinstaller and libtorrent available via Homebrew.
MIT — see pyproject.toml.
Third-party notices: libtorrent (BSD), Flask (BSD), httpx (BSD), platformdirs (MIT).