feat(services): add Mux managed embedded service - #6034
Merged
Merged
Conversation
Adds Mux (coder/mux — local agent-orchestration daemon) as a fourth-tier embedded service built on the existing ServiceSupervisor framework, the same shape as 9Router and CLIProxyAPI: - Installer (src/lib/services/installers/mux.ts): npm install/update via runNpm (array args + env-based prefix, no shell interpolation), modeled on ninerouter.ts. Mux ships an npm package (`mux`) with a documented headless `mux server --host <host> --port <port>` mode, so no git-clone+build path was needed. - Registered in bootstrap.ts (SERVICES[] + buildSpawnArgsFactory). - DB seed migration 113 (version_manager row, not_installed/auto_start=0). - 7 API endpoints under /api/services/mux/ (install/start/stop/restart/ update/status/auto-start) plus the shared [name]/logs SSE endpoint, mirroring the cliproxy route shape and delegating errors through createErrorResponse(). - Dashboard tab (MuxServiceTab) reusing ServiceStatusCard, ServiceLifecycleButtons, AutoStartToggle, ServiceLogsPanel. - Docs: EMBEDDED-SERVICES.md (service table, architecture diagram, API reference, key-injection section), openapi.yaml, ENVIRONMENT.md, .env.example. Security: - Every /api/services/mux/* route is covered by the existing LOCAL_ONLY_API_PREFIXES "/api/services/" prefix (Hard Rule #17); added an explicit isLocalOnlyPath regression test for all 8 routes. - Mux binds to 127.0.0.1 explicitly (never 0.0.0.0) as defense-in-depth, since it orchestrates AI agents that can execute host commands. - The bearer token is generated the same way as 9Router's key (getOrCreateApiKey) and injected via MUX_SERVER_AUTH_TOKEN (mux's documented env form) rather than a CLI flag, so it never appears in `ps`/process listings. - No shell interpolation anywhere in the installer (Hard Rule #13): all npm/spawn args are static arrays; the install prefix and auth token travel via the env option. Co-authored-by: Ansh7473 <Ansh7473@users.noreply.github.com> Inspired-by: decolua/9router#1802
Contributor
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Contributor
Merged
tkgo11
pushed a commit
to tkgo11/OmniRoute
that referenced
this pull request
Sep 23, 2026
Adds Mux (coder/mux — local agent-orchestration daemon) as a fourth-tier embedded service built on the existing ServiceSupervisor framework, the same shape as 9Router and CLIProxyAPI: - Installer (src/lib/services/installers/mux.ts): npm install/update via runNpm (array args + env-based prefix, no shell interpolation), modeled on ninerouter.ts. Mux ships an npm package (`mux`) with a documented headless `mux server --host <host> --port <port>` mode, so no git-clone+build path was needed. - Registered in bootstrap.ts (SERVICES[] + buildSpawnArgsFactory). - DB seed migration 113 (version_manager row, not_installed/auto_start=0). - 7 API endpoints under /api/services/mux/ (install/start/stop/restart/ update/status/auto-start) plus the shared [name]/logs SSE endpoint, mirroring the cliproxy route shape and delegating errors through createErrorResponse(). - Dashboard tab (MuxServiceTab) reusing ServiceStatusCard, ServiceLifecycleButtons, AutoStartToggle, ServiceLogsPanel. - Docs: EMBEDDED-SERVICES.md (service table, architecture diagram, API reference, key-injection section), openapi.yaml, ENVIRONMENT.md, .env.example. Security: - Every /api/services/mux/* route is covered by the existing LOCAL_ONLY_API_PREFIXES "/api/services/" prefix (Hard Rule diegosouzapw#17); added an explicit isLocalOnlyPath regression test for all 8 routes. - Mux binds to 127.0.0.1 explicitly (never 0.0.0.0) as defense-in-depth, since it orchestrates AI agents that can execute host commands. - The bearer token is generated the same way as 9Router's key (getOrCreateApiKey) and injected via MUX_SERVER_AUTH_TOKEN (mux's documented env form) rather than a CLI flag, so it never appears in `ps`/process listings. - No shell interpolation anywhere in the installer (Hard Rule diegosouzapw#13): all npm/spawn args are static arrays; the install prefix and auth token travel via the env option. Inspired-by: decolua/9router#1802 Co-authored-by: Ansh7473 <Ansh7473@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds Mux (coder/mux — a local agent-orchestration daemon) as a fourth managed
embedded service, built entirely on the existing
ServiceSupervisorframework(the same shape as ninerouter and CLIProxyAPI) rather than a bespoke integration.
Thanks to @Ansh7473 for the original implementation.
Install method chosen: npm (not git-clone+build)
muxis published on npm (npm view mux→mux@0.27.0,bin: mux -> dist/cli/index.js)with a documented headless mode:
mux server --host <host> --port <port>. Since annpm-installable path exists, the installer uses the existing
runNpmhelper(
src/lib/services/installers/utils.ts) exactly like the ninerouter installer — nogit-clone+build capability was introduced.
Server details verified by inspecting the published tarball (
npm pack mux@0.27.0):GET /health→{ status: "ok" }(used as the supervisor health-check path)--auth-token <token>flag orMUX_SERVER_AUTH_TOKENenv varMUX_ROOTenv var (scoped underDATA_DIR/services/mux/datainstead of leaking into
~/.mux)What changed
src/lib/services/installers/mux.ts— npm install/update,resolveSpawnArgs()src/lib/services/bootstrap.ts— registered inSERVICES[]src/lib/db/migrations/113_mux_service_seed.sql—version_managerseed rowsrc/app/api/services/mux/{install,start,stop,restart,update,status,auto-start}—7 endpoints, all delegating errors through
createErrorResponse()src/app/api/services/[name]/logs/route.ts— wiredmuxinto the shared SSE logs dispatchersrc/app/(dashboard)/dashboard/providers/services/tabs/MuxServiceTab.tsx— dashboard tabreusing
ServiceStatusCard,ServiceLifecycleButtons,AutoStartToggle,ServiceLogsPaneldocs/frameworks/EMBEDDED-SERVICES.md,docs/openapi.yaml,docs/reference/ENVIRONMENT.md,.env.example— documentation updatesSecurity
/api/services/mux/*route is alreadycovered by the existing
LOCAL_ONLY_API_PREFIXES"/api/services/"prefix insrc/server/authz/routeGuard.ts. Added an explicit regression test assertingisLocalOnlyPath()returnstruefor all 8 mux routes(
tests/unit/authz/routeGuard.test.ts).127.0.0.1explicitly (never0.0.0.0) as defense-in-depth,since it orchestrates AI agents capable of executing host commands.
runNpm(array args,install prefix via the
envoption) — identical safety contract to the ninerouterinstaller.
resolveSpawnArgs()also uses arrayargs, never shell-interpolatedstrings.
getOrCreateApiKey)and injected via
MUX_SERVER_AUTH_TOKEN(mux's documented env form) — never as aCLI flag, so it never appears in
ps/process listings.createErrorResponse()/sanitizeErrorMessage().Test plan
node --import tsx/esm --test tests/unit/services/installers/mux.test.ts— 9/9 passnode --import tsx/esm --test tests/unit/authz/routeGuard.test.ts— 31/31 pass (incl. new mux loopback-gating assertions)node --import tsx/esm --test tests/unit/dashboard/providers/services/mux-tab.test.ts— 1/1 passnode --import tsx/esm --test tests/unit/check-route-guard-membership.test.ts— 15/15 passtests/unit/services/**,tests/unit/authz/**,tests/unit/dashboard/providers/services/**) — 511/511 passnpm run typecheck:core— cleannpm run check:cycles— no cyclesnpm run check:docs-all— pass (env/docs contract in sync)npm run check:openapi-coverage/check:openapi-routes/check:openapi-security-tiers— passnpm run check:any-budget:t11/check:tracked-artifacts— passnpx eslinton all changed/new files — 0 errors (1 pre-existing warning, unrelated line)