Commit Graph
585 Commits
Author SHA1 Message Date
Wanjohi eb20ab8309 feat(install): the desktop app downloads from a link that never changes
/download/windows and /download/linux redirect to a one-minute signed URL
for the current desktop app, from the same private bucket as the host
agent, so a download page can link to them once and every download still
passes through the API. Which version they hand out is a constant bumped
by a commit after a release is published, as the host installer's is.

/desktop.sh installs it on Linux for the calling user: one binary in
~/.local/bin and a launcher entry, no root.
2026-10-11 15:04:36 +03:00
Wanjohi 04ca876b81 chore(install): host agent 0.2.13 (#393)
Hosts installed or upgraded from here on report their slots, so runs
beyond them wait in line.
prod-04ca876
2026-10-11 11:07:39 +00:00
Wanjohi 3d381673f8 chore(install): host agent 0.2.13 2026-10-11 14:07:28 +03:00
Wanjohi 87d1098598 feat: runs wait in line for a free GPU; free days are Saturday to Monday (#392)
Two changes from dev:

- **Runs wait in line for a free slot on their host.** A host says on
its heartbeat how many runs it holds and is handed only that many; the
rest wait, paid before free-weekend, then first come first served.
Reading a waiting run returns its place in line. A waiting run nobody
has asked after for a minute leaves the line. A host that does not
report slots is handed every run, as before, so this is safe to deploy
ahead of the host agent.
- **Free days are Saturday, Sunday and Monday.**

Adds migration `0025_run_queue` (two nullable columns). API tests: 177
pass.
prod-87d1098
2026-10-11 10:56:47 +00:00
Wanjohi 01542cf14a feat(session): runs wait in line for a free slot on their host
A host holds only so many runs at once, and until now every waiting run
was handed to it regardless; the ones it could not hold failed there.

The host now says on its heartbeat how many runs it holds, and is handed
only as many waiting runs as it has free. The rest wait in line: paid
runs before free-weekend runs, then first come first served. Reading a
waiting run returns its place in line, and says when a paid run that
asked later has gone ahead, so a free player is never reordered quietly.

A waiting run nobody has asked after for a minute was abandoned, and
leaves the line rather than holding up everyone behind it. A host that
does not report its slots is handed every run, as before.
2026-10-11 13:53:00 +03:00
Wanjohi b52479975c feat(trial): the free days are Saturday, Sunday and Monday
Monday in place of Friday: the window is Saturday 00:00 to Tuesday 00:00,
Helsinki time, and the refusal outside it says Saturday to Monday.
2026-10-11 13:52:56 +03:00
Wanjohi cc8d1c1423 release: a listed box names its machine's host and whether it is the fleet (#391)
Cherry-pick of aa824a1 from dev, alone.

`GET /box` gains `host` (the machine's DNS label) and `fleet` on listed
boxes. The desktop app uses them to open the fleet machine's Steam
sign-in during onboarding. Additive; nothing existing changes shape.
Tests: 172/172 against Postgres.
prod-cc8d1c1
2026-10-11 09:28:00 +00:00
Wanjohi 2c150b761d feat(box): a listed box names its machine's host and whether it is the fleet
The desktop app gives a new cloud user a fleet box and then opens that
machine's Steam sign-in on its hostname; GET /box now says which hostname,
and which box is the fleet one.
2026-10-11 12:20:30 +03:00
Wanjohi 8152d17011 chore(install): host agent 0.2.12
Pins the installer to host agent 0.2.12. In this release:
- a run boots at the size it asked for;
- each run narrates its install progress through the edge;
- hosts re-pull the guest image and the jail, which drops virglrenderer
0001 and brings neshub's codec fix.
prod-8152d17
2026-10-10 17:51:28 +00:00
Wanjohi 4fc93ca4aa chore(install): host agent 0.2.12 2026-10-10 20:50:54 +03:00
Wanjohi 2e0f170082 fix(neshub): an encoder that missed the codec choice is told again (#389)
A client could connect and state its codecs before the game's encoder
was listening; the command was dropped, the encoder started on AV1, and
a client without AV1 got sound and no picture. Commands now wait for the
encoder's socket, and a frame in a codec no client decodes sends the
agreed set again.
prod-2e0f170
2026-10-10 12:36:33 +00:00
Wanjohi 9269ee5d51 fix(neshub): an encoder that missed the codec choice is told again
The codec the clients agree on goes to the encoder over a socket that does
not exist until the game draws, and a client can connect first. The command
was dropped, the encoder started on AV1, and a client without AV1 sat on a
splash with sound and no picture. Commands now wait for the socket, and a
frame in a codec no client decodes sends the agreed set again, which also
covers a launcher handing over to a game with an encoder of its own.
2026-10-10 15:34:23 +03:00
Wanjohi 4c88c579b4 feat(billing): sizes move up one; 1080p is the reference and the default (#388)
xs is a box with no stream, sm 720p, md 1080p, lg 1440p, xl 4K. Each
resolution keeps its price, so md is now 1x. New boxes default to md;
existing boxes move up one size.
prod-4c88c57
2026-10-10 12:19:57 +00:00
Wanjohi ac713e62ae feat(billing): sizes move up one; 1080p is the reference and the default
xs is a box with no stream, sm 720p, md 1080p, lg 1440p and xl 4K. Each
resolution keeps its price, so md is now 1x. New boxes default to md, and
existing boxes move up one size so none of them drops a resolution.
2026-10-10 15:18:23 +03:00
Wanjohi 47f8a54c76 fix(session): commit the migration that adds a run's size (#387)
The size column's migration was listed in the journal but never
committed, so migrating a fresh database failed and the last prod
release stopped before deploying.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
prod-47f8a54
2026-10-10 12:14:07 +00:00
Wanjohi 0f38898fd9 fix(session): commit the migration that adds a run's size 2026-10-10 15:13:54 +03:00
Wanjohi 7949d8b1c7 feat(session): a run picks its own size (#386)
POST /session takes an optional `tier`; the start job, the allowance
check and the burn rate use it, falling back to the box's tier. Adds a
nullable `session.tier` column.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-10-10 11:03:37 +00:00
Wanjohi 8608506682 feat(session): a run picks its own size
POST /session takes an optional tier. The host starts the run at that
size, the allowance is checked against it, and the run is billed at it.
A run asked for without one runs at its box's size, as before.

The size belongs to the run rather than the box: a box is a game's
install on a machine, and the size is chosen each time it is played, so
playing bigger never means a second box and a second install.
2026-10-10 14:03:13 +03:00
Wanjohi d74a1d8c63 feat(session): list your runs that have not stopped (#385)
GET /session returns the caller's runs that have not stopped, so a
client refused with a busy box can rejoin the same game or offer to stop
the other one.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
prod-d74a1d8
2026-10-10 08:19:33 +00:00
Wanjohi 05a3fee87a feat(session): list your runs that have not stopped
Asking for a run on a busy box is refused, but the refusal does not say
what holds the box. A client that just lost its connection needs to know
whether the run in the way is the game it asked for, which it can rejoin,
or another one it should offer to stop. GET /session answers that.
2026-10-10 11:19:20 +03:00
Wanjohi 348d852683 release: library downloads only from your boxes' hosts, named by GPU (#384)
GET /library picked each game's most recent download row from any host,
so one person's library could show another person's machine id and
progress. It now reads only hosts the caller's boxes are on.

Boxes and library downloads gain `gpu`, the host's first reported card
model, so clients name a place by hardware rather than a machine id.
Null until a host reports one.

Tests: core 240 pass (3 new), api 171 pass. The leak test was checked to
fail with the host filter removed.
prod-348d852
2026-10-10 07:12:10 +00:00
Wanjohi 152337296d fix(library): downloads only from your boxes' hosts, named by their GPU
GET /library picked each game's most recent download row from any host,
so one person's library could show another person's machine id and
progress. It now reads only hosts the caller's boxes are on.

Boxes and library downloads gain `gpu`, the host's first reported card
model, so a client names a place by hardware a person recognises rather
than by a machine id. Null until a host reports one.
2026-10-10 10:03:49 +03:00
Wanjohi 2009a4eeac release: install host agent 0.2.11 (#381)
Points the installer at host agent 0.2.11.
prod-2009a4e
2026-10-09 10:30:14 +00:00
Wanjohi 77595ecb65 chore(install): host agent 0.2.11 2026-10-09 13:29:58 +03:00
Wanjohi 8a378d5438 release: owner stop, viewer count, machine downloads (#380)
- `POST /session/:id/stop`: the owner ends a run. Adds
`session.time_stop_requested` (migration 0022) and a `user` reason on
`session.stop` jobs.
- The box reports how many clients are watching (`viewers` lifecycle
message), so a host can end a run nobody uses.
- `GET /machine/:id/downloads`: the owner reads what their machine is
downloading and has ready.
prod-8a378d5
2026-10-09 10:15:50 +00:00
Wanjohi 8054e62bd9 feat(machine): let the owner read what their machine has downloaded
GET /machine/:id/downloads lists every game the machine is downloading,
has ready or failed to download, with progress and each game's name.
The library already carries a download per game, but it is the most
recent one across every host, which is not what a person watching their
own machine set up wants to see.

Owner-scoped in the query: a machine that is not yours answers 404, the
same as one that does not exist.
2026-10-09 12:50:44 +03:00
Wanjohi 7670988ece feat(lifecycle): tell the host how many clients are watching
A host cannot tell a run somebody is playing from one nobody has looked
at in an hour, so it cannot end the second without guessing.

The hub now answers the address socket with a second line,
`viewers <n>`, counting clients that are streaming. Init reads it
alongside the address and sends a new `viewers` message to the host
when the count changes. A reader that takes only the first line gets
the address exactly as before, a server that never writes the second
line is still read, and a host that does not know the message skips it,
so either end can ship first.
2026-10-09 12:33:43 +03:00
Wanjohi f0bfe82d58 feat(session): let the owner end a run
POST /session/:id/stop. A run no host has claimed ends at once; one that
is starting or live is marked with time_stop_requested and its host is
handed a session.stop with reason `user` on every poll until it reports
the run ended. The host already ends a run the same way whatever the
reason, so no host change is needed.

Asking twice keeps the first time asked, and asking of a finished run
changes nothing. Somebody else's run answers 404, and a host cannot
stop a run as its owner.
2026-10-09 12:27:09 +03:00
Wanjohi a47d25e7c2 release(install): host agent 0.2.10 (#377)
Installs host agent 0.2.10, which reports its GPUs.
prod-a47d25e
2026-10-09 08:57:57 +00:00
Wanjohi 2ca3229d57 chore(install): host agent 0.2.10 2026-10-09 11:56:43 +03:00
Wanjohi 1cebafdd27 release(api): machines keep the GPUs their host reports (#376)
- The host report accepts `gpus` (model, VRAM in MiB, PCI id); the
machine keeps them and `/machine` returns them. One new nullable column.
- Must reach prod before host agent 0.2.10, which sends the field.
prod-1cebafd
2026-10-09 08:45:57 +00:00
Wanjohi 3fbcf459f2 machine: a host reports its GPUs, and the machine keeps them
The host report gains gpus: each card's model, VRAM in MiB when the
driver says, and the PCI id it was named by. Kept on the machine (a jsonb
column, written only when it changes) and returned by /machine. What a
person calls the machine, and the first measured part of the capacity a
scheduler will place by.
2026-10-09 11:37:40 +03:00
Wanjohi 7c7a694c04 Release: installer pins host agent 0.2.9 (#375)
Installs host agent 0.2.9.
prod-7c7a694
2026-10-09 07:44:55 +00:00
Wanjohi 3975de5027 install: host agent 0.2.9 2026-10-09 10:33:19 +03:00
Wanjohi 4655f44154 Release: host reports accepted, library drops games no longer owned (#374)
- The API accepts a host's own tier label in a box report. Every report
naming a box was rejected with a 400, so stop reasons never reached a
run.
- A library sync now removes the games the account no longer owns. An
empty sync removes nothing, and a game owned again comes back.
- Build: a mesa patch keeping the GE rings off the discardable path.
prod-4655f44
2026-10-09 07:19:57 +00:00
DatCaptainHorseandClaude Opus 5.5 4054bb20d9 build: mesa patch keeping the GE rings off the discardable path
An evicted discardable BO has no GTT fallback, so under VRAM pressure a
box could lose its GE rings and fail every graphics submission until
VRAM freed up. Seen live: Control failed 127k submissions after a
neighbour inside its share evicted it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 15:35:17 +03:00
Wanjohi ba9191825c library: a sync drops the games the account no longer owns
A sync is the whole of what the account owns, but it only ever upserted,
so a revoked licence or a library from a previous Steam account stayed
listed forever. A sync that fully lands now soft-deletes the rest; an
empty list prunes nothing, and a game owned again comes back.
2026-10-08 12:19:05 +03:00
Wanjohi 4445f4836e api: accept the host's own tier label in a report
The report schema pinned each box's tier to our size enum, but hosts name
a box's shape by resolution (1080p). Every report naming a box was a 400,
so stop reasons never reached the run. Placement already stores the size;
the reported label is free text and is not stored.
2026-10-08 12:14:46 +03:00
Wanjohi e17e453c1e Release: installer pins host agent 0.2.8 (#373)
Pins the installer to host agent 0.2.8.
prod-e17e453
2026-10-08 08:12:01 +00:00
Wanjohi 56e64d4ba8 install: host agent 0.2.8 2026-10-08 11:10:59 +03:00
Wanjohi 8716f4d743 Release: installer pins host agent 0.2.7 (#372)
Pins the installer to host agent 0.2.7: a jailed box can bind its
sockets.
prod-8716f4d
2026-10-08 07:30:29 +00:00
Wanjohi a939defd5e install: host agent 0.2.7 2026-10-08 10:29:09 +03:00
Wanjohi 154060b0b6 Release: installer pins host agent 0.2.6 (#371)
Pins the installer to host agent 0.2.6: a jailed box can open its tap.
prod-154060b
2026-10-08 06:59:21 +00:00
Wanjohi 2fb59f4dd8 install: host agent 0.2.6 2026-10-08 09:58:43 +03:00
Wanjohi 1911284ded Release: installer pins host agent 0.2.5 (#370)
Brings #368 to prod. Prod is merged in, keeping dev's installer; against
prod this changes one line, the pin from 0.2.4 to 0.2.5.
prod-1911284
2026-10-08 06:51:45 +00:00
Wanjohi eb3fae6ca6 Merge prod into the host agent 0.2.5 release 2026-10-08 09:50:30 +03:00
Wanjohi d89d73c263 install: host agent 0.2.5 (#368)
Pins the installer to host agent 0.2.5.
2026-10-08 06:37:58 +00:00
Wanjohi 8d167b96b2 Release: host installer runs the agent as root (0.2.4) (#367)
Brings #366 to prod.
prod-8d167b9
2026-10-07 23:13:07 +03:00
Wanjohi 514212bbbe Host agent runs as root; NVIDIA host programs ship in the base image (#366)
- **Installer:** piped into `sudo`, installs to `/usr/local/bin`, and
hands onboarding `/var/lib/nestri`. Root lets the host agent jail each
box under its own uid and give guests a network. A host that ran the
agent as the sudo user has that state copied over (never moved), so it
keeps its machine identity and its downloads. Pins host agent 0.2.4.
- **Base image:** `vhost-user-nvgpu` and `nvgpu-userspace`, static
(musl), from the same `NVGPU_REF` as the guest driver and Vulkan layer,
under `/boot/host`. The host agent takes them out at unpack, as it does
the kernel, and forwards the GPU when it finds the NVIDIA driver loaded.

Merge after the host agent 0.2.4 release: the installer pins it.
2026-10-07 19:59:29 +00:00
Wanjohi 90a2259642 fix(install): stop a running host agent before upgrading it (#365)
Re-running the install command on a host that runs the agent failed:
onboarding refuses to race the agent holding the host lock. The
installer now stops `nestri-host.service` first; onboarding starts it
again on the new binary.
prod-90a2259
2026-10-07 16:09:11 +00:00