Mux
Lightweight, dependency-free, in-memory fake of the Mux Video API for testing code that uses the real @mux/mux-node SDK.
Default port: 4839
Quick start
import { MuxServer } from "./services/mux/src/server.js";
const server = new MuxServer(4839);
await server.start();
// ... run your app/tests ...
await server.stop();
Point the real SDK at it (override baseUrl to http://127.0.0.1:4839) or drive the REST API directly:
const auth = "Basic " + Buffer.from("tokenId:tokenSecret").toString("base64");
await fetch("http://127.0.0.1:4839/video/v1/assets", {
method: "POST",
headers: { Authorization: auth, "Content-Type": "application/json" },
body: JSON.stringify({ input: [{ url: "https://example.com/video.mp4" }], playback_policy: ["public"] }),
});
Access via MCP / preview URL
When run inside a parlel pool, reachable at its mapped preview URL (e.g.
http://127.0.0.1:4839). MCP clients drive the Video surface (assets, uploads,
playback-ids) using MUX_TOKEN_ID / MUX_TOKEN_SECRET for Basic auth.
Implemented operations
State is in-memory and ephemeral. All routes require Basic auth (token id:secret). Created assets are immediately status: "ready".
Assets
POST /video/v1/assets— create an asset (201 { data: { id, status:"ready", playback_ids, ... } }).GET /video/v1/assets— list assets.GET /video/v1/assets/:id— fetch an asset.DELETE /video/v1/assets/:id— delete an asset (204).GET /video/v1/assets/:id/playback-ids— list playback ids.POST /video/v1/assets/:id/playback-ids— add a playback id ({ policy }).
Direct uploads
POST /video/v1/uploads— create a direct-upload URL (201 { data: { id, url, status:"waiting", ... } }).GET /video/v1/uploads/GET /video/v1/uploads/:id— list / fetch uploads.PUT /video/v1/uploads/:id/cancel— cancel an upload.
Service & inspection (parlel extensions)
GET /— service metadata.GET /health—{ status: "ok" }.POST /__parlel/reset— reset all in-memory state.OPTIONS *— CORS preflight (204).
Surface coverage
This emulator faithfully replicates the API surface most application code and agents exercise. Anything below the supported lines is either an intentional design choice for a fast, zero-cost local emulator (✓ By design) or a candidate for a future release (⟳ Roadmap) — never a silent inaccuracy.
Legend: ✅ fully supported · ◐ accepted (stored, not strictly enforced) · ✓ by design · ⟳ on the roadmap.
| Feature | Status |
|---|---|
Asset create/list/get/delete + data envelope | ✅ Supported |
| Playback id list/add | ✅ Supported |
| Direct uploads create/get/cancel | ✅ Supported |
| Basic auth (token id:secret) | ◐ Presence checked, not verified |
| Real ingest / encoding / live streaming | ⟳ Roadmap — Not performed (assets are instantly "ready") |
Mux Data (/data/v1/...) | ⟳ Roadmap |
| Signed playback token verification | ⟳ Roadmap |
| Real persistence | ✓ By design — In-memory by design — fast, isolated, resets cleanly between tests |
Error shapes
Mux uses { error: { type, messages } }:
| Status | When |
|---|---|
401 | missing/invalid Basic auth |
404 | unknown asset/upload |
Manifest
See services/mux/manifest.json — name mux, port 4839, protocol http,
healthcheck /health, env MUX_TOKEN_ID, MUX_TOKEN_SECRET, MUX_BASE_URL.
Configuration — test.env
Copy these into your test.env (used by the bridge sidecar flow). Tokens are Parlel's seeded test credentials — any non-empty value is accepted by the emulator, so you rarely need to change them. Swap in real credentials only when pointing at the live service in prod.env.
MUX_TOKEN_ID=parlel
MUX_TOKEN_SECRET=parlel
MUX_BASE_URL=http://parlel-bridge:4839
<!-- parlel:testenv:end -->