Adds apps/podsteadr (main Fastify+Vue app, container.build from the podsteadr repo), apps/podsteadr-mediamtx (RTMP/WHIP ingest, HLS, recording), and apps/podsteadr-blossom (BUD-02 media blobs), wired together on a dedicated podsteadr-net bridge network per the multi-container pattern documented in docs/app-developer-guide.md (indeedhub's api/relay/minio/redis/postgres siblings). All podsteadr ports are auth: none with a rationale, since it's a public podcast/livestream server whose RSS feeds, HLS playback, and blob reads must stay reachable by third-party clients with no Archipelago session — the app already gates its own sensitive routes with NIP-98 and per-stream secret keys. Also updates apps/PORTS.md, apps/README.md, and bumps the reviewed unauthenticated-port count in core/container/src/manifest.rs's unauthenticated_ports_are_all_accounted_for test (25 -> 31) to acknowledge the six new auth:none ports. Regenerated catalog-derived files (core/archipelago/src/fips/app_ports.rs, neode-ui/src/views/appSession/generatedAppSessionConfig.ts) via scripts/generate-app-catalog.py. All three manifests pass scripts/validate-app-manifest.sh and `cargo test -p archipelago-container manifest`.
126 lines
3.8 KiB
YAML
126 lines
3.8 KiB
YAML
app:
|
|
id: podsteadr-blossom
|
|
name: podsteadr Blossom
|
|
version: "4"
|
|
description: Blossom (BUD-02) sha256-addressed media blob server backing podsteadr's episode uploads and covers.
|
|
category: media
|
|
|
|
# Hyphenated name matches the podsteadr repo's docker-compose container_name
|
|
# (podsteadr-blossom); alias `blossom` is the short hostname podsteadr's
|
|
# server reaches it by (BLOSSOM_URL_INTERNAL=http://blossom:3000).
|
|
container_name: podsteadr-blossom
|
|
|
|
container:
|
|
image: ghcr.io/hzrd149/blossom-server:4
|
|
pull_policy: if-not-present
|
|
network: podsteadr-net
|
|
network_aliases: [blossom]
|
|
# Image runs as container-root (no USER directive) writing to a
|
|
# bind-mounted /app/data — CHOWN/DAC_OVERRIDE cover the fresh-bind-dir
|
|
# ownership gap the same way apps/botfights and apps/immich document.
|
|
# Unverified against a real install; check first-boot logs.
|
|
data_uid: "0:0"
|
|
|
|
dependencies:
|
|
- storage: 20Gi
|
|
|
|
resources:
|
|
cpu_limit: 1
|
|
memory_limit: 512Mi
|
|
disk_limit: 20Gi
|
|
|
|
security:
|
|
capabilities: [CHOWN, DAC_OVERRIDE, FOWNER]
|
|
readonly_root: false
|
|
no_new_privileges: true
|
|
network_policy: isolated
|
|
|
|
ports:
|
|
- host: 8098
|
|
container: 3000
|
|
protocol: tcp
|
|
auth: none
|
|
auth_rationale: >-
|
|
Media blobs (episode audio/video, covers) must be publicly fetchable
|
|
by podcast clients as RSS enclosure URLs — that's the entire purpose
|
|
of this port. Uploads are separately gated by blossom's own BUD-02
|
|
signed-nostr-event auth (upload.requireAuth below), not a node
|
|
session; reads are intentionally public per the config's own header
|
|
comment.
|
|
|
|
volumes:
|
|
- type: bind
|
|
source: /var/lib/archipelago/podsteadr-blossom/data
|
|
target: /app/data
|
|
options: [rw]
|
|
- type: bind
|
|
source: /var/lib/archipelago/podsteadr-blossom/config/config.yml
|
|
target: /app/config.yml
|
|
options: [ro]
|
|
|
|
environment: []
|
|
|
|
files:
|
|
- path: /var/lib/archipelago/podsteadr-blossom/config/config.yml
|
|
overwrite: true
|
|
content: |
|
|
# blossom-server (v4.x) configuration for podsteadr.
|
|
# Uploads require a signed nostr auth event (BUD-02, kind 24242);
|
|
# reads are public so podcast apps can fetch enclosures.
|
|
#
|
|
# NOTE (blossom-server 4.4.1 gotcha, do not rediscover): `rules:` MUST
|
|
# be nested under `storage:` — a top-level `rules:` key is silently
|
|
# ignored, the ruleset ends up empty, and every upload fails 401
|
|
# "Server dose not accept video/mp4 blobs" (typo is theirs). The
|
|
# GitHub master branch is a Deno rewrite with a different schema
|
|
# (storage.rules, BUD-11, range support); the `:4` image is the older
|
|
# node/koa codebase this config targets.
|
|
|
|
publicDomain: ""
|
|
|
|
databasePath: data/sqlite.db
|
|
|
|
dashboard:
|
|
enabled: false
|
|
|
|
discovery:
|
|
nostr:
|
|
enabled: false
|
|
relays: []
|
|
upstream:
|
|
enabled: false
|
|
domains: []
|
|
|
|
storage:
|
|
backend: local
|
|
local:
|
|
dir: ./data/blobs
|
|
removeWhenNoOwners: false
|
|
# "expiration" is time since a blob was last accessed — unaccessed
|
|
# blobs get pruned after this. Podcast media should effectively
|
|
# never expire, so keep this long.
|
|
rules:
|
|
- type: "*"
|
|
expiration: 10 years
|
|
|
|
upload:
|
|
enabled: true
|
|
requireAuth: true
|
|
requirePubkeyInRule: false
|
|
|
|
list:
|
|
requireAuth: false
|
|
allowListOthers: true
|
|
|
|
tor:
|
|
enabled: false
|
|
proxy: ""
|
|
|
|
health_check:
|
|
# No documented health endpoint; TCP liveness on the app port.
|
|
type: tcp
|
|
endpoint: localhost:3000
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 3
|