Availability tiers¶
Available capacity changes with the owner's activity, often by enough to determine which models fit. Measure each state instead of publishing one free-VRAM figure.
The three tiers¶
| Tier | Owner state | vacancyd sees | Lending |
|---|---|---|---|
| 1: gaming | Playing; a game owns the GPU | session: active + owner process, or GPU pressure |
closed |
| 2: idle | Logged in, unlocked, ordinary apps; or locked | session: active / locked |
closed when unlocked and recently touched, open when locked (after the release hold) |
| 3: logged-out | Nobody logged in, including a PC waiting at the sign-in screen and a Linux server with only SSH sessions | session: logged_out |
open (after the release hold) |
The lending column describes host-scoped upstreams, the default. An upstream set to owner_scope = "resource" is exempt from owner presence: it lends in every tier, limited only by its own resources, and /status, vacancyd status and the console name it. See resource-scoped upstreams.
Lending opens after a hold, not at once. The owner veto engages on the first read that counts as presence and releases only after owner.release_after_s (120 seconds by default) of reads that do not. Locking the screen, signing out, switching user or going idle therefore lends once that hold has passed since the owner was last seen, and returning refuses again immediately.
Locked belongs with idle, not with logged-out. A lock leaves every application running and every framebuffer allocated. The desktop's VRAM cost does not go away when the screen does. What changes at lock is permission, not capacity: the machine becomes lendable while its free VRAM stays roughly what it was. Tier 2 therefore describes one capacity with two lending answers, which is exactly why capacity and permission are separate signals in the daemon.
Tier 3 is the only tier that actually returns memory, because logging out tears down the session's processes and their allocations.
Unlocked is not unconditionally closed. owner.idle_lends_after_s (0 by default, which disables it) reopens tier 2 once an unlocked session has gone untouched by keyboard or mouse for that many seconds, and /status then names idle lending in presence.basis. On Windows this needs the session agent running, since a service alone cannot read idle time. On Linux it uses the idle hint the desktop reports to systemd-logind; a desktop that never sets that hint never lends on idle. Without a reading, or before the threshold is reached, unlocked stays closed exactly as the table says. This is a relaxation within tier 2, not a fourth tier: measured capacity stays whatever tier 2 measured, only the lending permission changes again.
On Windows, a PC at the sign-in screen is in tier 3. That covers a restart that nobody has signed in after, a sign-out, and the Switch user screen. The sign-in screen is a connected session with no user, and vacancyd lends only after two consecutive session reads show the same view, so a brief state during sign-in or sign-out cannot open lending. If another user's session is still signed in, its state decides instead: an unlocked session refuses, a locked or disconnected one lends. A session read that fails or returns a state vacancyd does not recognise refuses. To keep the sign-in screen refusing, set owner.sign_in_screen_is_presence = true or turn on Keep refusing while Windows shows the sign-in screen under Advanced settings in the owner console.
On Linux the same tiers come from systemd-logind. A session counts as someone at the machine when it belongs to a user and is graphical (X11, Wayland or Mir, local or remote) or is a local console login on a seat:
| logind shows | vacancyd sees | Lending |
|---|---|---|
| A counted session in the foreground, unlocked | session: active |
closed, unless idle lending applies |
| No unlocked counted session in the foreground, and at least one counted session locked | session: locked |
open after the release hold |
| Counted sessions, none in the foreground or locked (switched to another user or terminal) | session: disconnected |
open after two identical reads and the release hold |
| No counted session: only SSH logins, the display-manager greeter, or nothing | session: logged_out |
open after two identical reads and the release hold |
| The first of those two reads, including the first read after logind returns | session: unknown |
closed |
| logind cannot be read | session: unknown |
closed |
SSH and other remote text logins, the per-user service manager, background sessions and sessions that are closing do not count. The greeter is the Linux sign-in screen; owner.sign_in_screen_is_presence = true keeps a greeter in the foreground refusing. A host without systemd-logind can set owner.presence = "headless" to lend as logged out without reading sessions at all. /status shows every session logind reported under presence.sessions, with whether it counted and why.
Measure each state¶
An idle desktop, a locked desktop and a logged-out machine can have different free memory. Existing models and background applications also change the budget. Measure the states your owner policy permits on the machine you will lend; do not copy another machine's capacity figures. A locked screen changes presence, while the applications it leaves running can still consume resources.
Measuring¶
scripts/measure-tiers.ps1 samples every NVIDIA GPU, system memory, CPU load, and vacancyd's own session view, tagging each row with its tier. It is read-only and allocates nothing on any GPU.
.\scripts\measure-tiers.ps1 -Tier idle -Minutes 10
.\scripts\measure-tiers.ps1 -Tier gaming -Minutes 15 # then launch the game
.\scripts\measure-tiers.ps1 -Tier locked -Minutes 10 # then lock
.\scripts\measure-tiers.ps1 -Tier logged-out -Minutes 10 # needs a service/task
Tier 3 cannot be measured from an interactive shell because the shell dies at logout. Run it from the vacancyd service itself (its metrics are sampled continuously and survive logout), or as a scheduled task with "run whether user is logged on or not".
⚠️ These transitions are the operator's to make. The script never locks the machine, never logs anyone out, and never touches power or display policy. Automating those is a separate decision; on this workstation they are deliberately manual today.
Turning tiers into thresholds¶
A per-model threshold in vacancyd.toml is the free memory a request needs when it may have to load the model: measured footprint + ~500 MiB headroom. Once the model is resident and a request reuses it as loaded, admission charges the model's resident_min_free_mib (default 0) instead, so the threshold is not a second copy of memory the model already holds (see the gate). Sanity-check the load threshold against each tier:
| If the threshold is… | …then the model |
|---|---|
| below tier 3 free | fits the measured logged-out capacity; it may also fit in other states |
| below tier 2 free | fits the measured idle or locked capacity; admission still depends on owner policy and current observations |
| below tier 1 free | fits the measured gaming-state capacity; admission still depends on owner policy and current observations |
The gate does not need to know about tiers: it compares live free capacity against the threshold, and the tiers are simply the recurring shapes that live capacity takes. The tiers are for choosing thresholds and for explaining the machine to whoever borrows from it.
Recording results¶
Publish the tier tables for borrowers and keep the raw CSVs with the measurements behind each threshold.