feat(apps): add podsteadr app stack with player and FIPS access #163

Closed
ssmithx wants to merge 0 commits from feat/podsteadr-app into main
Collaborator

Summary

Adds podsteadr, a self-hosted, nostr-native podcast publishing and livestreaming server, plus its listener-side podsteadr player, as a four-container stack that installs, starts, stops and uninstalls as one app.

App Image Role Port
podsteadr lfg2025/podsteadr:1.0.0 Fastify web UI, API and RSS feeds 8095
podsteadr-blossom blossom-server 6.4.1 BUD-02 media blobs, HTTP Range 8098
podsteadr-mediamtx MediaMTX 1.20.0 RTMP/WHIP ingest, HLS output 1935, 8889, 8189/udp, 8890
podsteadr-player lfg2025/podsteadr-player:1.0.0 Installable PWA player with a read-only FIPS gateway 8097

My Apps shows two cards, podsteadr and podsteadr player; blossom and MediaMTX are listed under Services.

FIPS mesh

  • The catalog generator now counts auth: none ports as service ports, so the fips0 firewall list (fips/app_ports.rs) opens 8095, 8097 and 8098.
  • podsteadr writes http://<npub>.fips:... feed and media URLs when a feed is fetched over the mesh.
  • The player fetches .fips URLs directly when opened over the mesh. When opened by LAN address or HTTPS it goes through its own /fips/ gateway. The gateway relays only podsteadr feeds and catalogs (8095) and blossom blobs (8098) on npub….fips hosts, GET/HEAD only, without client cookies or credentials. It computes each host's mesh address from the npub, and runs on network: pasta because bridge networks can't reach FIPS addresses over IPv6.

Stack registration (core)

Mirrors pine:

  • stack_member_app_ids / owning_package treat the four apps as one package.
  • install routing, orchestrator_uninstall_app_ids and all_container_names cover all four containers.
  • startup_order starts blossom, then mediamtx, then the player, then the app.
  • Crash recovery auto-starts the containers and recreates podsteadr-net with its DNS aliases.
  • blossom and mediamtx are excluded from the app list.

Images and build scripts

  • Both images are tagged for source.archipelago-foundation.org/lfg2025, with pull_policy: if-not-present.
  • build-from-prototype.sh builds both images locally under those tags; push-to-registry.sh <version> builds and pushes them.
  • Fix: rootless podman on Archipelago hosts sees /var/tmp read-only. Builds now stage under ~/.cache/podman-build-tmp, and images are tagged by image id after the build, because build -t of a fully cached image copies through /var/tmp and fails.

Source

Both app repos live on nostr git (relay.ngit.dev), with a Gitea mirror. Manifest repo links go to gitworkshop.dev. The podsteadr repo carries .claude/skills/archy-integration/SKILL.md, which maps its files to the archy files that must follow them.

Other

  • serviceNames.ts lists the blossom and mediamtx services.
  • The apps-config test used podsteadr as its example of an unknown app, so it already failed on this branch. It now uses a made-up id.
  • apps/PORTS.md adds 8097.
  • The README describes the stack, both builds and FIPS access.

Before signing

  • Push both images with apps/podsteadr/push-to-registry.sh 1.0.0. Until then, a node needs them built locally with build-from-prototype.sh.
  • End-to-end node test: install from the App Store, publish and play an episode across nodes over FIPS, stop/start/restart, uninstall (all four containers removed), reinstall. Images are built on a test node with these scripts, but this branch's backend is not yet deployed there.
  • Run tests/lifecycle/run-gate.sh on a node.
  • Decide the license: apps/podsteadr/manifest.yml says license: MIT, but neither app repo has a LICENSE file.
  • Add the app to releases/app-catalog.json and re-sign. podsteadr is not in the per-app OTA update mapping (image-versions.sh, image_versions.rs), so new versions ship with an Archipelago release.

Testing

  • scripts/validate-app-manifest.sh: all four APPROVED. blossom has one readonly_root warning, which is expected.
  • scripts/check-app-catalog-drift.py --release --strict: clean.
  • scripts/check-app-build-contexts.py .: passes.
  • The catalog generator is idempotent: a second run changes nothing.
  • cargo test -p archipelago-container: 83 passed, including the unauthenticated-endpoint allowlist with 8097.
  • cargo test -p archipelago, targeted: 177 passed (stack membership, owning package, uninstall ids, container names, startup order, crash recovery). This needs Rust ≥ 1.96 for atomic_try_update.
  • neode-ui vitest:
    • The apps, discover and appSession suites pass: 143/143.
    • The full run passes 1595/1596. The one failure, ConnectionJourney, is a timing test not touched here; it passes 3/3 alone on both this branch and main.
  • Release build of archipelago and neode-ui: OK.
  • Player image: tested on a node (pasta, read-only root, all capabilities dropped).
    • Its njs gateway tests pass: 6/6.
    • Fetching a feed, catalog and audio Range from another node over FIPS returns 200/206.
    • Every refused case returns 403/404.

🤖 Generated with Claude Code

## Summary Adds **podsteadr**, a self-hosted, nostr-native podcast publishing and livestreaming server, plus its listener-side **podsteadr player**, as a four-container stack that installs, starts, stops and uninstalls as one app. | App | Image | Role | Port | |---|---|---|---| | `podsteadr` | `lfg2025/podsteadr:1.0.0` | Fastify web UI, API and RSS feeds | 8095 | | `podsteadr-blossom` | blossom-server 6.4.1 | BUD-02 media blobs, HTTP Range | 8098 | | `podsteadr-mediamtx` | MediaMTX 1.20.0 | RTMP/WHIP ingest, HLS output | 1935, 8889, 8189/udp, 8890 | | `podsteadr-player` | `lfg2025/podsteadr-player:1.0.0` | Installable PWA player with a read-only FIPS gateway | 8097 | My Apps shows two cards, **podsteadr** and **podsteadr player**; blossom and MediaMTX are listed under Services. ### FIPS mesh - The catalog generator now counts `auth: none` ports as service ports, so the fips0 firewall list (`fips/app_ports.rs`) opens 8095, 8097 and 8098. - podsteadr writes `http://<npub>.fips:...` feed and media URLs when a feed is fetched over the mesh. - The player fetches `.fips` URLs directly when opened over the mesh. When opened by LAN address or HTTPS it goes through its own `/fips/` gateway. The gateway relays only podsteadr feeds and catalogs (8095) and blossom blobs (8098) on `npub….fips` hosts, GET/HEAD only, without client cookies or credentials. It computes each host's mesh address from the npub, and runs on `network: pasta` because bridge networks can't reach FIPS addresses over IPv6. ### Stack registration (core) Mirrors pine: - `stack_member_app_ids` / `owning_package` treat the four apps as one package. - install routing, `orchestrator_uninstall_app_ids` and `all_container_names` cover all four containers. - `startup_order` starts blossom, then mediamtx, then the player, then the app. - Crash recovery auto-starts the containers and recreates `podsteadr-net` with its DNS aliases. - blossom and mediamtx are excluded from the app list. ### Images and build scripts - Both images are tagged for `source.archipelago-foundation.org/lfg2025`, with `pull_policy: if-not-present`. - `build-from-prototype.sh` builds both images locally under those tags; `push-to-registry.sh <version>` builds and pushes them. - Fix: rootless podman on Archipelago hosts sees `/var/tmp` read-only. Builds now stage under `~/.cache/podman-build-tmp`, and images are tagged by image id after the build, because `build -t` of a fully cached image copies through `/var/tmp` and fails. ### Source Both app repos live on nostr git (relay.ngit.dev), with a Gitea mirror. Manifest repo links go to gitworkshop.dev. The podsteadr repo carries `.claude/skills/archy-integration/SKILL.md`, which maps its files to the archy files that must follow them. ### Other - `serviceNames.ts` lists the blossom and mediamtx services. - The apps-config test used `podsteadr` as its example of an unknown app, so it already failed on this branch. It now uses a made-up id. - `apps/PORTS.md` adds 8097. - The README describes the stack, both builds and FIPS access. ## Before signing - [ ] **Push both images** with `apps/podsteadr/push-to-registry.sh 1.0.0`. Until then, a node needs them built locally with `build-from-prototype.sh`. - [ ] **End-to-end node test:** install from the App Store, publish and play an episode across nodes over FIPS, stop/start/restart, uninstall (all four containers removed), reinstall. Images are built on a test node with these scripts, but this branch's backend is not yet deployed there. - [ ] **Run `tests/lifecycle/run-gate.sh` on a node.** - [ ] **Decide the license:** `apps/podsteadr/manifest.yml` says `license: MIT`, but neither app repo has a LICENSE file. - [ ] **Add the app to `releases/app-catalog.json` and re-sign.** podsteadr is not in the per-app OTA update mapping (`image-versions.sh`, `image_versions.rs`), so new versions ship with an Archipelago release. ## Testing - `scripts/validate-app-manifest.sh`: all four APPROVED. blossom has one `readonly_root` warning, which is expected. - `scripts/check-app-catalog-drift.py --release --strict`: clean. - `scripts/check-app-build-contexts.py .`: passes. - The catalog generator is idempotent: a second run changes nothing. - `cargo test -p archipelago-container`: 83 passed, including the unauthenticated-endpoint allowlist with 8097. - `cargo test -p archipelago`, targeted: 177 passed (stack membership, owning package, uninstall ids, container names, startup order, crash recovery). This needs Rust ≥ 1.96 for `atomic_try_update`. - `neode-ui` vitest: - The apps, discover and appSession suites pass: 143/143. - The full run passes 1595/1596. The one failure, `ConnectionJourney`, is a timing test not touched here; it passes 3/3 alone on both this branch and `main`. - Release build of `archipelago` and `neode-ui`: OK. - Player image: tested on a node (pasta, read-only root, all capabilities dropped). - Its njs gateway tests pass: 6/6. - Fetching a feed, catalog and audio Range from another node over FIPS returns 200/206. - Every refused case returns 403/404. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
ssmithx added 1 commit 2026-10-19 23:45:30 +00:00
podsteadr is a self-hosted, nostr-native podcast publishing and
livestreaming server. It ships as three manifests:

- podsteadr: Fastify app (web UI, API, RSS) on 8095
- podsteadr-blossom: blossom-server 6.4.1 for media blobs on 8098
  (native HTTP Range; BLOSSOM_REQUIRE_CONFIG=1 with a 100-year prune
  rule so episodes are never pruned)
- podsteadr-mediamtx: MediaMTX 1.20.0 for RTMP/WHIP ingest and HLS

All six host ports are auth: none with rationale: feeds, enclosures and
HLS must reach third-party podcast clients, and the app gates its own
writes (NIP-98, BUD-02 upload auth, per-stream publish secrets). The
unauthenticated-endpoint test lists them explicitly.

Adds a catalog entry for podsteadr (the blossom/mediamtx dependencies
are marked internal in the drift check), a placeholder icon, and the
regenerated app-session/fips port lists. The generator also picked up
public-web-router's missing display name, which was already drifted
on main.

Not yet in the signed release catalog, and the app image is still
localhost/podsteadr:1.0.0; it needs pushing to the registry before
fleet-wide install.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ssmithx added 1 commit 2026-10-21 14:11:13 +00:00
Skip directory creation for existing file sources while retaining ownership repair. Remove Blossom's host-root data_uid override so fresh data directories use rootless ownership. Add an idempotent generated/existing file-bind regression test.

Validation: Blossom manifest preflight and diff check passed. Isolated backend runner blocked at compilation by existing atomic_try_update usage in monitoring/store.rs:105; regression test has not executed. Live lifecycle acceptance remains pending.
Author
Collaborator

Review continues on ngit

This contribution has been replicated to its canonical ngit proposal:

https://gitworkshop.dev/nevent1qqs9lw2lxmk47c2g6kn8363mpe3ru03rh08c75py3y59xyjv2e8lqugpz3mhxue69uhhyetvv9ujumn8d96zuer9wc8klunw

The exact existing Git history is preserved, including the review fixes, at 299cb20a2e9194dce9bc72965ba538d0b2011d7e. No squash, rebase, or merge was performed. The ngit proposal includes the original description, outstanding tasks, and updated source-test results/limitations.

Please continue review and updates on ngit. This Gitea PR remains open as historical context; it has not been merged or marked accepted. Reviews and discussions are platform-specific and have not been copied as approvals.

## Review continues on ngit This contribution has been replicated to its canonical ngit proposal: https://gitworkshop.dev/nevent1qqs9lw2lxmk47c2g6kn8363mpe3ru03rh08c75py3y59xyjv2e8lqugpz3mhxue69uhhyetvv9ujumn8d96zuer9wc8klunw The exact existing Git history is preserved, including the review fixes, at `299cb20a2e9194dce9bc72965ba538d0b2011d7e`. No squash, rebase, or merge was performed. The ngit proposal includes the original description, outstanding tasks, and updated source-test results/limitations. Please continue review and updates on ngit. This Gitea PR remains open as historical context; it has **not** been merged or marked accepted. Reviews and discussions are platform-specific and have not been copied as approvals.
ssmithx added 4 commits 2026-10-22 19:37:14 +00:00
Add the podsteadr-player manifest (static PWA + read-only /fips/ gateway,
pasta for IPv6 mesh egress, port 8097 auth: none) and make podsteadr depend
on it. Give podsteadr-blossom an api interface on 8098 so blob URLs in
feeds are reachable over FIPS. The catalog generator now counts auth: none
ports as service ports, so app_ports.rs gains 8097 and 8098.

Images move to the project registry and repo links to the source domain,
dropping the raw IP and production hostname from the manifests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
build-from-prototype.sh and push-to-registry.sh now build podsteadr and
podsteadr-player under the tags the manifests use. Rootless podman on
Archipelago hosts sees /var/tmp read-only, so stage builds under
~/.cache/podman-build-tmp, and tag by image id after build: 'build -t' of a
fully cached image copies through /var/tmp and fails. README documents the
four-container stack, both builds and FIPS access.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mirror pine: install/uninstall all four members together (blossom,
mediamtx, player, app), start them in dependency order, include them in
container listings and auto-start, and recover podsteadr-net with its DNS
aliases after a crash. blossom and mediamtx are hidden as services.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Show podsteadr-blossom and podsteadr-mediamtx under Services. The apps
config test used podsteadr as its example of an unknown app, which it no
longer is; use a made-up id instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ssmithx added 1 commit 2026-10-22 19:43:11 +00:00
podsteadr and podsteadr-player now live on nostr git (relay.ngit.dev), with
Gitea as a mirror. Manifest repo links use gitworkshop.dev; the README and
vendored Dockerfile give the nostr:// clone URLs and point at the podsteadr
repo's archy-integration skill.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ssmithx changed title from feat(apps): add podsteadr app package to feat(apps): add podsteadr app stack with player and FIPS access 2026-10-22 20:16:58 +00:00
ssmithx added 1 commit 2026-10-23 01:20:54 +00:00
The app's container is podsteadr-app, not podsteadr, so the package scanner
listed it under its container name and also restored an absent 'podsteadr'
placeholder from installed-apps, whose ui-ready stays false: the dashboard
showed 'Web UI not ready' for a healthy install. Alias it like immich_server,
and give status, stack health and the health monitor's dependency map the
same mapping.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ssmithx added 1 commit 2026-10-23 04:21:48 +00:00
The post_install hook copied the host's nostr-provider.js to
/app/public, which is on the read-only root filesystem, so the copy failed
(logged and skipped) and the app kept serving its stale bundled script.
Copy it to /data instead, check it landed, and point the server at it
with ARCHIPELAGO_PROVIDER_PATH, as gashboard does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ssmithx added 1 commit 2026-10-24 03:17:17 +00:00
package.stop marks the package id, its live container names and its
member app ids as user-stopped. package.start and package.restart only
cleared the package id and the member app ids, so a container whose name
differs from its app id (podsteadr-app, immich_server) stayed marked after
a start: the start path saw it as wedged and force-recreated it, and the
reconcile guard and crash recovery would then leave it down after a crash,
an Archipelago restart or a reboot. Clear every name in the package's
container list too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collaborator

Reconciled against canonical ngit review: feat(apps): add podsteadr app stack with player and FIPS access is applied.

This PR's exact head 99e6e915c0f7e32e4163219c6e7942c83af58fdc is already an ancestor of accepted main 1a4ea15df9c8800b6d288277d2839c7b93662ff7, which matches on ngit and Gitea. The existing review fixes and commit history are preserved.

Closing the redundant Gitea PR record because its contribution has already been integrated through ngit. No second merge or history rewrite is being performed. Artifact publication and installed-device acceptance remain separate from this source-review disposition.

Reconciled against canonical ngit review: [feat(apps): add podsteadr app stack with player and FIPS access](https://gitworkshop.dev/nevent1qqs9lw2lxmk47c2g6kn8363mpe3ru03rh08c75py3y59xyjv2e8lqugpz3mhxue69uhhyetvv9ujumn8d96zuer9wc8klunw) is **applied**. This PR's exact head `99e6e915c0f7e32e4163219c6e7942c83af58fdc` is already an ancestor of accepted main `1a4ea15df9c8800b6d288277d2839c7b93662ff7`, which matches on ngit and Gitea. The existing review fixes and commit history are preserved. Closing the redundant Gitea PR record because its contribution has already been integrated through ngit. No second merge or history rewrite is being performed. Artifact publication and installed-device acceptance remain separate from this source-review disposition.
chaum closed this pull request 2026-11-09 12:31:36 +00:00

Pull request closed

Please reopen this pull request to perform a merge.
Sign in to join this conversation.