The player now takes any RSS feed URL as well as a podsteadr server, and plays podsteadr feeds hosted on the FIPS mesh. A browser can't speak FIPS, and an HTTPS page can't fetch or play http:// URLs, so .fips URLs are routed one of two ways (src/lib/net.ts): - served over the mesh (http://<npub>.fips:8097/), the page fetches .fips URLs directly over the device's own FIPS connection; - served over HTTPS (player.atobitcoin.io), .fips URLs are rewritten to the host's /fips/<host>/<port>/<path> gateway, which relays only podsteadr feeds (8095) and blossom blobs (8098) over the host's FIPS link (deploy/player-domain.conf). Also: - "Add" accepts a feed, an OPML catalog, or a server address and works out which; old saved servers migrate to the new subscription format - PWA: manifest, icons, service worker caching only the app shell (never feeds, audio or the gateway), install button, safe areas - lock-screen controls and artwork via the Media Session API - artwork, dates and durations in the episode list - no secure-context-only APIs on the mesh copy (randomUUID, clipboard) - buying episodes over FIPS is refused with a clear message: NIP-98 signs the server's public URL, which a .fips address never matches Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
96 lines
3.7 KiB
Plaintext
96 lines
3.7 KiB
Plaintext
# podsteadr-player on its own HTTPS domain, plus the FIPS gateway.
|
|
#
|
|
# Install: /etc/nginx/sites-available/player-domain, symlinked into sites-enabled.
|
|
# Certificate: certbot certonly --nginx -d player.atobitcoin.io (needs the port-80
|
|
# block live first — see deploy/README.md).
|
|
#
|
|
# The player itself runs in the podsteadr-player container on 127.0.0.1:8097. That
|
|
# container is also published on the FIPS mesh (http://<npub>.fips:8097/), where it
|
|
# fetches .fips feeds directly; this HTTPS copy can't (browsers block http:// from
|
|
# https:// pages), so it uses the /fips/ gateway below instead.
|
|
|
|
limit_req_zone $binary_remote_addr zone=player_fips_feeds:10m rate=5r/s;
|
|
limit_conn_zone $binary_remote_addr zone=player_fips_conns:10m;
|
|
|
|
server {
|
|
listen 80;
|
|
listen [::]:80;
|
|
server_name player.atobitcoin.io;
|
|
location /.well-known/acme-challenge/ {
|
|
root /var/www/html;
|
|
}
|
|
location / {
|
|
return 301 https://$host$request_uri;
|
|
}
|
|
}
|
|
|
|
server {
|
|
listen 443 ssl;
|
|
listen [::]:443 ssl;
|
|
server_name player.atobitcoin.io;
|
|
|
|
ssl_certificate /etc/letsencrypt/live/player.atobitcoin.io/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/player.atobitcoin.io/privkey.pem;
|
|
ssl_protocols TLSv1.2 TLSv1.3;
|
|
|
|
add_header X-Content-Type-Options nosniff always;
|
|
add_header Referrer-Policy no-referrer always;
|
|
|
|
location / {
|
|
proxy_pass http://127.0.0.1:8097;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Host $http_host;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
}
|
|
|
|
# ---- FIPS gateway ------------------------------------------------------------
|
|
# /fips/<host>.fips/<port>/<path> -> http://<host>.fips:<port>/<path>, over this
|
|
# node's own FIPS link. Deliberately NOT a general mesh proxy: anyone on the
|
|
# internet can call it, and mesh peers expose things (node UIs on :80, the peer
|
|
# API on :5679) that must never be reachable from here. Only podcast content
|
|
# passes: podsteadr catalogs/feeds on 8095 and blossom blobs on 8098, GET/HEAD
|
|
# only, no client cookies or credentials forwarded.
|
|
#
|
|
# .fips names resolve via the fips daemon's DNS on 127.0.0.1:5354 (AAAA only).
|
|
# Not via systemd-resolved: on this node it routes ~fips to [::1]:5354, where the
|
|
# daemon isn't listening.
|
|
|
|
location ~ "^/fips/(?<fips_host>(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+fips)/8095/(?<fips_path>catalog\.opml|feeds/[A-Za-z0-9_-]+/feed\.xml)$" {
|
|
limit_except GET { deny all; }
|
|
limit_req zone=player_fips_feeds burst=20 nodelay;
|
|
resolver 127.0.0.1:5354 ipv4=off valid=60s;
|
|
resolver_timeout 5s;
|
|
|
|
# Host is what makes podsteadr write mesh (.fips) enclosure URLs into the feed.
|
|
proxy_pass http://$fips_host:8095/$fips_path;
|
|
proxy_set_header Host $fips_host:8095;
|
|
proxy_set_header Cookie "";
|
|
proxy_set_header Authorization "";
|
|
proxy_hide_header Set-Cookie;
|
|
proxy_connect_timeout 20s;
|
|
proxy_read_timeout 30s;
|
|
}
|
|
|
|
location ~ "^/fips/(?<fips_host>(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+fips)/8098/(?<fips_path>[0-9a-f]{64}(?:\.[a-z0-9]{1,8})?)$" {
|
|
limit_except GET { deny all; }
|
|
limit_conn player_fips_conns 8;
|
|
resolver 127.0.0.1:5354 ipv4=off valid=60s;
|
|
resolver_timeout 5s;
|
|
|
|
# Range/If-Range pass through untouched, so seeking works; stream, don't buffer.
|
|
proxy_pass http://$fips_host:8098/$fips_path;
|
|
proxy_set_header Host $fips_host:8098;
|
|
proxy_set_header Cookie "";
|
|
proxy_set_header Authorization "";
|
|
proxy_hide_header Set-Cookie;
|
|
proxy_http_version 1.1;
|
|
proxy_buffering off;
|
|
proxy_connect_timeout 20s;
|
|
proxy_read_timeout 120s;
|
|
}
|
|
|
|
location /fips/ {
|
|
return 404;
|
|
}
|
|
}
|