Remedia

Server configuration

Strict configuration, environment references, media access, and observability.

Create a JSON configuration before starting the server. The default path is ./remedia.config.json; --config PATH selects another file. Unknown fields, invalid ranges, duplicate accounts, and unresolved environment values fail startup.

remedia init

The generated file listens on loopback and denies media access until you allow sources. For secret loading, see Environment and secrets. For all listener, account, media, and capacity defaults, use the field reference.

auth.password is the only required single-user setting. Its username defaults to remedia; optionally set auth.username. For multiple accounts use users: [{ username, password }] instead. Exactly one of auth or nonempty users is required. Credentials support environment references. listen defaults to 127.0.0.1:8787; use an IP and port, such as [::1]:8787 for IPv6. Audio, limits, observability and media policy are optional and retain their defaults.

remedia init and --generate-config-file [PATH] create only schema metadata and auth.password with a cryptographically random password. Existing files are never overwritten. On Unix the file is created with mode 0600. Read credentials from the file; the CLI never prints them.

This configuration format is breaking: bind, media.allowHttps, plugin-host endpoint, allowInsecureHttp and allowPrivateNetwork are rejected. There are no aliases or fallback parsers.

Crossfade capability

The server config accepts an optional audio.crossfade object. It is off by default. Set enabled: true to permit individual guild sessions to opt in.

{
  "audio": {
    "crossfade": {
      "enabled": false,
      "durationMs": 5000,
      "prepareBeforeMs": 30000,
      "curve": "equalPower"
    }
  }
}

node.info.capabilities.crossfade reports configured separately from available. Clients must check available: true before opting in. Until crossfade is enabled in server policy, the server reports available: false. When enabled, clients must also pass crossfade: { enabled: true } to guild session creation. Existing sessions without opt-in retain single-source playback.

prepareBeforeMs is the lead time before the crossfade begins, rather than before the outgoing source completes. For example, a 5-second fade and 30-second preparation lead means the prepare event would occur approximately 35 seconds before the track ends.

Schema and editor completion

The workspace package @remedia/config exports RemediaConfigSchema (Zod 4), the RemediaConfig input type, and generateJsonSchema(). Its generated draft 2020-12 artifact is served with the docs at remedia.config.schema.json. The same artifact is distributed as @remedia/config/schema.json.

{
  "$schema": "https://remedia.twlite.dev/schemas/remedia.config.schema.json",
  "auth": { "password": "env('REMEDIA_PASSWORD')" }
}

The configuration generator includes this metadata automatically. Rust accepts $schema without fetching it and continues rejecting every other unknown key. VS Code and JetBrains JSON-aware editors can resolve the URI for completion, field descriptions, types, enums, numeric bounds, and defaults. Before the docs artifact is deployed, use the local generated file through a relative $schema path or an editor association, for example in VS Code workspace settings:

{
  "json.schemas": [
    {
      "fileMatch": ["remedia.config.json"],
      "url": "./packages/remedia-config/remedia.config.schema.json"
    }
  ]
}

The generated artifact is the field reference, avoiding a separately maintained settings table. Rebuild with pnpm --filter @remedia/config schema. CI runs schema:check and a validation corpus shared with Rust. Runtime schema parsing applies defaults but never expands environment references or accesses directories. Custom refinements such as UTF-8 byte lengths, duplicate usernames, cross-field player limits, and normalized host restrictions require runtime validation. Rust also validates resolved environment values and filesystem access at startup.

Opus encoder settings

{
  "audio": {
    "opus": {
      "bitrate": 128000,
      "complexity": 9,
      "vbr": true
    }
  }
}

This optional section can be added to an existing configuration. Omitting audio, opus, or any individual property uses the defaults shown above. Values are validated before startup. Unknown fields are rejected, including inside audio and opus.

bitrate is in bits per second, not kilobits: 128000 means 128 kbit/s for the stereo stream. Higher bitrates generally preserve more detail at the cost of bandwidth. complexity trades CPU for encoding quality: 0 uses the least computation, 10 the most; 9 is the music-oriented default. With vbr: true, libopus varies packet sizes according to the audio while targeting the configured bitrate. false requests constant bitrate. Encoding stays at 48 kHz stereo with 20 ms frames and libopus's Application::Audio mode.

All server players receive these settings, with an independent, lazily created encoder for each output. Settings apply only when encoding PCM. Compatible Opus passthrough forwards the original packets without constructing an encoder, so these settings cannot change their bitrate or quality. There are no client or guild overrides, and the RPC protocol is unchanged. Embedded Node.js uses the same core defaults without loading this JSON file. It has no JavaScript encoder-setting API. See Opus encoding output for the Rust configuration API.

Media access restrictions

File paths are canonicalized before use and must be regular files beneath one of the configured roots. Use narrowly scoped roots containing only media the server should serve.

Remote inputs accept only HTTP(S), according to allowHttp for HTTP; HTTPS is always permitted by scheme. allowedUrlHosts is an explicit host allowlist. An empty list denies all hosts. A listed parent domain also matches its subdomains. "*" opts in to any public hostname. Private, loopback, link-local, multicast, reserved, and documentation IP ranges are rejected by default after DNS resolution.

FFmpeg follows HTTP redirects, and the core HTTP input owns the redirected request. The server cannot revalidate each redirect target for direct URL sources. For untrusted URL inputs, use a strict host allowlist and enforce outbound egress rules that block internal networks and cloud metadata endpoints. Do not disable blockPrivateNetworks or configure * unless the media URLs are trusted and network egress is independently restricted.

Raw FFmpeg filtergraphs are trusted application configuration because filters can access external resources. Do not expose unrestricted filter changes to untrusted clients.

Observability

The server initializes the existing remedia-observability context. It does not add a second logging configuration. REMEDIA_LOG takes precedence over REMEDIA_DEBUG=1; REMEDIA_LOG_FORMAT accepts text or json; REMEDIA_OTEL_ENABLED=true enables optional OTLP traces and metrics. The standard OTEL_* endpoint, protocol, resource, and sampling variables are honored by the shared crate. observability.console: false disables server console output while preserving configured diagnostic receivers and OTLP exporters. See Observability.

The process never logs passwords, authorization headers, Discord voice tokens, session keys, MLS material, or full media URLs. Treat RPC credentials, resume credentials, and voice credentials as secrets even when they are not written to logs.

Detailed references

On this page