A step-by-step guide to setting up and using shelfctl.
- Go 1.21+ installed
- GitHub account
- GitHub personal access token (see Step 1 below)
shelfctl requires a GitHub personal access token (PAT) set in the SHELFCTL_GITHUB_TOKEN environment variable.
Classic PAT scopes:
repo- for private shelvespublic_repo- for public-only shelves
Fine-grained PAT permissions: Grant Contents (Read/Write) and Releases (Read/Write) on the shelf repos you manage.
Note: GitHub CLI (gh) is not required - shelfctl uses the GitHub REST API directly.
If you already have GitHub CLI installed and authenticated:
gh auth login
export SHELFCTL_GITHUB_TOKEN=$(gh auth token)Add to shell profile to persist:
# Bash
echo 'export SHELFCTL_GITHUB_TOKEN=$(gh auth token)' >> ~/.bashrc
# Zsh
echo 'export SHELFCTL_GITHUB_TOKEN=$(gh auth token)' >> ~/.zshrc- Visit https://github.com/settings/tokens
- Generate new token (classic or fine-grained)
- Select required scopes/permissions (see above)
- Copy the token (starts with
ghp_orgithub_pat_)
export SHELFCTL_GITHUB_TOKEN=ghp_your_token_hereAdd 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.
Choose the method that fits your platform:
Homebrew (macOS/Linux):
brew install blackwell-systems/tap/shelfctlInstall script (Linux/macOS):
curl -fsSL https://raw.githubusercontent.com/blackwell-systems/shelfctl/main/install.sh | shInstalls the latest release to /usr/local/bin. Override with INSTALL_DIR=~/bin or pin a version with VERSION=v0.4.4.
Go install:
go install github.com/blackwell-systems/shelfctl/cmd/shelfctl@latestDownload pre-built binary:
Download the appropriate binary for your platform from the releases page, extract, and move to your PATH.
winget (Windows):
winget install blackwell-systems.shelfctlVerify installation:
shelfctl --helpshelfctl creates its config automatically on first run. Just run:
shelfctlYou'll see:
No config found. Let's set up shelfctl.
GitHub username or org: your-username
Token env var name [SHELFCTL_GITHUB_TOKEN]:
Config created at ~/.config/shelfctl/config.yml
Enter your GitHub username or organization name, and press Enter to accept the default token environment variable name (or type a custom one).
Manual setup (optional): If you prefer to create the config manually,
copy config.example.yml:
mkdir -p ~/.config/shelfctl
cp config.example.yml ~/.config/shelfctl/config.yml
# Edit with your GitHub usernameSecurity: The token itself is never stored in the config file - only the environment variable name. shelfctl reads the token from your environment at runtime.
Let's create a shelf for programming books:
shelfctl init \
--repo shelf-programming \
--name programming \
--create-repoThis will:
- Create
shelf-programmingrepository on GitHub - Create the
libraryrelease tag - Add an empty
catalog.ymlto the repo - Add the shelf to your config file
Verify it worked:
shelfctl shelvesOutput:
[OK] programming (shelf-programming)
catalog: catalog.yml (0 books)
You can also run shelfctl doctor at any time to verify your entire setup (config, token, API connectivity, shelf access, cache).
Just run shelve with no arguments for a fully guided workflow:
shelfctl shelveThis launches an interactive TUI that:
- Lets you pick the shelf (if you have multiple)
- Shows a file browser to select your book
- Provides a form to fill in metadata (title, author, tags, ID)
- Uploads and catalogs automatically
Or provide all details via flags:
shelfctl shelve ~/Downloads/some-book.pdf \
--shelf programming \
--title "Introduction to Algorithms" \
--author "CLRS" \
--tags algorithms,textbook,cs \
--year 2009If you don't provide all metadata, shelfctl will prompt you.
What happened:
- The PDF was uploaded to GitHub as a release asset
- A catalog entry was created with metadata and SHA256 checksum
- The catalog was committed and pushed
Run browse in a terminal for an interactive browser:
shelfctl browse --shelf programmingThis shows a visual browser with:
- Keyboard navigation (↑/↓ or j/k)
- Live filtering (press
/to search) - Color-coded indicators ([OK] = cached)
Use --no-interactive or pipe output for scripts:
shelfctl browse --shelf programming --no-interactiveOutput:
programming/introduction-to-algorithms
Introduction to Algorithms
CLRS
tags: algorithms, textbook, cs
format: pdf, 12.4 MB
shelfctl info introduction-to-algorithmsThis shows full metadata, source location, cache status, and checksum.
shelfctl open introduction-to-algorithmsThis will:
- Download the book to your cache (if not already cached)
- Verify the checksum
- Open it with your system's default PDF viewer
The next time you open it, it will use the cached copy (no download).
You can also browse from the interactive TUI:
shelfctl browse
# Press 'o' on a book to open itCreate a visual web page to browse your library in any browser:
# Generate and open in one step
shelfctl index --open
# Or generate only
shelfctl indexThis generates ~/.local/share/shelfctl/cache/index.html with:
- Visual book grid with covers and metadata
- Real-time search/filter (no server needed)
- Click books to open them
- Works offline, no shelfctl needed
The --open flag launches the index in your default browser automatically (macOS, Linux, and Windows supported). Without it, the path is printed to the terminal.
The index shows only cached books, so download books first to include them.
Your local cache stores downloaded books at ~/.local/share/shelfctl/cache/. You can manage cache without affecting your shelf catalog or release assets:
# View cache statistics
shelfctl cache info
# View stats for specific shelf
shelfctl cache info --shelf programming
# Remove specific books from cache
shelfctl cache clear introduction-to-algorithms sicp
# Clear all cached books from a shelf
shelfctl cache clear --shelf programming
# Interactive picker (multi-select with spacebar)
shelfctl cache clear
# Clear entire cache (requires confirmation)
shelfctl cache clear --allIn browse TUI:
- Press
spaceto select multiple books - Press
xto remove selected books from cache - Books remain in your library, only local copies deleted
- Useful for reclaiming disk space
Books will automatically re-download when opened or browsed.
When you annotate or highlight PDFs in your reader, those changes are saved to your local cache. Sync them back to GitHub to preserve your work:
# Sync specific book (CLI)
shelfctl sync sicp
# Sync all modified books (CLI)
shelfctl sync --allIn browse TUI:
- Press
son a modified book to sync it - Or press
spaceto select multiple modified books, then presssto sync all - Status messages show progress during upload
- Books marked with modified indicator in cache info
Modified files are protected by default when clearing cache - you'll get a warning to sync first or use --force to delete anyway.
Create shelves for different topics:
# History shelf
shelfctl init --repo shelf-history --name history --create-repo --create-release
# Fiction shelf
shelfctl init --repo shelf-fiction --name fiction --create-repo --create-release
# Research papers
shelfctl init --repo shelf-research --name research --create-repo --create-releaseVerify:
shelfctl shelvesYou can add books directly from URLs:
shelfctl shelve https://example.com/paper.pdf \
--shelf research \
--title "Important Research Paper" \
--tags ai,ml \
--year 2024Use tags to organize books within a shelf:
# Add a book about Lisp
shelfctl shelve ~/Downloads/sicp.pdf \
--shelf programming \
--title "Structure and Interpretation of Computer Programs" \
--author "Abelson & Sussman" \
--tags lisp,functional,textbook
# Add a book about algorithms
shelfctl shelve ~/Downloads/algo-design.pdf \
--shelf programming \
--title "Algorithm Design" \
--author "Kleinberg & Tardos" \
--tags algorithms,graphs,textbookFilter by tag:
shelfctl browse --shelf programming --tag lisp
shelfctl browse --shelf programming --tag algorithmsCreate a shell script:
#!/bin/bash
for file in ~/Downloads/books/*.pdf; do
shelfctl shelve "$file" \
--shelf programming \
--title "$(basename "$file" .pdf)" \
--tags imported
doneThen review and update metadata later by editing catalog.yml directly.
Realized a book belongs in a different shelf?
shelfctl move book-id --to-shelf different-shelfYou can use releases within a shelf for sub-organization:
# Add to a specific year
shelfctl shelve book.pdf \
--shelf research \
--release 2024 \
--title "Recent Paper"
# Add to archive
shelfctl shelve old-book.pdf \
--shelf programming \
--release archive \
--title "Old Book"If you have books in another GitHub repo:
# Scan the old repo
shelfctl migrate scan --source your-username/old-books > queue.txt
# Edit queue.txt to add shelf mappings and metadata
# Migrate in batches
shelfctl migrate batch queue.txt --n 20 --continueThe --continue flag lets you resume if interrupted.
If a friend also uses shelfctl:
# Import their programming shelf into yours
shelfctl import \
--from their-username/shelf-programming \
--shelf imported-booksThis copies books from their catalog to yours (requires their repos to be public or you to have access).
The --id flag lets you control book IDs. Good practices:
- Use lowercase with hyphens:
effective-go - Keep them short but meaningful
- For academic papers, use citation keys:
smith2024-neural-nets - For ISBNs:
isbn-9780262033848
Set up autocompletion for faster command typing:
# Bash (add to ~/.bashrc)
source <(shelfctl completion bash)
# Zsh (add to ~/.zshrc)
source <(shelfctl completion zsh)
# Fish
shelfctl completion fish > ~/.config/fish/completions/shelfctl.fishThis enables tab completion for commands, flags, and shelf names.
Add to your shell profile:
alias shelfadd='shelfctl shelve'
alias shelflist='shelfctl browse'
alias shelfopen='shelfctl open'Edit catalog.yml directly for bulk updates. It's just YAML:
- id: book-id
title: "The Book Title"
author: "Author Name"
year: 2024
tags:
- tag1
- tag2
format: pdf
checksum:
sha256: abc123...
size_bytes: 1048576
source:
type: github_release
owner: you
repo: shelf-programming
release: library
asset: book-id.pdf
meta:
added_at: "2024-01-15T10:30:00Z"After editing, commit and push manually or use shelfctl to add more books (it will merge correctly).
Your ~/.config/shelfctl/config.yml is small and important:
cp ~/.config/shelfctl/config.yml ~/Dropbox/shelfctl-config-backup.ymlBy default, repos can be private. Just ensure your GitHub token has access.
# On GitHub, make shelf-private-docs private via repo settingsshelfctl works identically with private repos.
- Read commands.md for complete command reference
- Read architecture.md for schemas and configuration reference
- Check troubleshooting.md if you hit issues
- Contribute improvements via contributing.md
# 1. Authenticate with GitHub (choose one)
# Option A: Using gh CLI (easier)
gh auth login
export SHELFCTL_GITHUB_TOKEN=$(gh auth token)
# Option B: Manual token
# export SHELFCTL_GITHUB_TOKEN=ghp_your_token_here
# 2. Install
go install github.com/blackwell-systems/shelfctl/cmd/shelfctl@latest
# 3. Run shelfctl (auto-creates config)
shelfctl
# Follow the prompts: enter your GitHub username, accept default token env var
# 4. Create shelf
shelfctl init --repo shelf-books --name books --create-repo --create-release
# 5. Add a book
shelfctl shelve ~/book.pdf --shelf books --title "My Book" --tags reading
# 6. Open it
shelfctl open my-book
# Done!