- Go 78.7%
- Python 10.5%
- Nix 6.9%
- Shell 2.8%
- Dockerfile 1.1%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .forgejo/workflows | ||
| cmd/plexdrome | ||
| internal | ||
| tools/synthseek-capture | ||
| .dockerignore | ||
| .gitignore | ||
| Dockerfile | ||
| flake.lock | ||
| flake.nix | ||
| go.mod | ||
| LICENSE | ||
| README.md | ||
plexdrome
Copyright © 2026 Nimmo. Licensed under the European Union Public Licence (EUPL), version 1.2.
This licence applies to the original source code, documentation, and capture tooling in this repository. Third-party software, container base images, and Nixpkgs inputs remain under their respective upstream licences.
Minimal Plex compatibility layer for Synthseek, backed by an OpenSubsonic server. Lightweight Music Server (LMS) is the initial deployment target; the adapter boundary remains server-neutral for future backend changes.
Run
Plexdrome is configured entirely through environment variables. At minimum it needs a private Plex token and stable machine identifier, plus an OpenSubsonic endpoint and either an API key or user credentials:
export PLEXDROME_PLEX_TOKEN='…'
export PLEXDROME_MACHINE_ID='…'
export OPEN_SUBSONIC_URL='http://navidrome:4533'
export OPEN_SUBSONIC_API_KEY='…'
nix run .
It listens on :32400 by default. Set PLEXDROME_LISTEN_ADDR to override it.
The supported Plex-facing contract is deliberately small: identity and library
discovery, scan requests, track search, and playlist create/read/append/rename.
/healthz checks process health; /readyz verifies OpenSubsonic connectivity.
| Variable | Purpose |
|---|---|
PLEXDROME_PLEX_TOKEN |
Required shared token accepted from Synthseek. |
PLEXDROME_MACHINE_ID |
Required stable identifier used in Synthseek playlist URIs. |
OPEN_SUBSONIC_URL |
Required private URL for Navidrome, LMS, or another OpenSubsonic server. |
OPEN_SUBSONIC_API_KEY |
Required LMS per-user API token (sent as OpenSubsonic apiKey). |
OPEN_SUBSONIC_USERNAME, OPEN_SUBSONIC_PASSWORD |
Optional conventional/salted-token fallback for compatible backends. |
PLEXDROME_SCAN_MODE |
auto (default) asks the backend to scan; noop acknowledges scans without a backend operation. |
Logs contain only request method, path, status, latency, and backend operation
outcome; query values and authentication material are never logged. A backend
playlist marked readonly is rejected with 409, while an unsupported scan is
an acknowledged no-op so completed imports are not retried indefinitely.
Deployment
Place Plexdrome and Synthseek on the same internal Docker network on Chaos and
configure Synthseek with http://plexdrome:32400 plus the configured Plex
token. Do not publish the port unless you need a short-lived diagnostic tunnel.
Set OPEN_SUBSONIC_URL to Vega's private endpoint; no music mount is required.
The image runs as a non-root user and only needs outbound access to that backend.
Use /healthz for container liveness (it never contacts the backend) and
/readyz for readiness (it calls OpenSubsonic ping). A readiness failure
usually means private-network reachability or OpenSubsonic credentials need
attention. The intentionally unsupported Plex surface includes OAuth, plex.tv
discovery, media streaming, and all unobserved routes.
Build the OCI image locally with nix build .#container; the result is a Docker
image archive suitable for docker load.
Image distribution
Every push to main builds and publishes the image through Forgejo Actions:
docker pull git.nimmog.uk/nimmo/plexdrome:latest
The same build also receives sha-<full-commit-id>, which is immutable and
appropriate when a deployment needs a precise rollback target. NixOS and
ordinary Docker hosts consume these identical registry references; the Nix
container archive is retained for offline/local use.
Capture tooling
The disposable Synthseek/Plex traffic-capture harness is isolated from the
product under tools/synthseek-capture. It is kept
for re-running contract discovery when Synthseek changes.
From the repository root:
nix run ./tools/synthseek-capture
See the tool's README for its lifecycle, security model, and capture workflow.