unswarm

Your own swarm of models
just one at a time.

MIT · self-hosted · OpenAI-compatible API · .NET + Go + React

How it works

  1. 01

    Your app asks for a model.

    One OpenAI-compatible endpoint. Put whichever model you like in the model field — the app has expectations; we do not shame them.

  2. 02

    The scheduler decides.

    Model already loaded? Tokens flow. Not loaded? It stops the current model container, boots yours, and feeds the weights into your very small GPU.

  3. 03

    Your app thinks nothing happened.

    Requests queue, responses proxy through, and from the outside all ten of your models appear to exist simultaneously. They do. Sort of.

One honest caveat: swapping models takes seconds to minutes, depending on model size and how fast your disk is. Requests wait in the queue meanwhile. If you want everything answering at once, that's what more VRAM is for — or a generous sponsor.

Capabilities

Router profiles

A router profile acts like a virtual model: it appears under /v1/models like everything else, and your harness asks for it like any other id. What answers behind that model is a policy. Manual — you activate a model from the CLI or the frontend, and it stays. Auto — a priority-based fail chain: when the active model errors, the next one in priority answers. The panel below runs both.

policy behind the profile
GET /v1/models → cloud/mimo2.5-free cloud/mimo2.5 hosted/Qwen3.6-35B router/coding
harness pi · OpenCode idle
profile · virtual model router/coding 3 in chain

manual — you picked it

The harness asks for router/coding. It answers with the model you clicked, and keeps answering with it until you click a different one.

  • The id never moves — your harness calls the profile; the model behind it is what changes.
  • Your click is the policy — nothing swaps on its own while you are mid-conversation.
  • Swaps still cost — clicking a cloud entry answers right away; clicking a cold local model queues requests while its weights load.

auto — something failed

Same profile id, different policy: the request walks your priority list until something answers. The useful chains are cloud first, your box last — a 429 from a free tier at 3 a.m., a 503 from an upstream that no longer exists, and then your own hosted/ model catching the conversation like it always does.

  • Ordered chain — you rank the models; the router walks your ranking, not a guess.
  • Consumed errors — a 429 stays inside the profile; your client gets one response, not a stack trace of attempts.
  • Hosted last — free tier, paid tier, your box. A chain of local models mostly swaps weights; a chain of providers mostly saves requests.
  • Cooldown — a model that just fell over sits out the next few requests.

One control plane.
As many boxes as you own.

Agents connect out to the control plane over WebSocket — the control plane never dials into your machines.

The CLI

Two commands turn your control plane into coding-agent config: it rotates the inference key, reads the models your key is actually allowed to reach, and writes the provider block for pi and OpenCode — base URL, secret, context windows and all.

unswarm — zsh

Quick start

git clone https://github.com/atretador/unswarm.git
cd unswarm
docker compose up -d --build
docker exec -i unswarm-backend-1 dotnet exec -- unswarm-admin set-password --username admin --password YourPassword

Backend, frontend and database in one box to start; add agents when you buy more computers. Development setup (.NET 10 · Go · React 19) is in the README.