shelfctl is a personal library manager designed to organize PDFs and books using GitHub Release assets instead of git commits. It provides an efficient way to manage your digital library by storing metadata in a catalog.yml file while keeping documents as release assets, ensuring clean git history.
Key Features:
Stores files as GitHub Release assets to avoid bloat in git history.
On-demand downloading of individual books without cloning the entire repository.
Migration tools to split and reorganize existing collections into topic-based shelves.
Static HTML index for web-based library viewing with tag filters and search.
Interactive terminal UI (TUI) and CLI interface for browsing, caching, annotating, and syncing.
Audience & Benefit: Ideal for developers and GitHub users who want to organize their PDFs and books without bloating repositories. It offers a clean, efficient way to manage your library across devices while keeping costs low by leveraging free GitHub storage limits.
Installation via winget is available for Windows users.
README
shelfctl
Meet Shelby, your friendly library assistant.
Organize the PDFs and books you already have scattered across GitHub.
If you use GitHub, you've probably hit this: one monolithic "books" repo with hundreds of PDFs, or files scattered across random repos and gists. Eventually you hit GitHub's 100MB limit, or you bolt on Git LFS and discover it's expensive and annoying for a personal library.
Once PDFs land in git history, every clone stays heavy forever. Even after you delete the files, git history never forgets.
shelfctl solves this by storing files as GitHub Release assets (not git commits) and keeping only metadata in a simple catalog.yml. That means you can split a bloated repo into topic-based shelves, migrate books out of existing repos, and search + download on demand (without moving your library to a new service).
> [!WARNING]
> Deleting a PDF from a git repo doesn't remove it from git history. Clones still carry the weight. shelfctl avoids this entirely by storing documents as Release assets (only metadata is versioned).
Your GitHub account already gives you reliable distribution and storage primitives. shelfctl turns them into a library:
Release assets for the PDFs/EPUBs
catalog.yml for searchable metadata
one repo per shelf (shelf-programming, shelf-history, …)
Migration tools to split bloated repos and reorganize existing collections
> [!TIP]
> Already have PDFs committed in git? shelfctl can scan and migrate them into shelves without manual re-uploads:
> bash > shelfctl migrate scan --source you/old-books-repo > queue.txt >
Three ways to browse your library:
Interactive TUI - visual browser with keyboard navigation and search
Static HTML index - web-based viewer with tag filters and search (works offline)
Scriptable CLI - pipe, filter, automate, and integrate with shell workflows
Your library stays portable, backed by normal git repos. Free by default (only pay if you choose Git LFS or exceed GitHub plan limits).
30-Second Quickstart
# 1. Set up authentication (create token at github.com/settings/tokens)
export SHELFCTL_GITHUB_TOKEN=ghp_your_token_here
# 2. Create your first shelf (auto-creates config on first run)
shelfctl init --repo shelf-books --name books --create-repo
# 3. Add a book
shelfctl shelve ~/Downloads/book.pdf --shelf books
# 4. Browse your library
shelfctl browse
That's it! Your book is now stored as a GitHub Release asset with searchable metadata.
Why not commit PDFs?
The tradeoff:
Approach
Git History
Clone Weight
Per-File Download
Cost
Git commit
Bloats forever
Heavy (even after deleting files)
Possible, but awkward
Free
Git LFS
Clean
Still heavier than needed
Possible, but awkward
Paid storage/bandwidth
Release asset
Clean
Light
Yes (native)
Free*
*Free by default. You only pay if you choose LFS or exceed GitHub plan limits.
Why Release assets:
Git history stays clean (only metadata is versioned)
Documents live outside version control entirely
Download individual files on-demand from GitHub's CDN
On-demand downloads (no cloning)
Fetch a single book without cloning a repo or pulling a whole archive.
shelfctl open downloads only that file from GitHub's CDN and opens it. Your library can be huge, but you only download what you actually read.
Annotations and highlights sync
When you annotate or highlight PDFs in your reader, those changes are saved to your local cache. Use shelfctl sync to upload your annotated version back to GitHub (replaces the original, no versioning). From the TUI hub, select Sync Modified from the Cache menu (only appears when modified books are detected) to sync all at once. You can also press s in the browse view to sync the current or selected books. Your annotations stay with the book and sync across machines.
Features
Release assets backend (no git history bloat)
Files are stored as GitHub Release assets, not git commits. Your repository stays lightweight and git history never bloats.
On-demand open (per-file download)
Download and open a single book without cloning the entire repository. Your library can be huge, but you only fetch what you need.
Migration tools: scan → split → migrate
Built-in tools to scan existing repos, reorganize into topic-based shelves, and migrate files automatically. No manual re-uploads required.
Static HTML index
Generate a web-based library viewer with cover thumbnails, tag filters, and live search. Works offline in any browser without running shelfctl.
TUI + CLI
shelfctl (no arguments) launches an interactive visual hub. shelfctl --help lists all CLI commands. Every command supports --json output and --no-interactive for scripting and automation — the two modes share the same feature set.
One repo per topic shelf
Create shelf repos like shelf-programming, shelf-history, etc.
Books live in Releases, not git history
PDFs/EPUBs are uploaded as GitHub Release assets (not committed to the repo).
catalog.yml is the source of truth
Each shelf repo contains a catalog.yml that stores searchable metadata and maps book IDs to release assets. Only metadata is versioned; the actual documents live outside git history.
On-demand, per-book downloadsshelfctl open downloads only that one file from GitHub's CDN and opens it.
Full lifecycle management
shelfctl supports the workflow end-to-end: add, get, open, migrate, split, and more.
Shelf: A GitHub repository (e.g. shelf-programming)
Release: A storage container tag (default: "library", NOT a version release)
Assets: Your PDF/EPUB files attached to the release
catalog.yml: Searchable metadata file (tracked in Git) mapping book IDs to assets
Your books live as GitHub Release assets (outside Git history). Only metadata is versioned.
This keeps repos lightweight and enables per-file on-demand downloads.
Prerequisites
GitHub account with a personal access token
Go 1.21+ (only if using go install or building from source)
Add to shell profile to persist (~/.bashrc or ~/.zshrc).
API Rate Limits: GitHub's authenticated API allows 5,000 requests/hour. shelfctl caches downloaded book files locally; metadata is fetched from GitHub as needed. For typical personal library usage, you're unlikely to hit rate limits.
Optional: PDF Cover Thumbnails
For automatic cover extraction from PDFs, install poppler:
# Add a book (creates the shelf automatically if it doesn't exist)
shelfctl shelve ~/Downloads/sicp.pdf --shelf programming --title "SICP" --author "Abelson & Sussman" --tags lisp,cs
# List books across all shelves
shelfctl browse --shelf programming
# Open a book - downloads just this one file (6MB), not the entire release
shelfctl open sicp
# On another machine? Same command fetches it on-demand from GitHub
shelfctl open sicp
# Add annotations/highlights, then sync back to GitHub
open sicp # Annotate in your PDF reader
shelfctl sync sicp # Upload annotated version (replaces original, no versioning)
Already have PDFs committed in a repo? Reorganize them:
# (Ensure SHELFCTL_GITHUB_TOKEN is set - see Authentication section above)
# Scan your existing repos for files
shelfctl migrate scan --source you/old-books-repo > queue.txt
# Create organized shelves (private by default)
shelfctl init --repo shelf-programming --name programming --create-repo --create-release
shelfctl init --repo shelf-research --name research --create-repo --create-release
# Or make a shelf public
shelfctl init --repo shelf-public --name public --create-repo --create-release --private=false
# Edit queue.txt to map files to shelves, then migrate
shelfctl migrate batch queue.txt --n 10 --continue
HTML Library Index
Generate a static HTML page from your cached books — no server required. Open it in any browser for a visual, searchable view of your library that works completely offline.
shelfctl index --open
Visual book grid with cover thumbnails
Real-time search by title, author, or tags
Clickable tag filters with book counts
Sort by recently added, title, author, or year
Organized by shelf — click any book to open it locally
Commands
Command
Description
init
Bootstrap a shelf repo and release
shelves
Validate all configured shelves
delete-shelf
Remove a shelf from configuration
browse
Browse your library (interactive TUI or text)
index
Generate local HTML index for web browsing
search
Search books by title, author, or tags
status
Show library sync status and statistics
tags
List all tags with counts, rename tags in bulk
verify
Detect catalog vs release mismatches, auto-fix with --fix
sync
Upload locally modified books (annotations/highlights) to GitHub
cache clear
Remove books from local cache without deleting from shelves
cache info
Show cache statistics and disk usage
info
Show metadata and cache status
open
Open a book (auto-downloads if needed)
shelve
Add a book to your library
edit-book [id]
Update metadata for one or multiple books (batch mode)
Security: The token itself is never stored in the config file - only the environment variable name. shelfctl reads the token from your environment at runtime.
TUI Architecture - Technical deep dive into the unified TUI design
Design Philosophy
shelfctl is domain-specific by design: it solves the PDF/EPUB library problem and nothing else. This narrow focus keeps it simple, maintainable, and excellent at what it does.
⚖️ Disclaimer
shelfctl is a specialized management tool designed to help developers organize their personal document libraries using the GitHub API. By using this software, you agree to the following:
Platform Compliance: You are solely responsible for ensuring that your use of GitHub as a storage backend complies with GitHub's Terms of Service and Acceptable Use Policies. The author(s) of shelfctl are not responsible for any account suspensions or data removal by GitHub.
Content Responsibility: shelfctl does not provide, host, or distribute any content. Users are responsible for ensuring they have the legal right to store and distribute any files they upload to their own GitHub repositories and releases.
Shelby Mascot & Brand Assets: You may redistribute unmodified brand assets with shelfctl. Brand assets are not licensed for reuse outside shelfctl. See assets/LICENSE for details.