ADR-006: Streaming mode — URL-only (S3 demoted to Won't)
ADR-006: Streaming mode — URL-only (S3 demoted to Won’t)
Status
Accepted
Context
FR-PLUGIN-5 defines two streaming modes: URL (plugin returns a direct audio URL) and pipe (plugin writes audio bytes to the IPC channel). The pipe mode would require multiplexing binary audio data alongside JSON-RPC control messages on the same IPC channel (stdin/stdout, per ADR-001).
The pipe mode framing problem: JSON-RPC messages are line-delimited JSON. Binary audio data is not line-delimited. Multiplexing both on stdout requires:
- A length-prefixed binary framing protocol (e.g.,
Content-Length: N\r\n\r\n<body>) that every plugin author must implement correctly - Handling partial reads and buffer alignment at the receiver
- Platform-specific pipe buffer behavior (Linux pipe buffer is 64KB, Windows named pipe is different)
- Distinguishing a JSON-RPC response from an audio chunk at the consumer side
S3 (“Plugin streaming mode — pipe”) is currently a Should in the backlog. The question is whether the complexity justifies the Should priority.
Decision
Demote pipe mode from Should to Won’t for v1. All streaming in v1 uses URL mode only — it is the implicit default and the only supported mode. No streaming_mode field is needed in the plugin manifest; the field is reserved for v2 if pipe mode is ever implemented. The plugin returns a {"url": "...", "content_type": "..."} response; the core fetches the URL.
The pipe mode framing spec is deferred until a concrete plugin author requests it. When that happens, it becomes a v2 design item with a concrete use case to validate the framing protocol against.
Alternatives considered
- Keep pipe mode as Should (original S3): Would require the framing protocol to be designed, documented, implemented in core, and implemented in at least the reference plugin — all for a feature with no confirmed plugin author asking for it. The risk of getting the framing wrong is high, and the cost of changing it later is a breaking protocol change (violates B4).
- HTTP chunked transfer encoding over pipe: Using HTTP framing over stdin/stdout is technically possible but adds HTTP parsing complexity on both sides. The plugin would need an HTTP parser; the core would need an HTTP chunked decoder. Doesn’t reduce the framing problem, just relabels it.
- Separate control and data sockets: One socket for JSON-RPC, one for audio bytes. Adds socket lifecycle management for every streaming request. The plugin now manages two communication channels.
Consequences
Positive:
- Protocol stays line-delimited JSON. No binary framing. Every language can implement it correctly on the first try.
- The core fetches audio via HTTP/HTTPS (or
file://) usingreqwestor similar. Battle-tested, supports streaming, redirects, range requests. - Plugin authors who want to stream from a public API (Spotify, Jamendo, etc.) return the API’s stream URL directly — no intermediate server needed.
Negative:
- Plugins that produce audio from non-URL sources (e.g., TTS synthesis, radio re-encoding) must either host a local HTTP server or write to a tempfile and return a
file://URL. - If pipe mode is added in v2, all v1 plugins that would benefit must be updated. Mitigation: the protocol version bumps when pipe mode is introduced; plugins declare
streaming_modein their manifest, so old plugins continue working. - Streaming a local file through the reference plugin requires the plugin to return a
file://URL. The core must be configured to allowfile://source URLs (it does — FR-CORE-1 covers both URL and file path sources).
References
- FR-PLUGIN-5 (functional.md)
- S3 (backlog.md) — Will be demoted to Won’t
- ADR-001 (stdin/stdout IPC)