Skip to content

Latest commit

 

History

43 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

InfoCon Librarian

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.


What it does

  • 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 .torrent for 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

Requirements

Install (macOS)

brew install libtorrent-rasterbar python@3.12
pip3 install infocon-librarian

Or from source:

git clone https://github.com/h0ck3ystyx/InfoconSync
cd InfoconSync
python3 -m venv --system-site-packages .venv
.venv/bin/pip install -e .

Install (Windows)

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 libtorrent

Without 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.

Usage

# 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_ID

Privacy

Before 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.

Security

  • 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 Origin header, session cookie, and CSRF token
  • Torrent metadata paths are validated through SafeArchivePath before the engine sees them — .., absolute paths, empty components, and reserved platform names are all rejected
  • No unsafe-inline in the Content Security Policy; all UI assets are served from 'self'

Development

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 src

217 tests passing across unit, integration, and E2E layers. Coverage ≥ 90% for domain/planner/path code.

Architecture

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:

  1. Browser input never becomes a remote URL or filesystem path
  2. The planner decides transfer method before a job starts — no silent switching
  3. Only TransferManager calls the torrent adapter
  4. A torrent is Piece-verified only after a successful final recheck

Packaging (macOS)

./scripts/package-macos.sh           # unsigned .app
./scripts/package-macos.sh --sign    # signed
./scripts/package-macos.sh --sign --notarize TEAM_ID   # signed + notarized DMG

Requires pip install pyinstaller and libtorrent available via Homebrew.

License

MIT — see pyproject.toml.

Third-party notices: libtorrent (BSD), Flask (BSD), httpx (BSD), platformdirs (MIT).

About

Update local infocon mirror with updates from the source

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages