ShuffleMuse 只从进程环境读取配置,不读取 .env。配置在启动时加载并校验,修改后必须重启进程或重新创建 Compose 服务。
下表的“程序默认值”来自 internal/config/config.go;“Compose 值”来自当前 docker-compose.yml。
| 变量 | 程序默认值 | Compose 值 | 规则与作用 |
|---|---|---|---|
MUSIC_DIR |
/music |
/music |
音乐根目录,不能为空且运行时必须是可访问目录 |
MUSIC_PORT |
8080 |
8080 |
HTTP 监听端口,范围 1–65535 |
MUSIC_RESCAN_INTERVAL |
0 |
0 |
Go duration,不得小于 0;0 关闭定时重扫,正数启用对应周期 |
MUSIC_OPUS_BITRATE |
160 |
160 |
FFmpeg Opus 目标码率,单位 kbps,必须大于 0 |
MUSIC_PASSWORD |
空 | 空 | 空值完全关闭认证;非空启用单密码 Session |
MUSIC_AUTH_WHITELIST_SUBNETS |
空 | 空 | 免登录的直连 IP/CIDR,逗号分隔 |
MUSIC_LOGIN_MAX_FAILURES |
3 |
3 |
单客户端连续密码错误阈值,必须大于 0 |
MUSIC_LOGIN_BAN_SECONDS |
3600 |
3600 |
达到阈值后的固定封禁秒数,必须大于 0 |
MUSIC_TRUSTED_PROXY_SUBNETS |
空 | 空 | 有权提供真实客户端 IP 的代理 IP/CIDR,逗号分隔 |
MUSIC_REAL_IP_HEADER |
remote |
remote |
remote、x-forwarded-for、cf-connecting-ip 三选一 |
MUSIC_COOKIE_SECURE |
false |
false |
为 Session Cookie 添加 Secure;HTTPS 部署应设为 true |
MUSIC_ALLOWED_HOSTS |
localhost,127.0.0.1,::1 |
同默认 | 允许的 Host 名/IP/authority,逗号分隔且至少一项 |
MUSIC_FFMPEG_MAX_SESSIONS |
2 |
2 |
所有 FFmpeg 与 ffprobe 子进程的硬总上限,必须大于 0 |
MUSIC_MEDIA_AUX_RESERVED_SESSIONS |
总数为 1 时 0,否则 1 |
1 |
专供 metadata/封面的槽位;必须非负且小于总数。未设置且总数为 1 时兼容旧共享模式 |
MUSIC_MEDIA_QUEUE_LIMIT |
8 |
8 |
Opus 转码最多等待数;可为 0 |
MUSIC_MEDIA_AUX_QUEUE_LIMIT |
8 |
8 |
metadata/封面最多等待数;可为 0 |
MUSIC_MEDIA_WAIT_SECONDS |
15 |
15 |
等待媒体槽位的最长秒数,必须大于 0 |
MUSIC_MEDIA_TASK_SECONDS |
15 |
15 |
metadata、封面底层任务和 Opus 首字节最长秒数,必须大于 0 |
MUSIC_STREAM_WRITE_IDLE_SECONDS |
60 |
60 |
Opus 首字节后滚动写空闲 deadline,必须大于 0 |
MUSIC_MEDIA_NEGATIVE_CACHE_SECONDS |
30 |
30 |
确定性 metadata 失败和封面未找到的负缓存 TTL |
MUSIC_METADATA_CACHE_ENTRIES |
4096 |
4096 |
metadata 与内嵌封面 descriptor 共享 probe LRU 的最大条目数,必须大于 0 |
MUSIC_COVER_CACHE_ENTRIES |
128 |
128 |
兼容变量;小型封面 descriptor LRU 条目上限,不缓存图片字节 |
MUSIC_COVER_CACHE_BYTES |
67108864 |
67108864 |
兼容变量;descriptor 估算内存上限,不缓存图片字节 |
MUSIC_QUEUE_CACHE_MAX_QUEUES |
64 |
64 |
服务端随机队列数量上限 |
MUSIC_QUEUE_CACHE_BYTES |
134217728 |
134217728 |
共享快照、uint32 顺序和队列管理数据的总预算 |
MUSIC_QUEUE_IDLE_SECONDS |
86400 |
86400 |
队列最后访问后的过期秒数 |
MUSIC_LEGACY_MUSIC_ROOT |
空 | 空 | 旧版绝对标签路径的精确根目录;非空时必须是绝对路径 |
MUSIC_BOLTDB_PATH |
./data/tags.db |
/data/tags.db |
bbolt 数据库文件;父目录必须可写 |
列表值会去除两端空白,单个 IP 会转换为对应的单地址 CIDR,重复网段会去重。显式设置的整数、布尔和 duration 若无法解析,服务会拒绝启动,并在错误中指出变量名和值;已成功解析但超出规则的值同样会使启动失败。只有变量未设置或为空时才使用默认值。
MUSIC_RESCAN_INTERVAL=0 只关闭首次索引建立之后的定时重扫,不会关闭冷启动首次扫描,也不会关闭 Browse 的手动 Rescan。设置 5m、1h 等正数 Go duration 可重新启用周期重扫。
MUSIC_PASSWORD 为空时:
- 所有曲库 API 均无需登录;
/api/auth/login和/api/auth/logout不注册;- 认证白名单被忽略;
- 服务启动日志会打印认证关闭警告。
Compose 同时使用空密码和 127.0.0.1 端口绑定,以便默认只从宿主机访问。不要只改成 0.0.0.0 而继续使用空密码。
启用密码后,登录成功会设置 shufflemuse-session Cookie:
- Token 使用 32 字节密码学随机数;服务端只保存 SHA-256 摘要;
HttpOnly、SameSite=Lax、Path=/;- 普通 Session 固定有效 1 小时;“Remember me”固定有效 30 天;
MUSIC_COOKIE_SECURE=true时增加Secure;- 最多保存 1024 个 Session,过期项和最久未访问项会被清理;
- Session 不持久化,服务重启后全部失效;
- Logout 只撤销已知 Token,伪造 Cookie 不会扩大 Session 存储。
ShuffleMuse 不提供用户名、角色或细粒度权限。知道同一个密码的访问者拥有相同权限。
错误密码按解析后的客户端 IP 计数。达到 MUSIC_LOGIN_MAX_FAILURES 时立即返回:
HTTP/1.1 429 Too Many Requests
Retry-After: 3600
Content-Type: application/json
{"code":"LOGIN_IP_BLOCKED","error":"too many failed login attempts"}封禁截止时间在触发时固定,封禁期间继续请求不会延长;成功登录或封禁到期会清除状态。畸形 JSON、未知字段和超大登录请求不会计入密码失败。状态只在内存中,重启后清空。
登录失败状态有固定的 4096 个客户端全局上限,不提供配置开关。达到上限时先清理已到期封禁,再淘汰最久未使用的未封禁失败计数;有效封禁不会为了接纳新来源而被取消。若 4096 项全部是有效封禁,新来源的错误密码本次直接得到 429,但不会新增状态键;它之后提交正确密码时仍能进入正常验证流程。
MUSIC_AUTH_WHITELIST_SUBNETS 与 MUSIC_TRUSTED_PROXY_SUBNETS 不能互换:
| 配置 | 影响 | 地址来源 |
|---|---|---|
| 认证白名单 | 是否完全免登录 | 始终只看 TCP 直连对端 |
| 可信代理 | 登录失败归属于哪个客户端 IP | 仅为真实 IP 头授信 |
把反向代理、Docker 网桥或 Tunnel 对端加入认证白名单,会使所有经过它的用户免登录。正确做法通常是:代理网段只加入 MUSIC_TRUSTED_PROXY_SUBNETS,仍要求最终用户输入密码。
始终使用 TCP 对端,忽略所有转发头。没有经过可信代理,或者不能确认代理行为时使用此模式。
只有 TCP 对端命中可信代理网段时才解析 X-Forwarded-For。ShuffleMuse 从右向左剥离可信代理地址,取第一个不可信地址。下列情况回退到 TCP 对端:
- 对端不可信或可信代理列表为空;
- 头缺失;
- 任意链元素不是合法 IP;
- 整条链都属于可信代理。
代理必须覆盖而不是简单追加来自外部客户端的伪造头,并应按实际链路把所有可信跳点配置完整。
只有 TCP 对端可信时读取唯一的 CF-Connecting-IP。头缺失、重复、包含逗号或不是合法单一 IP 时回退到 TCP 对端。这是 Cloudflare Tunnel 常用模式,但可信网段必须填写 ShuffleMuse 实际看到的 Tunnel 对端地址,不能照抄与本机网络不符的示例。
每个 API 和 SPA 请求都会先执行 Host 校验。MUSIC_ALLOWED_HOSTS 可填写:
- 主机名:
music.example.com; - IP:
192.168.1.20; - 明确 authority:
music.example.com:8443。
只填写主机名时会接受该主机的任意端口;填写带端口 authority 时可精确匹配该 authority。未命中返回 400 INVALID_HOST。
对 POST 和 DELETE:
Sec-Fetch-Site: cross-site返回403 CSRF_BLOCKED;- 如果存在
Origin,其 authority 必须与请求 Host 一致; - 没有
Origin的 CLI 请求保持兼容。
全局响应头包含 CSP、X-Frame-Options: SAMEORIGIN、X-Content-Type-Options: nosniff、Referrer-Policy: same-origin 和限制权限的 Permissions-Policy。ShuffleMuse 不终止 TLS,也不设置 HSTS;公网 HTTPS 应由反向代理负责。
当前 Compose 默认值已经适合:
ports:
- "127.0.0.1:8080:8080"
environment:
MUSIC_PASSWORD: ""
MUSIC_ALLOWED_HOSTS: "localhost,127.0.0.1,::1"
MUSIC_REAL_IP_HEADER: "remote"ports:
- "0.0.0.0:8080:8080"
environment:
MUSIC_PASSWORD: "replace-with-a-long-random-password"
MUSIC_ALLOWED_HOSTS: "music.lan,192.168.1.20"
MUSIC_REAL_IP_HEADER: "remote"
MUSIC_COOKIE_SECURE: "false"局域网也可以由 HTTPS 反向代理接入;此时应启用 Secure Cookie。
environment:
MUSIC_PASSWORD: "replace-with-a-long-random-password"
MUSIC_ALLOWED_HOSTS: "music.example.com"
MUSIC_TRUSTED_PROXY_SUBNETS: "TRUSTED_PROXY_CIDR"
MUSIC_REAL_IP_HEADER: "x-forwarded-for"
MUSIC_COOKIE_SECURE: "true"TRUSTED_PROXY_CIDR 必须替换为容器实际看到的代理来源网段。若代理转发的 Host 与浏览器访问 authority 不一致,Origin 校验会拒绝写请求。
environment:
MUSIC_PASSWORD: "replace-with-a-long-random-password"
MUSIC_ALLOWED_HOSTS: "music.example.com"
MUSIC_TRUSTED_PROXY_SUBNETS: "ACTUAL_TUNNEL_PEER_CIDR"
MUSIC_REAL_IP_HEADER: "cf-connecting-ip"
MUSIC_COOKIE_SECURE: "true"先从 ShuffleMuse 请求日志、容器网络或反向代理配置确认 ACTUAL_TUNNEL_PEER_CIDR,不要把 Cloudflare 公网访客网段误当作本地 Tunnel 对端。
MUSIC_FFMPEG_MAX_SESSIONS 是所有子进程的硬总上限。默认总数 2、辅助保留 1,形成两个不互借的 lane:最多一个长 Opus 转码,同时保留一个 metadata/封面任务。原文件串流和普通文件下载不占用这个配额。
- CPU 足够且同时用户较多时,可谨慎增加并发;
- 内存或 CPU 较小时,保持
2或降低; MUSIC_MEDIA_QUEUE_LIMIT=0或MUSIC_MEDIA_AUX_QUEUE_LIMIT=0分别关闭对应 lane 的等待队列;- 队列满或等待超时会使转码、元数据和需要内嵌提取的封面请求返回
503 MEDIA_BUSY; - 已取得槽位但执行超过 deadline 返回
504 MEDIA_TIMEOUT; - 客户端断开会取消等待并释放队列位置;
- metadata 和封面相同身份的并发请求会在取得槽位前合并;封面转换只共享仍在进行的工作,完成后不保留图片结果;单个等待者取消不影响其他等待者;
- 辅助 lane 内 metadata 与 cover descriptor 优先于封面 JPEG 转换,但连续 4 个高优先级任务后会让等待中的转换执行一次;
- 元数据缓存键包含路径、大小和 mtime;确定性失败短暂负缓存,busy、超时和取消不缓存。
封面 descriptor 和 30 秒负缓存仍由上述兼容变量约束。转换结果只存在于当前请求或同一 in-flight 请求组中,响应结束后释放;浏览器凭 private, max-age=3600 和 ETag 复用结果。服务端不创建派生图片、磁盘缓存或图片字节 LRU。
每次随机化用独立系统熵播种 ChaCha8,再执行均匀 Fisher–Yates 洗牌。队列只保存 uint32 索引,并让同 generation 的队列共享只读曲库快照。浏览器固定每页读取 200 首且最多缓存 5 页。
- 新队列创建固定最多并发 2 个、等待 4 个、等待 5 秒;这些边界不可配置;
- 创建时清理 TTL 项,并在数量或字节预算不足时按最后访问时间淘汰;
- 单个队列连同其首次引用快照超过字节预算时返回
503 QUEUE_CAPACITY; - 正常重扫不重排现有队列:删除项变为
available:false,新增项等下一次新建/Randomize; - 默认 128 MiB 预算面向数万到约十万首曲库;极大曲库应按实际
FileEntry字符串和 4 字节/候选顺序数组评估。
已认证的 /api/status 会返回当前生效的 opusBitrate,Web UI 的模式按钮和播放详情会据此显示实际转码码率。
标签数据库以相对 MUSIC_DIR 的路径为当前格式。设置 MUSIC_LEGACY_MUSIC_ROOT 后,每次成功扫描发布前会尝试迁移旧绝对路径,但只在同时满足以下条件时转换:
- 旧路径确实位于配置的精确 legacy root 内;
- 转换后的相对路径存在于本次成功扫描;
- legacy root 是绝对路径。
无法明确转换的记录不会按任意后缀猜测,而是保留并出现在 Graveyard。完成迁移并确认数据后可清空该配置。