ipx-tui (crates/tui) is iPX's reader in ratatui: your feeds, a feed's items, an item's text as plain text, read and flagged as on the page, through the API with an API token. It installs as `ipx`, so the daemon's binary is now `ipxd`: the Dockerfile, release script, tests and every doc and message that names its commands follow. Naming the binary ipxd alone renamed its crate, and with it every log target (ipx::scan became ipxd::scan): the default filters are ipx=info, so nearly every line would have been dropped and the dashboard's queries on the targets gone blank. The daemon's code is now a library still called ipx, under a five-line ipxd main.rs, and the targets are as they were. ratatui is taken without its default features: 32 new packages in place of 78. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RqkrVDqdiQEMWAkNGQ2tYA
4.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 |
|---|---|
ipxd list |
Subscriptions and their state |
ipxd status |
Counts: feeds, pending, downloaded |
ipxd fetch [FEED] [--force] |
Scan everything, or one feed. --force ignores the TTL |
ipxd add <url> [--folder X] [--keywords a,b] [--list] [--category C] |
Subscribe; the id comes from the feed title. --list subscribes no one but keeps it in the catalogue, and so in what this server shares with the directory, when its last subscriber leaves; --category is the category the directory files it under when the feed names none. Run for a feed already in the catalogue, these list it or set its category |
ipxd rm <feed> |
Unsubscribe; downloads and history are kept |
ipxd import <file.opml> / ipxd export <file.opml> |
Move subscriptions in or out. Import subscribes the first admin, as the shared web token does; in the web UI it subscribes whoever is signed in |
ipxd reap [--dry-run] |
Run retention now |
ipxd user <add|list|passwd|rm> |
Accounts for the web UI |
ipxd daemon [--web ADDR] |
Scheduler, control socket and the HTTP API (the page is ipx-web's) |
add, rm and import change the catalogue in a process of their own. With a daemon running they
then tell it to read the catalogue again; without that it kept its own copy and wrote it back at
its next change, undoing them. If it does not answer they say so: restart it.
Accounts
Passwords are read from stdin, so they miss the shell history and any ps listing.
echo -n 'a good password' | ipxd user add ray # local account
ipxd user add ray@example.com --no-password # signs in through the proxy only
echo -n 'a good password' | ipxd user passwd admin # change a password; signs the account out everywhere
ipxd user list # who exists, and how each signs in
ipxd 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 on the next daemon start, with a random password in admin-password beside
config.toml (or IPX_ADMIN_PASSWORD), announced in the log — change it.
To avoid even the command line, read it interactively:
read -s PW && echo -n "$PW" | ipxd user passwd admin
Scanning
ipxd fetch # everything due
ipxd 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
ipxd reap --dry-run # what would go, oldest first
ipxd 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. An item anyone kept keeps its 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
ipxd daemon # scheduler + socket + HTTP API
ipxd 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. On a machine
that also runs ipx in a container, pkill -x ipx stops that one as well, since the host sees a
container's processes: stop the one you started by its PID instead (kill <pid>).
Talking to it directly
printf '{"cmd":"fetch","force":true}\n' | socat - UNIX-CONNECT:$XDG_RUNTIME_DIR/ipx.sock
See architecture.md for the protocol.