PROGRESS.md becomes CHANGELOG.md with the finished step lists moved to an appendix. The README is an overview pointing at docs/: configuration, cli, users, sso (refreshed for accounts and admin-only settings), and architecture. CLAUDE.md collects what working on this code actually requires -- pkill -x not -f, the page being compiled in, the dead columns on entries, the Playwright worker that deleted its own database. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AdXho5tTkjFLeUXKbEjKBh
3.3 KiB
Command line
ipx [--config PATH] [--local] <command>
Every command that has a wire form probes the control socket first: if a daemon is running, the
daemon does the work and the CLI just renders the events it streams back. That is deliberate — two
processes must never download the same thing. --local forces the work to happen in-process.
| Command | What it does |
|---|---|
ipx list |
Subscriptions and their state |
ipx status |
Counts: feeds, pending, downloaded |
ipx fetch [FEED] [--force] |
Scan everything, or one feed. --force ignores the TTL |
ipx add <url> [--folder X] [--keywords a,b] |
Subscribe; the id comes from the feed title |
ipx rm <feed> |
Unsubscribe; downloads and history are kept |
ipx import <file.opml> / ipx export <file.opml> |
Move subscriptions in or out |
ipx reap [--dry-run] |
Run retention now |
ipx user <add|list|passwd|rm> |
Accounts for the web UI |
ipx daemon [--web ADDR] |
Scheduler, control socket and web UI |
Accounts
Passwords are read from stdin, so they miss the shell history and any ps listing.
echo -n 'a good password' | ipx user add ray # local account
ipx user add ray@example.com --no-password # signs in through the proxy only
echo -n 'a good password' | ipx user passwd admin # change a password
ipx user list # who exists, and how each signs in
ipx user rm sam # account, subscriptions and read state
The first account created is an admin; later ones are ordinary users. A database with no accounts at all gets admin / ipodderx on the next daemon start, announced in the log — change it.
To avoid even the command line, read it interactively:
read -s PW && echo -n "$PW" | ipx user passwd admin
Scanning
ipx fetch # everything due
ipx fetch atp --force # one feed, ignoring its TTL and schedule
A scan: conditional GET (If-None-Match / If-Modified-Since), parse, record new entries, apply
the filters, then download up to the per-scan cap, newest first. A feed nothing has changed in
answers 304 and costs one request.
Retention
ipx reap --dry-run # what would go, oldest first
ipx reap # actually delete
Files are deleted to get back under max_total_gb, oldest first, and items past max_age_days
with no file are pruned from the database. Starred by anyone keeps a file, and one only counts
as read when everyone subscribed has read it. The enclosure row survives as reaped, which is what
stops the next scan fetching it again.
The daemon
ipx daemon # scheduler + socket + web UI
ipx daemon --web 0.0.0.0:8099 # override the configured bind for one run
One daemon per socket; a second refuses to start rather than fight over the database. It shuts down cleanly on SIGTERM, including mid-download.
To kill it, match the binary exactly:
pkill -x ipx
pkill -f ipx matches the shell running the command too, and kills your own session.
Talking to it directly
printf '{"cmd":"fetch","force":true}\n' | socat - UNIX-CONNECT:$XDG_RUNTIME_DIR/ipx.sock
See architecture.md for the protocol.