diff --git a/CLAUDE.md b/CLAUDE.md index 96bd452..7d400ba 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -81,7 +81,7 @@ subdomain. and the Warhammer RSS feed (09:10). ### Docker role categories (`roles/docker/tasks//`) -- **infrastructure/**: Caddy, Pocket ID, Gondulf (IndieAuth), Dockge +- **infrastructure/**: Caddy, Pocket ID, Dockge - **development/**: Gitea (+ act_runner), ByteStash - **media/**: Audiobookshelf, Kavita, Karakeep (files named `hoarder`), Manyfold - **productivity/**: Paperless-ngx, Baikal, Syncthing, HeyForm, Dawarich, Obsidian LiveSync, Gokapi @@ -127,11 +127,11 @@ user (`vault_aws_dns`). It also deletes the records listed under ### SSO (Pocket ID) Pocket ID (`id.thesatelliteoflove.com`) replaced Authentik on 2026-10-31. Apps that log in with it: Gitea, ByteStash, Audiobookshelf, Kavita, Karakeep, -Manyfold, Paperless-ngx, Gokapi, GoToSocial. Some read the client from env +Manyfold, Paperless-ngx, Gokapi, GoToSocial, StarPunk. Some read the client from env vars; Gitea, Audiobookshelf and Kavita store it in their own database (set in their admin UI). OIDC clients are created in Pocket ID's UI and their ID and -secret go in the vault as `vault_.pocketid`. StarPunk logs in with -IndieAuth through Gondulf instead. +secret go in the vault as `vault_.pocketid`. StarPunk (v2.0.0+) also +runs its own IndieAuth server so Micropub apps can post; Gondulf was retired. ### Shared Docker Network (`lava`) Created by the docker role as `{{ docker.subnet }}` with `ip_range: {{ docker.ip_range }}`. Docker only hands out dynamic addresses from that range, so `172.20.0.x` is reserved for fixed IPs: Caddy (`docker.hairpin_ip`, .5) and Gitea (`docker.gitea_ip`, .6). Both need fixed IPs because ufw-docker firewall rules point at container IPs. A network without the range (the 2024 original) is migrated once with `migrate-lava-network.yml`. @@ -146,7 +146,7 @@ extra_hosts: ``` Most often needed for `subdomains.pocketid` (OIDC); also `subdomains.git` -(Gitea itself), `subdomains.gondulf` and `primary_domain` (StarPunk). +(Gitea itself) and `primary_domain` (StarPunk). ### Template pattern ```yaml diff --git a/README.md b/README.md index b683a5f..e7d0682 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,6 @@ takes ports 80/443 and passes each subdomain to the right container. |---|---|---|---| | Infrastructure | Caddy (reverse proxy, static sites) | — | — | | | Pocket ID (passkey OIDC provider) | id.thesatelliteoflove.com | passkeys | -| | Gondulf (IndieAuth for the main site) | gondulf.thesatelliteoflove.com | email code | | | Dockge (compose stack manager) | port 5001, no public route | local | | Development | Gitea + Actions runner | git.thesatelliteoflove.com (SSH on port 222) | Pocket ID | | | ByteStash (code snippets) | snippets.thesatelliteoflove.com | Pocket ID | @@ -40,7 +39,7 @@ takes ports 80/443 and passes each subdomain to the right container. | | Gokapi (file sharing) | gokapi.thesatelliteoflove.com | Pocket ID | | Communication | GoToSocial (fediverse) | social.thesatelliteoflove.com | Pocket ID | | | Listmonk (mailing lists) | listmonk.nerder.land | local | -| | StarPunk (Micropub for the main site) | starpunk.thesatelliteoflove.com | IndieAuth | +| | StarPunk (Micropub for the main site) | starpunk.thesatelliteoflove.com | Pocket ID | | Monitoring | Changedetection | watcher.thesatelliteoflove.com | local | | | Apprise API (notifications) | Tailscale only, port 8000 | — | | | Gotify + iGotify assistant | gotify.thesatelliteoflove.com, gotify-assistant.thesatelliteoflove.com | local | diff --git a/dns.yml b/dns.yml index 4d594ae..5867bbe 100644 --- a/dns.yml +++ b/dns.yml @@ -17,8 +17,6 @@ domains: - name: thesatelliteoflove.com dns_records: - - name: "gondulf" - ip: "{{ network.netcup_ip }}" - name: "starpunk" ip: "{{ network.netcup_ip }}" - name: "git" @@ -88,6 +86,8 @@ ip: "{{ network.netcup_ip }}" - name: "auth" # Authentik, replaced by Pocket ID ip: "{{ network.netcup_ip }}" + - name: "gondulf" # IndieAuth, replaced by StarPunk v2 + Pocket ID + ip: "{{ network.netcup_ip }}" - name: "files" ip: "{{ network.netcup_ip }}" - name: "kanboard" diff --git a/group_vars/all/domains.yml b/group_vars/all/domains.yml index 8e8f912..2ec2072 100644 --- a/group_vars/all/domains.yml +++ b/group_vars/all/domains.yml @@ -26,7 +26,6 @@ subdomains: listmonk: "listmonk.{{ secondary_domain }}" # Listmonk mailing list gokapi: "gokapi.{{ primary_domain }}" # Gokapi file sharing pocketid: "id.{{ primary_domain }}" # Pocket ID passkey OIDC - gondulf: "gondulf.{{ primary_domain }}" # IndieAuth for the main site starpunk: "starpunk.{{ primary_domain }}" # Micropub for the main site # Email domains for notifications diff --git a/group_vars/docker/services.yml b/group_vars/docker/services.yml index df8f717..f0b002c 100644 --- a/group_vars/docker/services.yml +++ b/group_vars/docker/services.yml @@ -2,7 +2,7 @@ # Service categories for organization service_categories: - infrastructure: ["caddy", "dockge", "gondulf"] + infrastructure: ["caddy", "dockge"] development: ["gitea", "bytestash"] media: ["audiobookshelf", "kavita", "hoarder", "manyfold"] productivity: ["paperlessngx", "baikal", "syncthing", "heyform", "dawarich", "obsidian-livesync", "gokapi"] @@ -19,5 +19,8 @@ services: dawarich: db_name: "dawarich" db_user: "dawarich" +# StarPunk admin: Pocket ID user ID (sub) of phil +starpunk_admin_sub: "5754855f-0a30-4755-9609-7294209e40f4" + # Gokapi super admin: must match the Pocket ID account email gokapi_admin_email: "phil@{{ primary_domain }}" diff --git a/roles/docker/README.md b/roles/docker/README.md index ef06ded..0b2fe01 100644 --- a/roles/docker/README.md +++ b/roles/docker/README.md @@ -26,7 +26,6 @@ Tags: each category file has a category tag, each service its own tag. |---|---|---| | `caddy` | Caddy | Fixed IP 172.20.0.5. Ports 80/443 (+443/udp). `Caddyfile.j2` changes restart it via a handler; the stack itself is deployed with `recreate: never`. Static sites are served from `/opt/stacks/caddy/site/`. | | `pocketid` | Pocket ID v2 | Passkey OIDC provider at `id.`. OIDC clients are created in its web UI; their IDs and secrets go in the vault. | -| `gondulf` | Gondulf | IndieAuth server for the main site (own image from Gitea). Sends login codes by email through Resend. | | `dockge` | Dockge | Compose UI on port 5001, managing `/opt/stacks`. | ### Development (`development`) @@ -59,7 +58,7 @@ Tags: each category file has a category tag, each service its own tag. |---|---|---| | `gotosocial` | GoToSocial | Pocket ID through env vars. Account domain is the apex; Caddy serves WebFinger there. Daily S3 backup of its volume. | | `listmonk` | Listmonk | Postgres 17. On `listmonk.nerder.land`. | -| `starpunk` | StarPunk | Micropub server for the main site (own image from Gitea). Logs in through Gondulf. | +| `starpunk` | StarPunk | Micropub server for the main site (own image from Gitea). Admin login via Pocket ID; issues its own Micropub tokens. | ### Monitoring (`monitoring`) | Tag | Service | Notes | @@ -97,9 +96,9 @@ in from the internet; the Caddy and Gitea task files add those rules. Containers labelled `mag37.dockcheck.update: true` are updated automatically each morning by dockcheck (set up in the cron role). Containers without the -label (Gondulf, StarPunk, Listmonk, HeyForm, Dockge and most databases) only +label (StarPunk, Listmonk, HeyForm, Dockge and most databases) only get a Gotify notice. Dawarich's database and Redis are skipped entirely. -Gondulf and StarPunk use pinned version tags. +StarPunk uses a pinned version tag. ## Adding a service @@ -121,7 +120,7 @@ roles/docker/ ├── tasks/ │ ├── main.yml # System setup, network, category imports │ ├── shell.yml # zsh, Atuin, bat -│ ├── infrastructure/ # caddy, pocketid, gondulf (+ dockge in main.yml) +│ ├── infrastructure/ # caddy, pocketid (+ dockge in main.yml) │ ├── development/ # gitea, bytestash │ ├── media/ # audiobookshelf, kavita, hoarder (Karakeep), manyfold │ ├── productivity/ # paperlessngx, baikal, syncthing, heyform, dawarich, obsidian-livesync, gokapi diff --git a/roles/docker/tasks/infrastructure/gondulf.yml b/roles/docker/tasks/infrastructure/gondulf.yml deleted file mode 100644 index 380e45a..0000000 --- a/roles/docker/tasks/infrastructure/gondulf.yml +++ /dev/null @@ -1,28 +0,0 @@ -- name: make gondulf directories - ansible.builtin.file: - path: "{{ item }}" - state: directory - loop: - - /opt/stacks/gondulf - -- name: Template out the gondulf compose file - ansible.builtin.template: - src: gondulf-compose.yml.j2 - dest: /opt/stacks/gondulf/compose.yml - owner: root - mode: '0644' - -- name: Template out the gondulf .env file - ansible.builtin.template: - src: gondulf-env.j2 - dest: /opt/stacks/gondulf/.env - owner: root - mode: '0640' - group: docker - diff: false # contains the secret key and SMTP password - -- name: deploy gondulf stack - community.docker.docker_compose_v2: - project_src: /opt/stacks/gondulf - files: - - compose.yml diff --git a/roles/docker/tasks/infrastructure/main.yml b/roles/docker/tasks/infrastructure/main.yml index e911b8d..55597dc 100644 --- a/roles/docker/tasks/infrastructure/main.yml +++ b/roles/docker/tasks/infrastructure/main.yml @@ -9,10 +9,6 @@ import_tasks: pocketid.yml tags: pocketid -- name: Install gondulf - import_tasks: gondulf.yml - tags: gondulf - - name: Template out the dockge compose file ansible.builtin.template: src: dockge-compose.yml.j2 diff --git a/roles/docker/templates/Caddyfile.j2 b/roles/docker/templates/Caddyfile.j2 index 13e5af0..4d71562 100644 --- a/roles/docker/templates/Caddyfile.j2 +++ b/roles/docker/templates/Caddyfile.j2 @@ -88,10 +88,6 @@ pdg.thesatelliteoflove.com { } } -gondulf.thesatelliteoflove.com { - reverse_proxy gondulf:8000 - } - starpunk.thesatelliteoflove.com { reverse_proxy starpunk:8000 } diff --git a/roles/docker/templates/gondulf-compose.yml.j2 b/roles/docker/templates/gondulf-compose.yml.j2 deleted file mode 100644 index d6efeb2..0000000 --- a/roles/docker/templates/gondulf-compose.yml.j2 +++ /dev/null @@ -1,34 +0,0 @@ -services: - gondulf: - image: git.thesatelliteoflove.com/phil/gondulf:1.0.1 - container_name: gondulf - restart: unless-stopped - extra_hosts: - - '{{ primary_domain }}:{{ docker.hairpin_ip }}' - volumes: - - gondulf_data:/data - env_file: - - .env - environment: - - GONDULF_HTTPS_REDIRECT=true - - GONDULF_SECURE_COOKIES=true - - GONDULF_TRUST_PROXY=true - - GONDULF_TOKEN_EXPIRY=3600 - - GONDULF_CODE_EXPIRY=600 - - GONDULF_TOKEN_CLEANUP_ENABLED=false - - GONDULF_TOKEN_CLEANUP_INTERVAL=3600 - - GONDULF_LOG_LEVEL=INFO - - GONDULF_DEBUG=false - healthcheck: - test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8000/health"] - interval: 30s - timeout: 5s - retries: 3 - start_period: 10s -volumes: - # Existing volume (gondulf_gondulf_data) holds the SQLite database - gondulf_data: -networks: - default: - external: true - name: {{ docker.network_name }} diff --git a/roles/docker/templates/gondulf-env.j2 b/roles/docker/templates/gondulf-env.j2 deleted file mode 100644 index 8534e45..0000000 --- a/roles/docker/templates/gondulf-env.j2 +++ /dev/null @@ -1,9 +0,0 @@ -GONDULF_SECRET_KEY={{ vault_gondulf.secret_key }} -GONDULF_BASE_URL=https://{{ subdomains.gondulf }} -GONDULF_DATABASE_URL=sqlite:////data/gondulf.db -GONDULF_SMTP_HOST={{ smtp.host }} -GONDULF_SMTP_PORT=587 -GONDULF_SMTP_USERNAME={{ smtp.username }} -GONDULF_SMTP_PASSWORD={{ vault_smtp.password }} -GONDULF_SMTP_FROM=gondulf@{{ smtp.from_domain }} -GONDULF_SMTP_USE_TLS=true diff --git a/roles/docker/templates/starpunk-compose.yml.j2 b/roles/docker/templates/starpunk-compose.yml.j2 index 3f35828..4a62d50 100644 --- a/roles/docker/templates/starpunk-compose.yml.j2 +++ b/roles/docker/templates/starpunk-compose.yml.j2 @@ -1,10 +1,10 @@ services: starpunk: - image: git.thesatelliteoflove.com/phil/starpunk:v1.6.0-rc.2 + image: git.thesatelliteoflove.com/phil/starpunk:v2.0.0 container_name: starpunk restart: unless-stopped extra_hosts: - - '{{ subdomains.gondulf }}:{{ docker.hairpin_ip }}' + - '{{ subdomains.pocketid }}:{{ docker.hairpin_ip }}' - '{{ primary_domain }}:{{ docker.hairpin_ip }}' volumes: - starpunk-data:/data @@ -14,9 +14,16 @@ services: SITE_NAME: StarPunk SITE_AUTHOR: Mr Whiskers SITE_DESCRIPTION: Starpunk instance - # Authentication + # Site owner identity (h-card source, Micropub "me") ADMIN_ME: https://{{ primary_domain }} SESSION_SECRET: {{ vault_starpunk.session_secret }} + # Admin login via Pocket ID (client "StarPunk"); only this user may log in. + # StarPunk issues Micropub tokens itself (ADR-064). + OIDC_ISSUER: https://{{ subdomains.pocketid }} + OIDC_CLIENT_ID: {{ vault_starpunk.pocketid.client_id }} + OIDC_CLIENT_SECRET: {{ vault_starpunk.pocketid.client_secret }} + OIDC_ADMIN_SUB: {{ starpunk_admin_sub }} + OIDC_PROVIDER_NAME: Pocket ID # Production settings FLASK_ENV: production FLASK_DEBUG: 0 diff --git a/todo.md b/todo.md index 3b6ef7a..d321128 100644 --- a/todo.md +++ b/todo.md @@ -21,7 +21,7 @@ Many compose templates repeat the same network, restart and label blocks. Pull the shared parts into includes or macros. ### Health checks -Only Pocket ID, Gondulf, Dawarich, Listmonk and Syncthing define health checks +Only Pocket ID, Dawarich, Listmonk and Syncthing define health checks in their templates. Add them to the rest where the image supports it. ### Backups