diff --git a/CHANGELOG.md b/CHANGELOG.md index 908a80e..0ffaf73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Add a feed no longer asks for a folder; a feed's folder is its title (#201). +- `docker-compose.yml` is a sample to start from, ipx alone on SQLite with its folders beside the file, instead of a copy of the author's server (#208). ## [0.12.0] - 2026-10-11 diff --git a/CLAUDE.md b/CLAUDE.md index b5f09ae..896925c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -7,18 +7,20 @@ made here. ## Where things are Production is the `iPX` container on Tower (192.168.1.130), the `ipx` service of the -Arcane project `content`: `/mnt/fast/arcane/projects/content/compose.yaml`. That file is what runs; -`docker-compose.yml` in this repo is a copy, and editing it changes nothing in production. +Arcane project `ipx`: `/mnt/fast/arcane/projects/ipx/compose.yaml`. That file is what runs, and +the containers of the split (#207) join it there. `docker-compose.yml` in this repo is a sample for +others, ipx alone on SQLite; editing it changes nothing in production. Until 2026-10-12 ipx was a +service of the `content` project, beside audiobookshelf, immich and Plex (#208). | | Host | In the container | |---|---|---| | Image | `192.168.1.130:5000/ipx:latest` | | | Config | `/mnt/fast/appdata/ipx/config.toml`: bind address, token, trusted proxies, torrent, paths. The feeds and server settings are in the database | `/config/config.toml` | -| Database | Postgres 18, database `ipodderx`, login `ipodderx`, on the `postgres` container of the Arcane project `databases` (`192.168.1.130:5433`). The URL is in `ipx.env` beside the compose file (`/mnt/fast/arcane/projects/content/ipx.env`, mode 600), passed to the container as `IPX_DATABASE_URL`. A relative `env_file`: Arcane runs compose in its own container, where `/mnt/fast/appdata` does not exist | | +| Database | Postgres 18, database `ipodderx`, login `ipodderx`, on the `postgres` container of the Arcane project `databases` (`192.168.1.130:5433`). The URL is in `ipx.env` beside the compose file (`/mnt/fast/arcane/projects/ipx/ipx.env`, mode 600), passed to the container as `IPX_DATABASE_URL`. A relative `env_file`: Arcane runs compose in its own container, where `/mnt/fast/appdata` does not exist | | | Old database | `/mnt/user/ipx/state.db`, SQLite, used until the move to Postgres on 2026-09-18 and kept for rollback | `/data/state.db` | | Downloads | `/mnt/user/ipx/downloads` | `/downloads` | | Web UI | `192.168.1.130:8099`, also `ipodderx.sdf1.net` via a Cloudflare tunnel | `0.0.0.0:8099` | -| Sign-in via the tunnel | Cloudflare Access app `ipodderx`, with Authentik as its identity provider; see [docs/sso.md](docs/sso.md) | trusts `Cf-Access-Authenticated-User-Email` from `192.168.16.1`, the `content_default` gateway, and only with a valid `Cf-Access-Jwt-Assertion` (`access_team`, `access_aud`) | +| Sign-in via the tunnel | Cloudflare Access app `ipodderx`, with Authentik as its identity provider; see [docs/sso.md](docs/sso.md) | trusts `Cf-Access-Authenticated-User-Email` from `192.168.64.1`, the `ipx_default` gateway (its subnet pinned in the compose file), and only with a valid `Cf-Access-Jwt-Assertion` (`access_team`, `access_aud`) | Work to do lives in the Gitea issues at https://git.sdf1.net/rays/ipx/issues, not in a `TODO.md`. `/src/tea` is logged in: `/src/tea issues list --login git.sdf1.net --repo rays/ipx`. @@ -47,13 +49,13 @@ Deploying a change is: build and push the image, then pull it and recreate the c ```sh docker buildx build --tag 192.168.1.130:5000/ipx:latest . --push -docker compose -f /mnt/fast/arcane/projects/content/compose.yaml pull ipx -docker compose -f /mnt/fast/arcane/projects/content/compose.yaml up -d ipx +docker compose -f /mnt/fast/arcane/projects/ipx/compose.yaml pull ipx +docker compose -f /mnt/fast/arcane/projects/ipx/compose.yaml up -d ipx docker logs --tail 20 iPX ``` -**Name the service.** A bare `up -d` recreates every container in `content`, beets and immich -included. Run `pull` before `up`, because `up` reuses whatever `latest` the host already has. +**Name the service.** A bare `up -d` recreates every container in the project, which once the +split lands means the frontend and directory too. Run `pull` before `up`, because `up` reuses whatever `latest` the host already has. **A build that fails with `429 Too Many Requests` on a base image** is Docker Hub rate-limiting this host. There is no Docker Hub login here, and the build asks about `debian:trixie-slim` and diff --git a/README.md b/README.md index a985fa3..c649fa7 100644 --- a/README.md +++ b/README.md @@ -34,8 +34,8 @@ docker build -t ipx . docker compose up -d ``` -`docker-compose.yml` is set up for the author's own server. Point its `image` and its three volumes -(`/config`, `/data` and `/downloads`) at yours first. The UI is on port 8099. BitTorrent uses 6881 +`docker-compose.yml` is a sample: ipx alone, with its database in SQLite and its three volumes +(`/config`, `/data` and `/downloads`) in folders beside the file. 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: diff --git a/docker-compose.yml b/docker-compose.yml index 863c03b..7ebfc4a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,31 +1,26 @@ +# A sample: ipx on its own, keeping everything in SQLite. Build the image first +# (`docker build -t ipx .`), then `docker compose up -d`, and sign in at http://:8099 +# as admin / ipodderx. docs/configuration.md has the rest, Postgres included. services: ipx: - image: 192.168.1.130:5000/ipx:latest - container_name: iPX + image: ipx + container_name: ipx restart: unless-stopped environment: + # Who owns the files it writes. 99:100 is Unraid's nobody:users. PUID: "99" PGID: "100" - TZ: "America/Toronto" - IPX_LOG: "ipx=info" - # One JSON object a line, which Loki (through Alloy) and the Grafana dashboard read. - IPX_LOG_FORMAT: "json" - # Traces to Tempo, in the monitoring project; ipx sends none without it. - OTEL_EXPORTER_OTLP_ENDPOINT: "http://192.168.1.130:4318" - # What the dashboard filters on, so a daemon run by hand for testing stays out of it. - OTEL_RESOURCE_ATTRIBUTES: "deployment.environment.name=production" - # IPX_DATABASE_URL=postgres://... to use Postgres; without it, /data/state.db (SQLite). - env_file: - - ipx.env # relative: Arcane resolves it inside its own container + TZ: "UTC" + # For Postgres instead of SQLite: + # IPX_DATABASE_URL: "postgres://ipx:password@db-host:5432/ipx" ports: - "8099:8099" # web UI - - "192.168.1.130:9187:9187" # /metrics for Prometheus, LAN only (ipx#194) - "6881:6881/tcp" # BitTorrent peers - "6881:6881/udp" # DHT volumes: - - /mnt/fast/appdata/ipx:/config # config.toml, and the web token - - /mnt/user/ipx/:/data # state.db - - /mnt/user/ipx/downloads:/downloads + - ./config:/config # config.toml, written on first start + - ./data:/data # state.db + - ./downloads:/downloads healthcheck: test: ["CMD", "ipx", "status"] interval: 30s diff --git a/docs/sso.md b/docs/sso.md index 210702c..bf7d35c 100644 --- a/docs/sso.md +++ b/docs/sso.md @@ -38,7 +38,7 @@ request it forwards through the tunnel, and ipx signs that person in. enabled = true bind = "0.0.0.0:8099" trusted_header = "Cf-Access-Authenticated-User-Email" -trusted_proxies = ["127.0.0.1", "::1", "192.168.16.1"] +trusted_proxies = ["127.0.0.1", "::1", "192.168.64.1"] access_team = "rays-sdf1.cloudflareaccess.com" access_aud = "8bfe73dfbc8c548d1cb5dc11c6db6887bcaf4f5144840396f83a620a140e1c4f" auto_create_users = true @@ -50,7 +50,7 @@ The last two turn on the token check described under [Verifying Cloudflare's tok on since 2026-09-19. Both can be read without the dashboard: a request to the site while signed out is sent to `https:///cdn-cgi/access/login/ipodderx.sdf1.net?kid=&...`. -Restart ipx after editing it: `docker compose -f /mnt/fast/arcane/projects/content/compose.yaml +Restart ipx after editing it: `docker compose -f /mnt/fast/arcane/projects/ipx/compose.yaml restart ipx`. ### What was missing @@ -65,22 +65,24 @@ Cloudflare and Authentik were already right. Three things on the ipx side were n `rays@sdf1.net`. With `auto_create_users` on, the first visit would have made a second, empty account. `ipx user rename rays rays@sdf1.net` fixed that without losing anything. -### The address to trust, and why it is 192.168.16.1 +### The address to trust, and why it is 192.168.64.1 `cloudflared` runs in its own container and reaches ipx through the host's published port. Docker (iptables firewall backend) masquerades traffic between its bridge networks, so the tunnel's -requests arrive from the **gateway of ipx's own network**, `content_default`: +requests arrive from the **gateway of ipx's own network**, `ipx_default`: ```sh -docker network inspect content_default -f '{{range .IPAM.Config}}{{.Gateway}}{{end}}' +docker network inspect ipx_default -f '{{range .IPAM.Config}}{{.Gateway}}{{end}}' ``` That was measured, not assumed. ipx does not log where a request came from, so the addresses were read from the kernel's connection table inside the container while the site was open. (`/proc/net/tcp` lists them in hex.) -If the `content` project's network is ever recreated, its gateway can change. Check it again, and -update `trusted_proxies` to match. +The `ipx` project's compose file pins that network's subnet to `192.168.64.0/20`, so its gateway +stays `192.168.64.1` when the network is recreated. It was `192.168.16.1`, `content_default`'s, while +ipx was part of the `content` project. If the subnet is ever changed, update `trusted_proxies` to +match. ### Names @@ -176,7 +178,7 @@ itself, with a **Proxy Provider** and an **outpost** that adds `X-authentik-user **The header is only believed from `trusted_proxies`.** Every other source is ignored, and the request falls through to a session cookie or the shared token. That is the whole security boundary. -With the tunnel reaching ipx through the host's port, `192.168.16.1` means **any container on Tower +With the tunnel reaching ipx through the host's port, `192.168.64.1` means **any container on Tower that connects to `192.168.1.130:8099`**, not only `cloudflared`. Machines on the LAN, and Tower itself, arrive under their own addresses and cannot set the header; the checks above show both sides. Never list a LAN address or range: anyone there could then send