# Where viiwork came from
URL: https://viiwork.pulu.app/blog/where-viiwork-came-from/
Updated: 2026-07-16

viiwork did not start as a project. It started as a problem I kept having at my desk.

## A pile of cheap VRAM

The hardware came first. gfx906 cards — Radeon VII, MI50, MI60 — had fallen off the back of the datacenter and onto the secondhand market, and for the price of one current GPU you could buy a stack of them with more VRAM between them than anything I could otherwise afford. They are not fast by today's standards, but a model either fits in VRAM or it does not, and these fit. I put them in whatever machines I had, pointed llama.cpp at a directory of GGUF files, and started serving models on the local network.

That worked for exactly one machine.

## viiwork 1, and the wall it built

The first version of viiwork was the thin layer I wrapped around that: a process that launched a model, gave it a port, and answered an [OpenAI-shaped API](/docs/api-integration/). One model, one process, one port. When I wanted a second model I started a second process on a second port. When I bought a second machine I did it all again over there.

The trouble was never one machine. It was the seams between them.

- Every model was its own instance file, with its own port, and the ports followed per-family schemes I had to keep straight in my head.
- A model's id came from its GGUF filename, so renaming a file quietly renamed the model.
- And every machine carried a hand-written `peers.hosts` list naming every other machine. Add a node and you edited that list *everywhere* — on the new box and on all the old ones — and if one copy drifted, requests fell into a hole.

By the time the fleet was three machines — `gb1`, `forge-white`, `yeti` — bringing a fourth online was an afternoon of editing config on four hosts to tell them all about each other. I was maintaining a map of the network by hand, on every machine, in triplicate. The inference worked. The bookkeeping was the whole job.

## The idea that became version 2

The rewrite came from one stubborn thought: the machines are already on a network that knows which of them are up. Why am I telling each one what the others are?

Everything the machines were already on — the tailnet, the LAN — could answer "who else is here" without my help. So viiwork 2 threw out the peer lists and let the [mesh](/docs/mesh/) form itself:

- **One node per machine.** A single process supervises every model on the box, instead of one process per model. Its `models:` list replaces the pile of instance files.
- **Two fixed ports, the same everywhere.** The API and dashboards on 8086, membership gossip on 7946. No more port schemes to remember.
- **A mesh with no roster.** A node advertises itself and [finds the others](/docs/autodiscovery/) through tailscaled, or through mDNS on a LAN. Adding a machine means writing that one machine's config and nothing anywhere else. A machine that stops is dropped from routing within seconds.
- **Explicit model names.** A node serves exactly the name you give a model, and the same name on every machine means a request for it can run on any of them.
- **Routing that knows about slots.** A request goes to a local backend with a free slot, or to the member with the most free, or waits briefly in a queue — instead of landing on whichever host a peer list happened to name first.

The config that used to live in four places collapsed into one file per machine that only describes that machine. The map of the network stopped being something I maintained and became something the mesh worked out for itself.

That change is the whole reason there is a v1-to-v2 migration guide: v2 refuses a v1 `viiwork.yaml` at startup, because almost every assumption underneath it had moved. If you are coming across from that era, [migrating to v2](/docs/migrating-to-v2/) is the map.

## Where it is now

Everything since has been built on that spine. Aliases let a stable name like `stable-coder` point at a real model and be switched mesh-wide from any machine. [kv_share](/docs/models/#sharing-sessions-between-hosts-kv_share) carries a session's warm state to the next host so a busy machine no longer means a cold read. [Releases are reproducible and signed](/docs/releases/), so a node will only update itself to a build the publisher actually stood behind. The reference fleet is still those gfx906 cards, now joined by NVIDIA boxes and a couple of [Apple Silicon Macs that run the engine natively](/docs/macos/).

But the shape of it has not changed since the rewrite. Switch a machine on, and within seconds its models are usable from every other node, with nothing edited anywhere else. That is the thing I wanted at my desk, back when I was keeping the network map in my head and losing. If you want to see it from the other end, [bringing a new machine into a mesh](/blog/bringing-a-node-online/) is a single machine's first `viiwork init`.
