# Command line ``` ipx [--config PATH] [--local] ``` 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 [--folder X] [--keywords a,b]` | Subscribe; the id comes from the feed title | | `ipx rm ` | Unsubscribe; downloads and history are kept | | `ipx import ` / `ipx export ` | 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 | | `ipx reap [--dry-run]` | Run retention now | | `ipx user ` | 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. ```sh 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: ```sh read -s PW && echo -n "$PW" | ipx user passwd admin ``` ## Scanning ```sh 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 ```sh 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. **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 ```sh 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: ```sh 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 `). ## Talking to it directly ```sh printf '{"cmd":"fetch","force":true}\n' | socat - UNIX-CONNECT:$XDG_RUNTIME_DIR/ipx.sock ``` See [architecture.md](architecture.md#control-socket) for the protocol.