forked from perminder-klair/subwave
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path.env.example
More file actions
259 lines (244 loc) · 15 KB
/
Copy path.env.example
File metadata and controls
259 lines (244 loc) · 15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# ═══════════════════════════════════════════════════════════════
# SUB/WAVE — required configuration
# ═══════════════════════════════════════════════════════════════
#
# Just three vars are required to boot the stack. Everything else
# (Navidrome, LLM, TTS engine, DJ persona, …) is collected by the
# first-run wizard at http://<your-host>/onboarding after the stack is up.
#
# The wizard writes those to state/settings.json, which is the
# runtime source of truth. The optional overrides below are kept
# for 12-factor / CI / GitOps style deploys where env beats UI.
# Admin credentials gate the wizard + admin UI. Mandatory in prod
# (the controller refuses to boot without them when NODE_ENV=production).
ADMIN_USER=admin
ADMIN_PASS= # generate one: openssl rand -hex 16
# Public origin (used for absolute URLs, OG tags, manifest, sitemap).
# Example: https://radio.example.com
SITE_URL=
# ═══════════════════════════════════════════════════════════════
# OPTIONAL — sensible defaults, uncomment to override
# ═══════════════════════════════════════════════════════════════
# Storage + locale
# STATE_DIR=./state
# TZ=Europe/London
# Web
# SUBWAVE_HOMEPAGE=player # or 'landing' for the marketing host
# SUBWAVE_INDEX_ALL=1 # index the shared docs/news/catalog pages on
# # THIS domain too; default points their
# # canonicals at getsubwave.com so search
# # engines credit one copy
# NEXT_PUBLIC_GA_ID= # Google Analytics ID. Applied at RUNTIME
# # (recreate `web`, no rebuild) as well as at
# # build time. Unset → no analytics.
# Pin the whole stack to a specific published image tag. Every subwave-* image
# ref resolves ${SUBWAVE_VERSION:-latest}, so unset/removed follows :latest.
# The `subwave` CLI writes a concrete pin here at `init` (matching the CLI's
# release) and moves it forward on `subwave update`. Published tags are bare
# semver (e.g. 0.35.0 or 0.35), plus `latest`.
# SUBWAVE_VERSION=latest
# ───────── Ports (byo-proxy compose only) ─────────
# CADDY_PORT=7700
# WEB_PORT=7700
# CONTROLLER_PORT=7701
# ICECAST_PORT=7702
# Host interface the byo ports bind to. Defaults to 0.0.0.0 (all interfaces).
# Set to 127.0.0.1 when your reverse proxy runs on THIS host, so the admin
# API isn't reachable from the network directly.
# BIND_ADDRESS=0.0.0.0
# ───────── Icecast secrets ─────────
# Leave blank — the broadcast image auto-generates these on first boot and
# writes them to state/icecast-secrets.env. Override here only if you need
# specific passwords (e.g. broadcasting to an external relay).
# To rotate: delete state/icecast-secrets.env and restart broadcast +
# controller. (The merged container holds both icecast and liquidsoap, so
# the old three-way restart is collapsed into one bounce.)
# ICECAST_SOURCE_PASSWORD=
# ICECAST_ADMIN_PASSWORD=
# ICECAST_RELAY_PASSWORD=
# Max concurrent listeners across all mounts (icecast <limits><clients>).
# Unset → whatever admin → Settings → Danger zone → Max listeners says
# (default 100). Setting it here OVERRIDES that field, which is why the
# broadcast log names the source it used on every boot. Prefer the admin
# field unless you deploy 12-factor style; on the AIO image the field is the
# only way in, since there is no .env there at all.
# ICECAST_MAX_CLIENTS=
# ───────── Real listener IPs behind a proxy ─────────
# Icecast only ever sees the reverse proxy in front of it, so admin →
# Listeners shows that proxy's address (the 172.x rows) rather than the
# listener's, unless icecast is told which proxy to believe.
#
# Unset, the default compose resolves its bundled `caddy` by name each time
# the broadcast container starts. That covers the steady state but NOT the
# first cold boot (caddy can't be running yet — it waits on broadcast's
# healthcheck), so listener rows read as the proxy IP until the next restart.
#
# Set this to the proxy's address — as icecast sees it — for correctness from
# the first boot, and whenever the proxy isn't a compose service resolvable
# from the broadcast container (the byo compose, a host-network nginx). Space
# or comma separated; must be EXACT addresses, as icecast accepts a CIDR and
# then silently never matches it.
# ICECAST_TRUSTED_PROXY_IPS=172.20.0.100
#
# Names to resolve instead of hardcoding an address. Unset → `caddy`.
# ICECAST_TRUSTED_PROXY_HOSTS=caddy
# Trust Cloudflare's CF-Connecting-IP header as the listener's real address in
# the CONTROLLER (the separate, icecast-side setting is above). This is the
# identity every per-IP limit keys on: the request cooldown and hourly caps,
# the admin brute-force lockout, the station-password throttle, and like dedup.
#
# Leave it unset unless Cloudflare (proxied DNS or a tunnel) is the ONLY way
# traffic reaches this stack. Unset, the controller uses X-Forwarded-For, which
# the bundled Caddy only passes through for peers in its trusted_proxies list
# and otherwise replaces with the real peer — sound on its own. CF-Connecting-IP
# gets no such filtering, so trusting it when anyone can reach the origin
# directly lets a single spoofed header defeat every limit listed above.
# TRUST_CF_CONNECTING_IP=1
# ───────── Overrides for the wizard's fields ─────────
# These all live in state/settings.json after the wizard runs. Set them here
# only if you want env to win (12-factor / CI / GitOps style deploys).
# NAVIDROME_URL=http://host.docker.internal:4533
# NAVIDROME_USER=
# NAVIDROME_PASS=
# Optional: play tracks straight off disk instead of streaming them over HTTP.
# Only worth setting when the controller container mounts the SAME library
# Navidrome scans. Note Navidrome's Subsonic `path` is built from tags, not read
# off disk, so it often disagrees with your real folder names (e.g. the API says
# "Chunga's Revenge" where the folder is "Chunga's Revenge (1970)"). Every
# mismatch falls back to streaming, so it is safe to leave on — it just does
# nothing for the tracks whose paths don't line up.
# MUSIC_LIBRARY_PATH=
# TTS_SPEED=0.85
# AUTO_QUEUE_REFRESH_MINUTES=60
# Convenience only — seeds the News skill's feed on first boot. After that the
# file wins: edit it in /admin/skills or state/skills/news/SKILL.md (changing
# these vars won't move an already-scaffolded feed).
# NEWS_FEED_URL=http://feeds.bbci.co.uk/news/rss.xml
# NEWS_MAX_ITEMS=10
# ───────── Cloud LLM / TTS API keys ─────────
# The wizard accepts these in a form and writes them to state/secrets.env
# (which the controller sources on boot). Set here only for IaC-style provisioning.
# ANTHROPIC_API_KEY=
# OPENAI_API_KEY=
# GOOGLE_GENERATIVE_AI_API_KEY=
# OPENROUTER_API_KEY=
# REQUESTY_API_KEY=
# DEEPSEEK_API_KEY=
# AI_GATEWAY_API_KEY=
# ELEVENLABS_API_KEY=
# FISH_API_KEY=
# SEARCH_API_KEY=tvly-... # Tavily or Brave Search key (optional, paid/metered)
# SearXNG: no env var. Configure base URL in admin UI → Settings → Search.
# Embedding key — only when embeddings use a different provider/key than chat
# (e.g. OpenRouter for embeddings, a local proxy for chat). Blank → inherit the
# chat key. OpenRouter embeddings also fall back to OPENROUTER_API_KEY above.
# EMBEDDING_API_KEY=
# ───────── Scrobbling (optional — also configurable in admin UI) ─────────
# LISTENBRAINZ_USER_TOKEN= # ListenBrainz or LB-compatible scrobbler token
# LISTENBRAINZ_API_URL= # optional full submit-listens URL override
# # e.g. http://koito:4110/apis/listenbrainz/1/submit-listens
# # otherwise set base URL in admin → Settings → Scrobbling
# ───────── Acoustic analysis (CLAP audio-similarity + Demucs vocals) ─────────
# The analysis pass (npm run analyze / admin "Analyze audio") computes bpm, key,
# loudness, structure, pace and a beat grid for every track. This runs by
# default in the lean, multi-arch `analyzer` sidecar — no config needed.
#
# Two heavier dimensions — CLAP "sounds-like" embeddings and Demucs vocal ranges
# — need a CPU-torch stack that's NOT in the default image. Enable them by
# pulling the heavy analyzer image (a one-liner, no rebuild):
# ANALYZER_HEAVY=1 # switch the `analyzer` service to subwave-analyzer-heavy
# # (CLAP + Demucs, ~1.9GB). amd64-only; on an arm64 host also
# # set DOCKER_DEFAULT_PLATFORM=linux/amd64 (runs emulated).
# # Unraid one-click (AIO) users instead pull subwave-aio-heavy.
#
# NVIDIA GPU? The heavy stack can run on CUDA instead — not an .env toggle (a
# GPU device reservation can't live here); layer the analyzer-gpu overlay:
# docker compose -f docker-compose.yml -f docker-compose.analyzer-gpu.yml up -d
# (AIO one-click users: no overlay — pull subwave-aio-cuda and pass the GPU to
# the container. See docs/unraid.md.)
#
# GPU in a DIFFERENT machine? Run the analyzer there and point the station at
# it — no need to move the stack or copy state around. A same-path state mount
# is optional but faster; it is required for remote
# stem caching. Without one, analysis retries by URL after the path probe:
# docs/tts-heavy.md#running-the-analyzer-on-another-machine
# ANALYZE_URL=http://192.168.1.101:8080 # overrides the in-compose analyzer
# ANALYZE_DEVICE= # auto (default) / cpu / cuda — torch device for CLAP/Demucs;
# # only meaningful on the cuda analyzer flavour
# ANALYZE_IDLE_UNLOAD_S= # seconds of no CLAP/Demucs use before the models are
# # dropped (0 = keep resident). Defaults per device:
# # 300 on cuda (frees the GPU for co-resident TTS/LLM
# # between passes), 1800 on cpu (a backfill or sound
# # search loads them once and they'd otherwise sit in
# # RAM/swap forever; the longer window keeps the cold
# # reload off interactive sound searches).
# ANALYZE_RECYCLE_IDLE_S= # sidecar only: seconds of no heavy use before the
# # whole worker process is recycled (default 3600;
# # 0 = never). The model release above hands back the
# # weights; the recycle also reclaims the ~1GB of
# # librosa/numba/torch scratch — and on cuda the CUDA
# # context — that survive it. Next request re-pays the
# # worker boot (a few seconds of imports).
#
# Runtime flags — env wins ON over the admin toggles (settings.audio.*), never
# off. A flag with no matching backend in the image is a clean no-op.
# ANALYZE_AUDIO_EMBEDDING= # 1/true = fill CLAP audio vectors (needs ANALYZER_HEAVY)
# ANALYZE_VOCAL_ACTIVITY= # 1/true = fill Demucs vocal ranges (needs ANALYZER_HEAVY)
# ANALYZE_QUIET_ONLY= # 1/true = pause analysis while anyone is listening;
# # resumes after settings.audio.analyzeQuietMinutes
# # (default 10) with no listeners (#1099)
#
# Building from source instead of pulling? `docker compose build analyzer` with
# ANALYZER_HEAVY=1 bakes the stack (or pass WITH_CLAP=1 / WITH_DEMUCS=1 directly).
#
# Vocal-gate tuning (#1125). The Demucs vocal stem is thresholded against BOTH
# its own loud level AND a fraction of the FULL-MIX loud level — the mix floor
# stops separation bleed on instrumentals from reading as vocals. Raise the
# floor if instrumentals still slip through (over-compressed masters leak more);
# lower it if quiet vocals get dropped. Defaults shown.
# VOCAL_MIX_FLOOR=0.06 # vocal stem must reach >=6% of the full-mix loud level
# VOCAL_STEM_REL=0.15 # ...OR >=15% of the vocal stem's own loud level
#
# Optional model overrides (sensible defaults shown):
# CLAP_MODEL=laion-clap
# DEMUCS_MODEL=htdemucs # vocal separation is the slow pass, and the default
# # is also the fastest on CPU we've measured — the
# # mdx*/*_q alternatives are bags of 4 sub-models and
# # run ~3x SLOWER despite the smaller download
# ANALYZE_SECONDS=40 # per-track analysis window (seconds), shared by
# # bpm/key, CLAP and Demucs — the real speed lever
# # (cost scales linearly). Raise it if you want vocal
# # ranges detected deeper into each track
# ANALYZE_CLAP_WINDOWS=3 # how many windows the CLAP "sounds-like" embed
# # averages over (1-3). 3 = start/mid/late (default,
# # best vectors), 1 = leading window only (fastest,
# # the pre-multi-window behaviour). CLAP cost per
# # track scales linearly with this
# ANALYZE_OUTRO_SECONDS=20 # tail window (seconds) decoded from the END of each
# # complete file for outro analysis (fade-vs-cold
# # ending, tail loudness/tempo — drives the
# # ending-aware crossfade). Pure librosa, cheap
#
# Which heavy-TTS engines the tts-heavy sidecar loads. Chatterbox AND PocketTTS
# are both baked into the image, but each costs RAM + a first-boot weight
# download + startup time — so if you only use one, name it here and the other
# never loads. Comma-separated; default loads both. Only matters with
# --profile tts-heavy.
# TTS_HEAVY_ENGINES=pocket-tts # or: chatterbox | chatterbox,pocket-tts
#
# Own TTS server? There's nothing to set here — the *Remote* engine points the
# DJ at any HTTP server that answers GET /health and returns rendered audio from
# POST /speak (a GPU box, a Tailscale peer, a bridge in front of a vendor API
# like Gemini TTS). It's a station setting, not env: admin → Settings → Voices →
# Remote. Contract + a 20-line reference server: docs/custom-tts.md
#
# Memory ceilings for the model-loading sidecars (OOM containment — keeps a
# runaway model load from taking down the host's other services). Defaults are
# generous; raise for the heavy analyzer on large libraries, lower on a
# constrained host. TTS_HEAVY_MEM_LIMIT only matters with --profile tts-heavy.
# ANALYZER_MEM_LIMIT=6g
# TTS_HEAVY_MEM_LIMIT=10g
#
# HuggingFace token — lets the tts-heavy sidecar lazily download gated model
# weights at runtime (PocketTTS cloning weights; some CLAP/Demucs checkpoints).
# HF_TOKEN=