MUXI.mdandPENDING-MUXI.mdnow survive formation updates. The runtime's self-improvement pass (runtime #283-#285) accumulates learnings inMUXI.mdat the formation root, withPENDING-MUXI.mdholding suggestions awaiting review. Both are runtime-owned state, not operator config, so a version deploy must carry them forward the same waymemory.dbis preserved.updateFromDirectory(the single choke point for blue-green updates and draft deploys of existing formations; POST deploy rejects existing IDs) now copies both files fromcurrent/intostaging/after the bundle move and before validation/spawn, so the staging formation boots with its learnings intact. Unlikememory.db(copied only when absent from the upload), the live tuning files always overwrite bundle copies: the server-side state is newer than anything an operator bundled. Copy failures log a warning and never block the deploy; the files are tuning state, not required for operation.preserveTuningFileshelper inpkg/api/update.gomatches on-disk names exactly viaReadDir(the runtime accepts bothMUXI.mdand lowercasemuxi.md); a plain per-candidateos.Statwould double-copy on case-insensitive filesystems such as macOS APFS, wherestat("muxi.md")also matchesMUXI.md.- Tests in
pkg/api/update_test.go(TestPreserveTuningFiles): both files preserved with content intact, live copy overwriting a bundle copy, lowercase variant, no-op when absent, and per-file failure reporting. - Rollback carries tuning state forward too:
HandleRollbackcopiesMUXI.md/PENDING-MUXI.mdfromcurrent/intoprevious/right beside the existingmemory.dbpreservation, before the three-way directory swap. Learnings accumulate independently of code versions, so rolling back a bad deploy must not revive week-old tuning state.
- Bundle extraction now lives under
<DataDir>/tmpinstead of the system$TMPDIR(usually/tmp). On modern Linux distros/tmpis a tmpfs mount on a different filesystem than/var/lib/muxi, and the previous extract path forced anos.Renamefrom one to the other — which fails withEXDEV("invalid cross-device link"). Bothdeploy.go(new formations) andupdate.go(existing-formation blue-green updates) were affected. Operators saw the failure asFailed to move source to stagingwith no underlying cause, because the deploy handler logged the real error to journald but only the masked message reached the client.EnsureDirectoriesalready created<DataDir>/tmpat every server start, so this path is same-FS by construction; the change is a one-lineMkdirTempargument at each call site. safeRenamehelper inpkg/api/util.gowrapsos.Renamewith acopyTreePreservingMode+RemoveAllfallback when the kernel returnsEXDEV. Defense in depth for operators with non-standard layouts (e.g.,MUXI_DATA_DIRbind-mounted onto a different mount than the rest of/var). Non-EXDEVrename errors propagate verbatim so the real cause still reaches the log instead of being masked behind a copy failure.copyTreePreservingModepreserves per-file modes — distinct from the simplercopyDirindraft.gowhich defaults files to0644and would silently widen a formation's0600 secrets.encto world-readable on the fallback path.os.MkdirAll(formationBaseDir, 0755)added at the top of update.go's directory-setup block. Closes the registry-without-dir window we hit during the 0.20260514.0 deploy:muxi server delete cicdwiped/var/lib/muxi/formations/cicdon disk while the registry auto-save raced and left the in-memory entry pointing at a now-missing path, so the subsequent deploy was routed through update.go (which previously assumed the dir existed) andos.Rename(/tmp/extract-..., /var/lib/muxi/formations/cicd/staging)failed withENOENT, masked as the same generic "Failed to move source to staging" message. A trivialMkdirAllcloses the hole without changing the happy path.- Tests in
pkg/api/util_test.go: same-FS rename, EXDEV-stubbed fallback (verifies tree copy +0600and0700mode preservation + source cleanup), non-EXDEV error propagation, andMUXI_DATA_DIRenv-override flow-through. The EXDEV branch is exercised by stubbing the package-levelrenameFnvariable rather than requiring two physical filesystems in the test environment.
docker run --platformpin in runtime-runner spawn: the docker-wrapped Singularity path now passes--platform linux/<arch>derived from the SIF filename on everydocker run. runtime-runner became a multi-arch image (amd64 + arm64); on Apple Silicon, Docker started resolving:latestto the host-native arm64 manifest, and Apptainer inside the arm64 container correctly refused to launch the amd64 SIF —muxi upfailed withFATAL: ... the image's architecture (amd64) could not run on the host's (arm64). The pin locks runner-arch to SIF-arch regardless of what Docker has cached locally, so adocker pullordocker system prunebetween server runs can no longer silently break the spawn path.sifPlatform(path)helper inprocess/spawn_common.goparses the arch suffix frommuxi-runtime-<version>[-<variant>]-linux-<arch>.sif(lean, pytorch, cuda variants all covered). Unparseable paths default tolinux/amd64to preserve the previous behavior for test fixtures and any out-of-convention SIF names.- Stale comment on
runtime/resolver.go::getPlatformupdated: it claimed runtime-runner was amd64-only and citedcmd/server/commands.go::pullRuntimeRunneras the invariant. That stopped being true when the runner went multi-arch. The comment now points atprocess/spawn_common.goas the source of truth for arch pinning, and flags the next step (resolver should pick native linux-arm64 SIFs once those ship so Apple Silicon can skip the Rosetta hop).
- Second embedding model preloaded during
muxi-server init:Xenova/multilingual-e5-smalljoinsnomic-ai/nomic-embed-text-v1.5in the cache. Formations that need non-English retrieval now skip the multi-hundred-MB first-deploy stall. - Quantized ONNX only (~125 MB): the multilingual file list is the transformers.js layout —
config.json,tokenizer.json,tokenizer_config.json,special_tokens_map.json,onnx/model_quantized.onnx. Nopytorch_model.bin, no sentence-transformers metadata. - Same best-effort contract as the Nomic download: failure prints a warning and lets the runtime fetch on first deploy. Both models share
<cacheDir>and the bind-mount into formation containers, so any runtime variant picks them up automatically. - Init UX: a second
* Setting up multilingual classification model... / ✓ Multilingual classification model readysection mirrors the existing embeddings section, including the spinner-driven progress line.
- Server self-update no longer 404s: download URL aligned with the v-prefixed S3 layout (
https://pkg.muxi.org/server/v{VERSION}/{binary}). Both the release workflow's S3 upload path and the in-binary download URL now agree on thevprefix, matching the git tag and GitHub release naming.
- Single source of truth for the runtime-runner image: new
config.DefaultRuntimeRunnerImageconstant. All 7 API handlers,buildDockerSingularityCommand,validator.go, andcmdInitnow resolve from the config field or fall back to the constant — previouslyspawn_common.gohardcoded the image name, silently ignoring an operator'sruntime.runtime_runner_imageoverride inconfig.yaml. SpawnConfig.RuntimeRunnerImagefield threaded through all spawn paths including rollback'sfinalSpawnConfig.ValidateRuntimeAvailable(runnerImage)andGetRuntimeInfo(wrapperImage)accept the configured image so startup validation and runtime-info reporting match what the spawn path actually uses.
- Default SIF mirror changed from
github.com/muxi-ai/runtime/releases/downloadtohttps://pkg.muxi.org/runtime. URL scheme simplified to{baseURL}/{version}/{filename}— no more GitHub-specific redirect parsing. fetchLatestVersionnow reads a plain-textlatest.txtfrom the mirror instead of parsing GitHub redirect headers. Simpler, no rate-limit concerns.- Server self-update URL changed from
releases.muxi.orgtopkg.muxi.org/server.
- Server binaries uploaded to S3 on release:
s3://BUCKET/server/VERSION/muxi-server-{os}-{arch}with--acl public-read. Mirrors the runtime's existing S3 layout. - Runtime
latest.txtuploaded tos3://BUCKET/runtime/latest.txtafter each release sofetchLatestVersioncan resolve "latest" from the S3 mirror.
TestHandleRollbackandTestHandleBundleDeploy_ValidBundleno longer hang: root cause wasformation.Load()defaultingMuxiRuntimeto"latest", triggering a real SIF download from GitHub with no HTTP client timeout. Fixed by (1) pointing testSIFBaseURLat a non-routable address, (2) addingDialContext+ResponseHeaderTimeoutto the downloader's HTTP transport, and (3) unifying health-check timeouts viaresolveHealthTimeout(cfg)across all 6 spawn-and-wait handlers.
- SIF arch pinned to
linux-amd64on macOS/Windows: the runtime-runner Docker image is x86_64-only (Singularity ships no arm64 build), so Apple Silicon was downloading an arm64 SIF that the amd64 container couldn't load.getPlatform()now keys offGOOSinstead ofGOARCHon non-Linux hosts.
muxi-server initpre-downloads the default embedding model (nomic-ai/nomic-embed-text-v1.5, ~524 MiB) into the cache dir so the first formation deploy doesn't stall on a multi-hundred-MB fetch. Pure HTTP — works identically on Linux, macOS, and Windows.- Fast-path on re-init / upgrade: if every model file is already present and non-zero size, init skips the HTTP download entirely and converges on the same
✓ Embeddings readyconfirmation. Safe to runinitorupgraderepeatedly. - Atomic writes: each file is written to
<file>.tmpand atomically renamed. A killed process can't poison the cache with partial files — a subsequent init re-fetches cleanly. - Cache directory: new
MUXI_CACHE_DIRenv var overrides the default<data_dir>/cachelocation. Self-healed on startup so the bind-mount into formation containers always has a writable target.
muxi_runtime.variant: formations can now opt into GPU or CUDA runtime SIFs. Variant names enter the SIF filename as a suffix —muxi-runtime-{version}-{variant}-linux-{arch}.sif— so CPU, GPU, and CUDA builds coexist on the same host.- Variant validation across 7 API handlers (deploy, update, restore, dev, start, restart, rollback) rejects unknown variants with a clear error instead of downloading a nonexistent SIF.
- HF cache bind-mount (
<cacheDir>→/opt/hf-cache) wired on both native (Apptainer) and Docker-wrapper paths withHF_HOME=/opt/hf-cache. Any runtime variant can now reuse the pre-downloaded embedding model without re-fetching.
- Single-line progress for Docker pulls: runtime-runner and Skills RCE pulls now collapse Docker's 50+ lines of per-layer output into one animated line —
⠙ Layers 5/8 (62%). The braille spinner ticks every 100 ms so the line keeps animating during silent layer downloads. - Spinner for embedding download: the HTTP download paints
⠙ 524 MiB downloadedwith the same spinner style, so all three setup sections feel consistent. - Dropped
--quieton both Docker pulls. The old silent mode made init look frozen for minutes on multi-hundred-MB transfers; explicit progress is better. DOCKER_CLI_HINTS=falsesuppresses Docker Desktop's "What's next: docker scout quickview…" promotional footer that cluttered every pull.- Terser messaging: all three setup sections use the same
* Setting up X... / ✓ X readypattern. Embedding model name and cache path are no longer printed — they're noise in an init transcript users read once.
Final init transcript on macOS (fresh machine):
* Setting up runtime-runner...
⠹ Layers 5/8 (62%)
✓ Runtime-runner ready
* Setting up Skills RCE...
⠹ Layers 3/4 (75%)
✓ Skills RCE ready
* Setting up embeddings...
⠙ 524 MiB downloaded
✓ Embeddings ready
- Create tmp directory on startup:
EnsureDirectoriesnow creates{dataDir}/tmpsoTMPDIR=/var/lib/muxi/tmpworks out of the box when deploying formations via Docker. - Default health check endpoint: changed from
/healthto/v1/healthto match the MUXI runtime API. Fixes formations failing health checks on first deploy.
- Auto-install Apptainer on server start: if
apptainer/singularityis not found on Linux,muxi-server startnow automatically installs Apptainer before proceeding. Solves the issue where Apptainer was lost on Docker container restarts. - Apptainer/Singularity lookup fix: runtime validation and binary lookup now prefer
apptaineroversingularity, matching what the installer actually installs. Previously the server only looked forsingularity, which doesn't exist after a standard Apptainer install.
- Ubuntu-based Docker runtime image: switched the runtime image from Alpine to Ubuntu so
muxi-server initworks in-container on Linux without failing distro detection for Apptainer installation. - Container runtime deps update: replaced
apk-based runtime dependencies withaptpackages (ca-certificates,docker.io,wget) to match the Ubuntu base image.
- Docker networking for host services: added
--add-host localhost:host-gatewayand--add-host host.docker.internal:host-gatewayto runtime-runner Docker commands so formations can reach host-local services (e.g. PostgreSQL) vialocalhostwithout changing connection strings - Release downloads via CDN: switched
github.com/muxi-ai/*/releases/download/*URLs toreleases.muxi.org/*/releases/download/*for server/runtime download paths.
- Built-in code execution: formations now ship with a managed RCE (Remote Code Execution) service for Skills
muxi-server init: downloads RCE automatically (SIF on Linux, Docker image on macOS/Windows)muxi-server start: launches RCE as a managed process, injectsMUXI_RCE_URLandMUXI_RCE_TOKENinto all formations- Auto port discovery: if default port 7891 is occupied, scans upward for an available port
muxi-server upgrade: self-update the server binary, pull latest RCE, and migrate config- Downloads latest server binary from GitHub releases (atomic swap with rollback)
- Adds missing config fields (e.g. RCE auth token) to existing configurations
- HuggingFace model cache: pass
HF_HOME=/opt/hf-cacheto SIF containers so the pre-cached embedding model is used instead of re-downloading on every startup (~80s), which caused health check timeouts - npm/npx in SIF containers: npm and npx are symlinks that use relative
require('../lib/cli.js'); bind-mounting the resolved path broke the import. Now creates wrapper scripts that invoke node with the full path to the npm module - Exact runtime version pinning: versions like
muxi_runtime: "0.20260220.0"were rejected by the resolver if not in the local registry. Now passes exact versions through to the downloader, which checks disk and downloads if needed - Restore path: use downloader in the restore path to resolve
latestruntime from GitHub instead of building a literalmuxi-runtime-latest-*.siffilename - Runtime resolution: always resolve
latestruntime from GitHub instead of using stale locally-cached version - Host tools: add
npm,npx,bun,uv,uvx,tar, andgzipto tools bind-mounted into SIF containers
/mcp/{formation_id}/*- New proxy route for MCP protocol requests- Maps
/mcp/{id}to the formation's/mcpendpoint (preserves/mcpprefix) - Supports SSE transport for MCP client connections (Claude Desktop, Cursor, etc.)
- Bind-mount host tools (Node.js, git, curl, ffmpeg, etc.) into SIF at
/opt/muxi-tools - Runtime-runner (macOS/Windows): pre-staged tools directory with shared libs via
ldd - Native Linux: direct bind-mount of host binaries and library directories
- Sets
PATH,NODE_PATH,FONTCONFIG_PATH,SSL_CERT_FILEinside SIF
- Dev logs: truncate log files on each
muxi uprun (prevents stale error accumulation) - Graceful shutdown: fix panic on Ctrl+C when monitor channel already closed (
sync.Once)
New API for running formations in development mode without full deploy cycle:
- POST /rpc/dev/run - Start draft formation from local path or draft directory
- POST /rpc/dev/stop - Stop draft formation
- /draft/{id}/* - Proxy route for draft formations (separate from live
/api/{id}/*)
Key features:
- Live and draft formations can run simultaneously with same ID
- Draft formations use separate registry (not persisted, not restored on restart)
- Shares port pool with live formations
- Enables
muxi upCLI command for fast local iteration
Server now notifies SDKs when updates are available:
- Fetches latest release versions from GitHub API on startup (refreshes every 24h)
- Parses
X-Muxi-SDK: {name}/{version}header from SDK requests - Responds with
X-Muxi-SDK-Latest: {version}header - If no release data available, echoes back SDK's version (no false notifications)
Supported SDKs: go, python, typescript, ruby, php, csharp, swift, kotlin, dart, java, rust, cpp
- GET /rpc/formations/{id}/download - Download formation as zip file
- Excludes hidden files (except
.env) andmemory.dbby default - Use
?db=truequery param to include persistent memory database
New API for Console's visual formation editor (Studio):
- POST /rpc/formations/{id}/draft/files - Single endpoint with action-based routing
- init - Create new draft or clone from live version
- list - List files in draft directory
- read - Read file content (utf-8 or base64)
- write - Write file content (utf-8 or base64)
- delete - Delete file or directory
- deploy - Deploy draft to live (new or blue-green update)
- discard - Remove draft without affecting live
Reuses existing deployment logic - same validation, health checks, and blue-green deployment.
Runtime now creates memory.db for persistent agent memory. Server handles this automatically:
- Update - Preserves
memory.dbfrom current version if not in upload - Rollback - Copies
memory.dbfrom current to previous before swap (roll back code, not data) - Download - Excludes
memory.dbby default for lightweight downloads
- Updated GitHub Actions to latest versions (checkout v6, setup-go v6, upload-artifact v6)
- Updated CI workflows to Go 1.24 to match go.mod
The orchestration platform for MUXI formations. Deploy, route, monitor, and auto-restart AI agent formations with a single Go binary.
- Single binary orchestration - Deploy and manage formations without external dependencies
- 14 RESTful API endpoints - Full CRUD for formation lifecycle management
- HMAC authentication - AWS Signature v4 style request signing with replay protection
- HTTP reverse proxy - Intelligent routing to formations via
/api/{id}/* - Port pool allocation - Thread-safe port management (8000-9000 range)
- Audit logging - JSON-formatted logs for all management operations
- Health monitoring - Continuous health checks with automatic recovery
- One-command deploy - Upload gzip tarball bundles to deploy formations
- Blue-green deployments - Zero-downtime updates with staging port validation
- Formation versioning - Current/previous directory structure with SHA256 tracking
- Rollback support - Instant rollback to previous version with blue-green safety
- Auto-restart - Crashed formations restart automatically with configurable limits
- Secrets validation - Checks for
secrets.encand.keywhen secrets referenced
- Singularity/Apptainer SIF - Container-based formation execution (Linux native)
- Docker-wrapped Singularity - Runtime support for macOS and Windows
- Auto-download - SIF images and runtime-runner containers downloaded on demand
- Runtime version resolution - Automatic version matching from formation.yaml
- Deploy progress - Real-time deployment stages via Server-Sent Events
- Update progress - Blue-green update stages with staging port info
- Rollback progress - Live rollback status with version tracking
- Restart progress - Restart status streaming
- Log streaming -
follow=trueparameter for live log tailing
- CLI profile auto-configuration - Creates
~/.muxi/cli/profiles.yamlon init - System service setup - Interactive systemd (Linux) and launchd (macOS) installation
- Gradient banner - Branded welcome message with version and architecture info
- Privacy-first metrics - No PII, no content, no formation data
- Hourly reporting - Batched metrics with 5-second retry backoff
- Opt-out support - Disable via
MUXI_TELEMETRY=0or~/.muxi/config.yaml - Tracked metrics - Deployments, updates, rollbacks, crashes, restarts, request latency
- Machine ID - Platform-specific deterministic ID generation
- Country lookup - Cached geo lookup via ipapi.co
- X-Muxi-Server - Server version injected into proxied requests
- X-Muxi-SDK pass-through - Client SDK headers forwarded to formations
- Server-owned protection - Clients cannot spoof server-injected headers
- X-Forwarded-* - Standard proxy headers (For, Proto, Host)
- Linux - Native support with systemd service (amd64, arm64)
- macOS - Native support with launchd service (Intel, Apple Silicon)
- Windows - Development support with Job Objects process management
- Docker - Multi-arch images (linux/amd64, linux/arm64)
- Homebrew -
brew install muxi-ai/tap/muxi-server - Install script -
curl -sSL https://muxi.org/install | bash - PowerShell -
irm https://muxi.org/install/windows.ps1 | iex - Direct download - GitHub release binaries for all platforms
- Docker -
ghcr.io/muxi-ai/muxi-server:latest
- YAML-based -
~/.muxi/server/config.yaml - Port 7890 - Official MUXI Server port
- Formation isolation - Formations bind to
127.0.0.1only - Configurable log level -
--log-levelflag andMUXI_LOG_LEVELenv var - Auto-restart settings - Max restarts, restart delay, health check intervals
MUXI Server uses ScalVer (Scalable Calendar Versioning):
Format: MAJOR.YYYYMMDD.PATCH
- Documentation: muxi.org/docs
- GitHub Issues: github.com/muxi-ai/server/issues