Files
archy/apps/podsteadr
ssmithxandClaude Opus 5.5 bfe0eb96ee fix(podsteadr): install nostr-provider.js into /data
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>
2026-10-23 04:21:45 +00:00
..

podsteadr — Nostr-native Podcasting & Livestreaming

Self-hosted, nostr-native podcast publishing and livestreaming. Log in with a NIP-07 nostr identity (no passwords, no email), upload an mp4 to publish an RSS 2.0 feed with Podcasting 2.0 lightning payment info, or go live via OBS (RTMP) or the browser (WebRTC/WHIP) — the stream is announced on nostr as a NIP-53 live event and viewers watch over HLS.

This is a four-container stack that installs, starts, stops and uninstalls as one app:

App Manifest Role
podsteadr apps/podsteadr/manifest.yml Fastify API + built Vue UI + RSS feeds
podsteadr-mediamtx apps/podsteadr-mediamtx/manifest.yml RTMP/WHIP ingest, HLS output, recording
podsteadr-blossom apps/podsteadr-blossom/manifest.yml BUD-02 sha256-addressed media blobs
podsteadr-player apps/podsteadr-player/manifest.yml Installable podcast player (PWA) with a FIPS gateway

The app, mediamtx and blossom join a dedicated podsteadr-net bridge network and resolve each other by short DNS aliases (podsteadr-app, mediamtx, blossom). The player uses pasta networking instead (see FIPS below).

My Apps shows two cards: podsteadr (publishing) and podsteadr player (listening). Blossom and MediaMTX are listed under Services.

Building the Images

Both images are built from their own repos, hosted on nostr git (ngit):

  • podsteadr: nostr://npub1krldcq2laqxeslseue8uyk24vg6mhmv0ra6ldc52hyfwdx5azd8se5j65x/relay.ngit.dev/podsteadr, built with the vendored apps/podsteadr/Dockerfile
  • podsteadr-player: nostr://npub1krldcq2laqxeslseue8uyk24vg6mhmv0ra6ldc52hyfwdx5azd8se5j65x/relay.ngit.dev/podsteadr-player, built with that repo's own Dockerfile (it carries the player's nginx.conf and FIPS gateway code)

Clone them with git clone nostr://... (needs ngit's git-remote-nostr) or browse them on gitworkshop.dev. The podsteadr repo's .claude/skills/archy-integration/SKILL.md lists which files here follow which files there.

Option 1: Use the build script

# From archy repo root; builds both images under the tags the manifests use
./apps/podsteadr/build-from-prototype.sh ~/podsteadr ~/podsteadr-player

The manifests use pull_policy: if-not-present, so a node with these images built locally installs without pulling.

Option 2: Build from source directories

cd ~/podsteadr
podman build -t source.archipelago-foundation.org/lfg2025/podsteadr:1.0.0 -f ~/archy/apps/podsteadr/Dockerfile .
cd ~/podsteadr-player
podman build -t source.archipelago-foundation.org/lfg2025/podsteadr-player:1.0.0 .

On Archipelago hosts rootless podman sees /var/tmp read-only; run podman with TMPDIR=$HOME/.cache/podman-build-tmp (the scripts do this for you).

Publishing to the shared registry

./apps/podsteadr/push-to-registry.sh 1.0.0

Nodes without the images built locally pull them from there. Bump container.image in both manifests if you push a new version.

FIPS mesh

Installing the stack opens 8095 (feeds/UI), 8097 (player) and 8098 (media) through the fips0 firewall: scripts/generate-app-catalog.py writes them into core/archipelago/src/fips/app_ports.rs from the manifests' interfaces.

  • Feeds: podsteadr notices a feed being fetched as http://<npub>.fips:8095/... and writes mesh URLs into it — the feed's own link, and http://<npub>.fips:8098/<sha256> for media on this node's blossom. Media on other blossom servers is left as stored.
  • Player, opened over the mesh (http://<npub>.fips:8097/): fetches .fips feeds and media directly over the device's own FIPS connection.
  • Player, opened by LAN address or over HTTPS: a browser can't speak FIPS, so the player sends .fips requests to its own /fips/ gateway, which fetches them over the node's FIPS link. The gateway passes only podsteadr feeds/catalogs (8095) and blossom blobs (8098) on npub….fips hosts, GET/HEAD only. It computes each host's mesh address from the npub (fd + first 15 bytes of sha256(pubkey)), so it needs IPv6 egress to the mesh but no DNS — hence network: pasta: podman's IPv4-only bridge networks can't reach FIPS addresses.

Ports

See apps/PORTS.md. Summary: 8095 (web UI/API/RSS), 1935 (RTMP), 8889 (WebRTC/WHIP), 8189/udp (WebRTC ICE), 8890 (HLS), 8098 (Blossom), 8097 (player).

All of podsteadr's ports are auth: none — this is a public podcast/livestream server, not a private personal app; RSS feeds, HLS playback, and blob reads must stay reachable by third-party clients with no Archipelago session, and the app enforces its own NIP-98 signed-request auth for sensitive routes and per-stream secret keys for RTMP/WHIP publish. See the auth_rationale on each port mapping.

Nostr Identity

podsteadr's frontend vendors a copy of Archipelago's nostr-provider.js shim and references it directly from index.html (its Fastify server isn't the nginx-served SPA shape the platform auto-patches — see "Nostr Signer Bridge" in docs/app-developer-guide.md). apps/podsteadr/manifest.yml declares a post_install hook that re-copies the canonical /opt/archipelago/web-ui/nostr-provider.js over the vendored copy on every install/reinstall, so it doesn't go stale across OTA releases.

Data

  • /var/lib/archipelago/podsteadr — SQLite DB, server's own nostr key, covers, and (read-only here) shared stream recordings.
  • /var/lib/archipelago/podsteadr/recordings — stream recordings (writable by podsteadr-mediamtx, read-only for podsteadr), 7-day retention.
  • /var/lib/archipelago/podsteadr-blossom/data — media blobs.

Known gotchas

See the podsteadr repo's docs/STATUS.md for the full list (blossom 6.x pruning rules and the 4.x size-repair step on upgrade, split-horizon blossom URL, MediaMTX has no shell so status is polled not hooked, standard vs. low-latency HLS). The blossom and mediamtx config files embedded in apps/podsteadr-blossom/manifest.yml / apps/podsteadr-mediamtx/manifest.yml already carry the load-bearing ones inline as comments.