The page and the service worker were strict TypeScript; the tests, the build, the home page's generator, the load tests, boot.js and the directory's site.js were plain JavaScript that nothing checked. All of it is TypeScript now, in five programs, each with its own globals, all checked by npm run typecheck: the page (tsconfig.json, with boot.ts and the directory's site.ts), the worker, Node's (tsconfig.tools.json: the Node tests, the browser suite, the build, the site generator, the load harness), and k6's (tsconfig.k6.json, with @types/k6). Node 24 runs the .ts files by stripping their types, so they hold only TypeScript that erases (erasableSyntaxOnly) and import each other by .ts name; package.json says "type": "module" for it, and Dockerfile.web moves to Node 24 to run build.mts. k6 2.3 strips its scripts' types itself. The browser suite's page.evaluate calls reach the page's globals; the page's tsc run now also writes its declarations to target/page-types, which the suite's program includes, so those calls are typed by the page's own code rather than by hand-written copies. boot.ts is compiled by build.mts with swc, as the worker is, and named by the hash of what is served. The directory's build has no Node, so site.js is compiled from site.ts and committed (node web/build.mts --directory-script), and tests/directory-script.ts fails if they drift. Checked: the site generator's 139 pages are byte for byte what the JavaScript made; the page build's app.js is identical and only boot.js (now minified) and its hash differ; contrast, page-smoke, page-unit, native-bridge, the browser suite (96 passed, 2 skipped) and the directory's and ipx's Rust tests pass; k6 parses each load script. The load tests themselves did not run: they build a release binary outside the capped builder. .agents/skills/security-audit is an installed skill's, not ours, and is left as it is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
88 lines
4.0 KiB
Markdown
88 lines
4.0 KiB
Markdown
# ipx
|
|
|
|
A self-hosted podcatcher for a household. It checks your feeds, downloads the episodes, and serves
|
|
a web UI modelled on the 2004 Mac app **iPodderX**, for any number of people sharing one copy of
|
|
the files. One Rust binary, `ipx`, is both the daemon and the command line.
|
|
|
|
It is a rewrite of [ipodderx-core](https://git.sdf1.net/rays/ipodderx-core), the Python engine
|
|
behind iPodderX (2004-2008, Ray Slakinski & August Trometer).
|
|
|
|
## What it does
|
|
|
|
- **The web UI.** It has a toolbar, and a feed list that opens with the Directory, Currently Listening and All
|
|
Subscriptions. Items sit in a sortable table with a Files pane, and there is a player bar. It
|
|
comes in Dark, Light and Classic themes, and works on a phone.
|
|
- **Several people, one copy.** Each person has their own subscriptions and their own read, pinned
|
|
and playback state. There is one file on disk per episode, however many people want it. People
|
|
sign in with a password or through a proxy (Cloudflare Zero Trust or Authentik), and admins
|
|
manage accounts and settings.
|
|
- **Scanning.** Feeds are checked on a schedule, globally or per feed, and a feed's own TTL is
|
|
honoured. Keyword, explicit-content and media-type filters decide what is downloaded, with a limit
|
|
on how many of a feed's newest episodes are downloaded.
|
|
- **Downloads.** Files come over HTTP or BitTorrent and are filed into a folder per feed.
|
|
Retention deletes the oldest files to stay under a disk quota or an age limit, and never touches
|
|
an item someone has pinned.
|
|
- **OPML.** You can import and export your own subscriptions. You can also subscribe to an OPML
|
|
URL, which keeps a whole list in step as a folder.
|
|
|
|
## Run it
|
|
|
|
With Docker:
|
|
|
|
```sh
|
|
docker build -t ipx .
|
|
docker build -f Dockerfile.web -t ipx-web .
|
|
docker compose up -d
|
|
```
|
|
|
|
`docker-compose.yml` is a sample: ipx, with its database in SQLite and its three volumes
|
|
(`/config`, `/data` and `/downloads`) in folders beside the file, and ipx-web, the page, served by
|
|
Caddy in front of it. Change them to suit. The UI is on port 8099. BitTorrent uses 6881
|
|
over TCP and UDP. Files are written as `PUID`/`PGID`, 99:100 by default.
|
|
|
|
From source:
|
|
|
|
```sh
|
|
cargo build --release
|
|
./target/release/ipx daemon
|
|
```
|
|
|
|
The first start creates **admin** with a random password, written to `admin-password` beside
|
|
config.toml (readable by ipx's user alone; the log says where, never what it is). Set
|
|
`IPX_ADMIN_PASSWORD` to choose it instead. Sign in at `/login`, then change it:
|
|
|
|
```sh
|
|
echo -n 'a good password' | ipx user passwd admin
|
|
```
|
|
|
|
The UI is plain HTTP, so put TLS in front of it if it is reachable from outside your network.
|
|
|
|
## Documentation
|
|
|
|
| | |
|
|
|---|---|
|
|
| [docs/configuration.md](docs/configuration.md) | Every config key, path and environment variable |
|
|
| [docs/cli.md](docs/cli.md) | Every command, including `ipx user` |
|
|
| [docs/users.md](docs/users.md) | Accounts, and what several people share |
|
|
| [docs/sso.md](docs/sso.md) | Signing in through Cloudflare Zero Trust or Authentik |
|
|
| [docs/architecture.md](docs/architecture.md) | How it works: modules, schema, control socket, HTTP API |
|
|
| [crates/ipx/CHANGELOG.md](crates/ipx/CHANGELOG.md), [web/CHANGELOG.md](web/CHANGELOG.md), [crates/directory/CHANGELOG.md](crates/directory/CHANGELOG.md) | What changed, by release, for ipx, the page and the directory |
|
|
| [CLAUDE.md](CLAUDE.md) | Notes for working on the code, including how production is deployed |
|
|
|
|
## Tests
|
|
|
|
```sh
|
|
cargo test # the engine: parsing, filters, retention, schedules, SQL, per-user state
|
|
node tests/page-smoke.ts # the page script loads without throwing
|
|
node tests/native-bridge.ts # the page hands playback to a native shell
|
|
npx playwright test # a real browser against a real daemon on fixture feeds
|
|
```
|
|
|
|
`npm install` gets the test runner, and `npx playwright install --with-deps chromium` gets the
|
|
browser.
|
|
|
|
## License
|
|
|
|
MIT, see [LICENSE](LICENSE). The icons are [Font Awesome Free](https://fontawesome.com) 7.3.1 by
|
|
@fontawesome, under [CC BY 4.0](https://fontawesome.com/license/free), embedded as SVG.
|