System requirements¶
What a machine needs to run vibey and its krypton clients, and how each figure is known.
The tables on this page are generated: scripts/minimum_specs.py writes them from the
committed evidence record,
docs/architecture/evidence/minimum-specs.json,
and the text between the markers is never edited by hand. The prose around them is.
How these figures are kept current¶
The requirements are rolling. A weekly workflow (.github/workflows/minimum-specs.yml)
runs python scripts/minimum_specs.py measure on the project's self-hosted macOS runner,
and measures the Linux matrix (below) on GitHub-hosted runners of each architecture. It
re-derives the requirements, re-renders this page and the research paper's tables, and
opens a pull request with the result. Nothing reaches develop except through that pull request
and the normal merge train. The configuration it runs from (contexts, models, thresholds,
assumptions and the idle rule) is declared in scripts/minimum_specs.toml.
Every figure in the record carries its value and unit, the command or method that produced it, when (UTC), on which host, and under which conditions. Each has one of five statuses, and the tables say which:
| Status | Meaning |
|---|---|
| measured | A command on the named host produced it. |
| declared | Read from the repository: a constant in the code, a floor in a manifest, or a stated assumption. |
| derived | Arithmetic on other figures. The formula and every input are in the record, and check recomputes it. |
| stale | The weekly measurement could not run. The last good value is kept and shown as stale since the date it was last measured, with the reason. It is never reused silently. |
| skipped | It could not run and there has never been a good value. No number is shown. |
A few figures come from a one-off pass that the weekly probes do not repeat, such as the offline network matrix and the real DESIGN call. They are shown with their own date.
The model measurements need a quiet host. They run only when the one-minute load is at or below the declared ceiling, no other client has sent the Ollama server an inference request within the quiet window, and the declared busy check is silent. The run waits a bounded time. If the host is still busy after that, the model figures are recorded as skipped, and the previous values stay in the record, marked stale.
Scope of the evidence. Every measured figure in the hardware, software, network and client tables comes from one machine, named in each table: an Apple M5 Mac with 24 GiB of unified memory. The seed values were measured on 2026-09-29 and 2026-09-30 by three passes (software, network and hardware). The Linux tables come from each distribution's container, named in each table. Anything about other hardware is either derived or listed under Not verified.
Hardware¶
The sovereign default is one host running vibey, PostgreSQL and Ollama serving
gpt-oss:20b. Memory is the binding constraint. vibey asks the model for two context
sizes. The conductor (DESIGN and DECOMPOSE) sizes its window to the prompt, up to 8,192
tokens by default. The gptossloop runner (BUILD) assumes 32,768 tokens. Through /v1,
though, Ollama loads the model at its own server default, which on a default install is the
model's maximum of 131,072. The minimum is sized for the runner's window. The
recommended size is sized for the model's maximum, plus room for other applications.
Measured on Mac17,2 · Apple M5 · 24 GiB · macOS 26.6.2 (25G83), aarch64 · unknown chip · 23 GiB · Ubuntu 24.04.4 LTS; figures dated 2026-09-29 to 2026-10-02 (UTC). Source: docs/architecture/evidence/minimum-specs.json, regenerated by scripts/minimum_specs.py; do not edit inside these markers.
| Requirement | Minimum | Recommended | Basis |
|---|---|---|---|
| Memory, Apple Silicon (unified) | 24 GB (stale since 2026-09-30: derived from stale input(s): ram.minimum_need_gib) (needs 17.79 GiB (stale since 2026-09-30: derived from stale input(s): ram.model_process.ctx32768, ram.vibey_side_gib)) | 32 GB (stale since 2026-09-30: derived from stale input(s): ram.recommended_need_gib) (needs 24.42 GiB (stale since 2026-09-30: derived from stale input(s): ram.model_process.ctx131072, ram.vibey_side_gib)) | derived, stale |
| Memory, 16 GB Mac | insufficient (stale since 2026-09-30: derived from stale input(s): ram.design_only_need_gib): DESIGN alone needs 17.15 GiB (stale since 2026-09-30: derived from stale input(s): ram.model_process.ctx8192, ram.vibey_side_gib); the GPU part is -78.0 MiB (stale since 2026-09-30: derived from stale input(s): bench.gpt-oss:20b.ctx8192.device_mib, gpu.metal_limit_16gb_mib) over its limit at 8,192 | - | derived, stale |
| GPU memory for gpt-oss:20b | 12,339 MiB (stale since 2026-09-30: no accounting line in the log) at 8,192; 12,974 MiB (stale since 2026-09-30: no accounting line in the log) at 32,768 | discrete GPU: 16 GB (stale since 2026-09-30: derived from stale input(s): bench.gpt-oss:20b.ctx32768.device_mib) card (not verified on CUDA) | measured, stale; derived, stale |
| Model throughput at 24k depth | 10 tok/s generation, 100 tok/s prompt (worst BUILD turn 512 s of 900 s) | 25 / 500 tok/s (worst turn 143 s); measured here 18.1 tok/s / 379 tok/s | derived; measured |
| CPU only (no GPU) | DESIGN call 462 s (fits: yes); worst BUILD turn 1,422 s (fits: no) | use a GPU; CPU only measured 2.8 tok/s generation, 44.5 tok/s prompt | derived; measured |
| Free disk | 20 GB (stale since 2026-09-30: derived from stale input(s): disk.minimum_need_gb) (needs 17.8 GB (stale since 2026-09-30: derived from stale input(s): disk.ollama_app_bytes)) | 50 GB (stale since 2026-09-30: derived from stale input(s): disk.recommended_need_gb) (needs 45.2 GB (stale since 2026-09-30: derived from stale input(s): disk.minimum_need_gb)) | derived, stale |
| PostgreSQL memory | summed RSS peak 57.5 MiB (default shared_buffers suffices) | - | measured |
| vibey processes | CLI 75.2 MiB (stale since 2026-09-30: no scratch database); worker 276 MiB (stale since 2026-09-30: no scratch database); hub idle 102 MiB (stale since 2026-09-30: no scratch database) | krypton launcher + hub idle 138 MiB (stale since 2026-09-30: no scratch database) | measured, stale |
What the rows mean in practice:
- A 16 GB Mac cannot run the sovereign default. Even DESIGN alone, at the conductor's 8,192-token ceiling, needs more than 16 GiB. The part that must sit on the GPU is also over the working-set limit a 16 GB Mac would have. This verdict is derived, not measured on 16 GB hardware.
- On 24 GB, the model runs, but the measured host swapped. Free memory fell to a few tens of MiB during DESIGN calls. The measured host was also running a container workload, so a quieter 24 GB machine should have more margin; that was not measured.
- CPU only is enough for the DESIGN interview, and not for BUILD. The throughput rows
are held against vibey's own per-request timeout (
VIBEY_OLLAMA_TIMEOUT, 900 s by default). A worst-case BUILD turn at CPU-only rates takes longer than that. Use a GPU. ollama psis not the memory to plan with. It understates the loaded model by 1.2 to 4.2 GiB. The memory figures here come from llama-server's own accounting in the Ollama log.
Software¶
Measured on Mac17,2 · Apple M5 · 24 GiB · macOS 26.6.2 (25G83), aarch64 · unknown chip · 23 GiB · Ubuntu 24.04.4 LTS; figures dated 2026-09-29 to 2026-10-02 (UTC). Source: docs/architecture/evidence/minimum-specs.json, regenerated by scripts/minimum_specs.py; do not edit inside these markers.
| Requirement | Minimum | Recommended | Basis |
|---|---|---|---|
| Python | 3.12 (declared >=3.12) | works on 3.12, 3.13, 3.14 | derived; declared |
| PostgreSQL | 14 (checked on connect) | measured on 18.4 (stale since 2026-09-29: $VIBEY_SPECS_PG_ADMIN_URL is not set): 20 (stale since 2026-09-29: $VIBEY_SPECS_PG_ADMIN_URL is not set) migrations applied | declared; measured, stale |
| Ollama with gpt-oss:20b (sovereign default) | required unless a paid engine is set up; measured on 0.35.0 | - | measured |
| git | 2.26 | - | declared |
- Python. The floor is the oldest candidate interpreter that installs the engine and
runs
vibey --version. An older interpreter is refused by the resolver, because the package declaresrequires-python. Python 3.15 does not install yet: its release candidate has nopydantic-corewheel. - PostgreSQL is required, and vibey never guesses a database: set
VIBEY_PG_URL. vibey checks the server's major version on connect. Forvibey doctorto pass cleanly, also set up scram-sha-256 authentication and split owner and application roles (ADR-0055, ADR-0061). - Ollama (or another OpenAI-compatible server, such as
llama-serveror vLLM) is required for the sovereign default. Without it you need a paid engine and its credentials. No paid engine CLI or key is needed for the sovereign path. - Optional:
uvis the recommended installer, and pip or pipx should also work (not tested). RabbitMQ is used only when a[bus]is configured. Docker or Podman is used for container isolation and image builds. Helm, kubectl and minikube are for Kubernetes.azis for the Azure deployment stages.ghis used only by the maintainer tooling (vibey-gh).
Network¶
Measured on Mac17,2 · Apple M5 · 24 GiB · macOS 26.6.2 (25G83), aarch64 · unknown chip · 23 GiB · Ubuntu 24.04.4 LTS; figures dated 2026-09-29 to 2026-10-02 (UTC). Source: docs/architecture/evidence/minimum-specs.json, regenerated by scripts/minimum_specs.py; do not edit inside these markers.
| Requirement | Minimum | Recommended | Basis |
|---|---|---|---|
| Install download, vibey-engine | 236.4 MB | with [hub] 238.0 MB; krypton-app 238.0 MB | measured |
| Installed size, vibey-engine | 701.9 MB | krypton-app 709.5 MB | measured |
| Model download, gpt-oss:20b | 13.79 GB | qwen3:14b 9.28 GB (opt-in) | measured |
| First install on the wire | 14.03 GB | - | derived |
| Internet at runtime (sovereign path) | none | - | measured |
Bandwidth matters once, at install time, and the model is almost all of it. At runtime the sovereign path uses loopback only: PostgreSQL on 5432 and Ollama on 11434, plus the hub on 8765 when a krypton client connects. No telemetry, analytics, update check or version check is made at runtime. The engine SDKs for the paid engines ship in the base package, so a sovereign-only install downloads them too.
Host allowlist¶
From the network pass of 2026-09-29. Observed means seen on the wire or by the tracer. Code means read from the code or configuration only.
Install time
| Host | Port | Purpose | Needed for | Observed / code |
|---|---|---|---|---|
pypi.org |
443 | Package index | Installing vibey-engine and krypton-app | observed |
files.pythonhosted.org |
443 | Wheels | Same | observed |
releases.astral.sh |
443 | uv-managed CPython | Only when no Python 3.12 or newer is present | observed |
registry.ollama.ai |
443 | Model manifests, redirects to the blob store | ollama pull gpt-oss:20b (and qwen3:14b) |
observed |
Cloudflare R2 (*.r2.cloudflarestorage.com) |
443 | Model blob bytes | Same | observed |
registry.npmjs.org |
443 | npm tarballs | Building the VS Code extension or the React Native app from source | observed |
huggingface.co |
443 | GGUF weights | Only gptossloop/qwenloop model install (llama-server backend) |
code |
gitlab.gnome.org, Flathub |
443 | json-glib wrap, GNOME runtime | Building the GTK desktop client | code |
Runtime, sovereign default path
| Host | Port | Purpose | Needed for | Observed / code |
|---|---|---|---|---|
localhost or a unix socket |
5432 | PostgreSQL: queue, ledger, LISTEN/NOTIFY | Every database command | observed |
127.0.0.1 |
11434 | Ollama /api/chat (conductor) and /v1/* (gptossloop) |
Sovereign DESIGN, DECOMPOSE and BUILD | observed |
127.0.0.1 |
8765 | The hub, vibey serve |
krypton clients | observed |
Runtime, optional features
| Host | Port | Purpose | Needed for | Observed / code |
|---|---|---|---|---|
api.github.com, github.com |
443 (22 for ssh) | GitHub API through gh; git push |
The triaged-delivery bridge, claudeloop issue import, vibey-gh |
observed / code |
Anthropic (api.anthropic.com) |
443 | claudeloop sessions through the bundled CLI | claudeloop | observed |
api2.cursor.sh, api.cursor.com |
443 | The Cursor SDK bridge | cursorloop (its doctor is live) |
observed |
api.openai.com (or a configured base URL) |
443 | The codex CLI | codexloop | code |
generativelanguage.googleapis.com, aiplatform.googleapis.com |
443 | The Antigravity SDK | agyloop | code |
Azure Resource Manager, through az |
443 | Deployment stages ④–⑥ | Only vibey worker --azure az |
code |
| The Kubernetes API | 443 or 6443 | vibey operator |
Only the operator extra |
code |
| Webhook URLs (global addresses only) | 443 or 80 | Gate notifications, HMAC-signed | Only when [notifications] webhooks are set |
code |
| LAN clients to the hub; mDNS | 8765 TCP (TLS), 5353 UDP | Device access and _vibey._tcp advertisement |
Only on a declared LAN ([hub] lan = true and --host) |
observed / code |
Offline capability¶
With every outbound connection dead and only local PostgreSQL and Ollama up, these all
worked: vibey --version, migrate, doctor, new, work on the sovereign gptossloop
provider (against a model-free stub), answer, worker --once, status, projects,
gates, engines, cost, budget, serve, hub pair, and the krypton launcher.
What needs the internet: installing packages, pulling models, the paid engines, the
triaged-delivery bridge, webhooks, --azure az, the operator and the opt-in integrations.
vibey doctor offline differs in one verdict: cursorloop's live authentication check
fails.
krypton clients¶
Measured on aarch64 · unknown chip · 23 GiB · Ubuntu 24.04.4 LTS; figures dated 2026-10-02 (UTC). Source: docs/architecture/evidence/minimum-specs.json, regenerated by scripts/minimum_specs.py; do not edit inside these markers.
| Requirement | Minimum | Recommended | Basis |
|---|---|---|---|
| krypton-app (launcher) | Python 3.12; pulls vibey-engine[hub] | - | derived |
| VS Code extension | VS Code ^1.90.0; Node >=20 to build | Node 24 | declared |
| React Native app (mobile, web) | Node >=20.19.4 | Node 24 | declared |
| Desktop (C, GTK 4) | meson >= 1.1.0; glib >= 2.74; json-glib >= 1.6 | GUI: gtk4 >= 4.12, libadwaita >= 1.4 | declared |
- krypton-app (the
kryptonlauncher) startsvibey serve, so it has vibey's own requirements and depends onvibey-engine[hub]. It needs PostgreSQL andVIBEY_PG_URLlike vibey does. - The VS Code extension runs inside VS Code 1.90 or newer. Building it from source needs
Node and npm. It talks to the hub only when
vibey.hubUrlis set; otherwise it polls Ollama on loopback every 30 s, about 0.8 KB per refresh. It is published to Open VSX asthe-vibey-project.krypton(0.2.0, first published with 3.3.0), and every release attaches its.vsix. - The React Native app (mobile and web) needs Node to build. The release builds the
Android package with Gradle and Java 17 on Ubuntu, and the iOS package with EAS on a
macOS runner with Xcode (
scripts/release_binaries.toml). The iOS package of the release-binaries dry run 36976031188 (krypton-ios-0.1.0.ipa) declares iOS 16.4 as its minimum (MinimumOSVersion) and is signed for the App Store, so it reaches a phone through TestFlight or the App Store; no device install has been recorded. - The desktop client is C17 on GTK 4 and libadwaita, built with meson. The core builds
without the GUI libraries.
libsoup-3.0(hub transport) and Avahi ordns_sd(discovery) are optional. Without discovery, you pair by typing the address and code. - On Linux, the release's tarball needs the distribution's GTK 4, libadwaita, libsoup 3, json-glib and Avahi; the Flatpak needs only Flatpak and fetches the GNOME runtime from Flathub, which carries no Avahi, so it cannot discover hubs by itself.
- On macOS, from 3.4.0, the release's
.dmgholds a self-containedkrypton.appfor Apple silicon that carries its own GTK 4 runtime, so it needs no Homebrew. It runs on macOS 15 Sequoia or newer: the app built by the dry run 36976031188 declaresLSMinimumSystemVersion15.0, the macOS of the runner it is built on. It discovers hubs over Bonjour (dns_sd) and asks once for local-network permission. It is ad-hoc signed and not notarised until the Developer ID secrets are set, so the first launch needs a confirmation (Downloads). Intel Macs are not supported: Homebrew no longer bottles the GTK 4 stack for them.
Linux: Ubuntu, Arch and Fedora on x86_64 and arm64¶
The Linux requirements are a matrix. Each supported distribution, on each architecture,
is one cell, and each cell is measured inside that distribution's own container image by
python scripts/minimum_specs.py cell --distro <d> --arch <a>. The distributions, the
architectures, the images, the package sets and where each distribution's support is
claimed are all declared in [minimum_specs.linux] in scripts/minimum_specs.toml, not
in this page or the workflow: today Ubuntu 24.04 LTS and Arch Linux (the CI desktop
job builds on both), Ubuntu 26.04 LTS (planned, #1116) and Fedora (named by the
operator; a COPR channel is in ADR-0019), each on x86_64 and arm64.
What a cell measures:
- The floors the distribution offers. The repository version of the kernel, Python,
PostgreSQL and the desktop client's libraries, read from package metadata without
installing them, and the glibc of the image. Each is compared with the floor vibey
declares: PostgreSQL against the major version vibey checks on connect, Python against
the oldest interpreter the engine runs on (if the distribution's is older, uv fetches
one), glibc against the newest glibc any installed wheel's
manylinuxtag requires, and the libraries againstclients/desktop/meson.build. - The closures. What each package set adds to the pristine image, sized by the package manager's own installed-size records: the base vibey needs (Python, git, certificates, and curl to fetch uv), PostgreSQL's server, and the desktop client's GTK 4 runtime. Recommended and weak dependencies are left out.
- A cold install of
vibey-engineandkrypton-appwith uv, from wheels built once on the host, with its venv, cache and download sizes.
In CI (.github/workflows/minimum-specs.yml) every cell runs on a native GitHub-hosted
runner of its architecture, ubuntu-24.04 or ubuntu-24.04-arm, and the publish job
folds the cells in with merge --complete: a cell whose job handed nothing over is marked
stale, with that reason. Run on the other architecture, a cell is emulated (QEMU): its
sizes and versions stand, because bytes are bytes, but its timings are refused, because
they would measure the emulator. A cell with no image is recorded as skipped with the
reason: there is no official Arch Linux image for arm64. A container's kernel, cores and
memory are its runner's, so they are recorded with the host, not as the distribution's.
Measured on aarch64 · unknown chip · 23 GiB · Ubuntu 24.04.4 LTS, archlinux:latest (linux/amd64, native) on GitHub-hosted GitHub Actions 1000073941, fedora:latest (linux/amd64, native) on GitHub-hosted GitHub Actions 1000073938, fedora:latest (linux/arm64, native) on GitHub-hosted GitHub Actions 1000073936, ubuntu:24.04 (linux/amd64, native) on GitHub-hosted GitHub Actions 1000073942, ubuntu:24.04 (linux/arm64, native) on GitHub-hosted GitHub Actions 1000073935, ubuntu:26.04 (linux/amd64, native) on GitHub-hosted GitHub Actions 1000073939, ubuntu:26.04 (linux/arm64, native) on GitHub-hosted GitHub Actions 1000073937; figures dated 2026-10-02 (UTC). Source: docs/architecture/evidence/minimum-specs.json, regenerated by scripts/minimum_specs.py; do not edit inside these markers.
| Distribution | Arch | Run | OS release | glibc | Kernel (packaged) | Python (packaged) | PostgreSQL (packaged) | Desktop libraries (packaged) |
|---|---|---|---|---|---|---|---|---|
| Ubuntu 24.04 LTS | x86_64 | native | Ubuntu 24.04.5 LTS | 2.39 (wheels need 2.34: yes) | 6.8.0 | 3.12.3 (>= 3.12: yes) | 16 (>= 14: yes) | gtk4 4.14.5, libadwaita 1.5.0 (meet floors: yes) |
| Ubuntu 24.04 LTS | aarch64 | native | Ubuntu 24.04.5 LTS | 2.39 (wheels need 2.34: yes) | 6.8.0 | 3.12.3 (>= 3.12: yes) | 16 (>= 14: yes) | gtk4 4.14.5, libadwaita 1.5.0 (meet floors: yes) |
| Ubuntu 26.04 LTS | x86_64 | native | Ubuntu 26.04.1 LTS | 2.43 (wheels need 2.34: yes) | 7.0.0 | 3.14.3 (>= 3.12: yes) | 18 (>= 14: yes) | gtk4 4.22.4, libadwaita 1.9.1 (meet floors: yes) |
| Ubuntu 26.04 LTS | aarch64 | native | Ubuntu 26.04.1 LTS | 2.43 (wheels need 2.34: yes) | 7.0.0 | 3.14.3 (>= 3.12: yes) | 18 (>= 14: yes) | gtk4 4.22.4, libadwaita 1.9.1 (meet floors: yes) |
| Arch Linux | x86_64 | native | Arch Linux | 2.44 (wheels need 2.34: yes) | 7.2.8 | 3.14.7 (>= 3.12: yes) | 18.6 (>= 14: yes) | gtk4 4.22.5, libadwaita 1.9.4 (meet floors: yes) |
| Arch Linux | aarch64 | not run: no official Arch Linux image for arm64: archlinux:latest is amd64 only, and Arch Linux ARM is a separate project, not Arch | - | - | - | - | - | - |
| Fedora (current release) | x86_64 | native | Fedora Linux 44 (Container Image) | 2.43 (wheels need 2.34: yes) | 7.2.8 | 3.14.7 (>= 3.12: yes) | 18.6 (>= 14: yes) | gtk4 4.22.5, libadwaita 1.9.4 (meet floors: yes) |
| Fedora (current release) | aarch64 | native | Fedora Linux 44 (Container Image) | 2.43 (wheels need 2.34: yes) | 7.2.8 | 3.14.7 (>= 3.12: yes) | 18.6 (>= 14: yes) | gtk4 4.22.5, libadwaita 1.9.4 (meet floors: yes) |
The requirements per cell. Memory is the same in every cell: the model dominates, and its weights and KV cache are the same bytes on any Linux. Cores depend on the architecture. Disk is each cell's own. A cell reading not measured names its reason in What is stale right now.
Measured on Mac17,2 · Apple M5 · 24 GiB · macOS 26.6.2 (25G83), aarch64 · unknown chip · 23 GiB · Ubuntu 24.04.4 LTS, archlinux:latest (linux/amd64, native) on GitHub-hosted GitHub Actions 1000073941, fedora:latest (linux/amd64, native) on GitHub-hosted GitHub Actions 1000073938, fedora:latest (linux/arm64, native) on GitHub-hosted GitHub Actions 1000073936, ubuntu:24.04 (linux/amd64, native) on GitHub-hosted GitHub Actions 1000073942, ubuntu:24.04 (linux/arm64, native) on GitHub-hosted GitHub Actions 1000073935, ubuntu:26.04 (linux/amd64, native) on GitHub-hosted GitHub Actions 1000073939, ubuntu:26.04 (linux/arm64, native) on GitHub-hosted GitHub Actions 1000073937; figures dated 2026-09-29 to 2026-10-02 (UTC). Source: docs/architecture/evidence/minimum-specs.json, regenerated by scripts/minimum_specs.py; do not edit inside these markers.
| Distribution | Arch | Memory min / rec | Cores min / rec | Disk min / rec | Basis |
|---|---|---|---|---|---|
| Ubuntu 24.04 LTS | x86_64 | 24 GB (stale) / 32 GB (stale) | not measured / not measured | 20 GB / 50 GB | derived, stale; not measured; derived |
| Ubuntu 24.04 LTS | aarch64 | 24 GB (stale) / 32 GB (stale) | unreachable / 4 | 20 GB / 50 GB | derived, stale; derived |
| Ubuntu 26.04 LTS | x86_64 | 24 GB (stale) / 32 GB (stale) | not measured / not measured | 20 GB / 50 GB | derived, stale; not measured; derived |
| Ubuntu 26.04 LTS | aarch64 | 24 GB (stale) / 32 GB (stale) | unreachable / 4 | 20 GB / 50 GB | derived, stale; derived |
| Arch Linux | x86_64 | 24 GB (stale) / 32 GB (stale) | not measured / not measured | 20 GB / 50 GB | derived, stale; not measured; derived |
| Arch Linux | aarch64 | 24 GB (stale) / 32 GB (stale) | unreachable / 4 | - | derived, stale; derived; not measured |
| Fedora (current release) | x86_64 | 24 GB (stale) / 32 GB (stale) | not measured / not measured | 20 GB / 50 GB | derived, stale; not measured; derived |
| Fedora (current release) | aarch64 | 24 GB (stale) / 32 GB (stale) | unreachable / 4 | 20 GB / 50 GB | derived, stale; derived |
How the Linux requirements are derived¶
Each requirement is fitted to measured points and solved in closed form
(scripts/requirements_math.py), so check refits it from the record and gets the same
digits. The model behind each one is the one its mechanism predicts:
- Memory is a line in the context window. The weights are a constant and the KV cache and compute buffers grow in proportion to the window, so the model's memory is fitted by least squares as M(c) = M₀ + k·c over the measured sweep, and R² and the largest residual are kept so that a bend would show. On Linux without a discrete GPU the whole model sits in system RAM, so the requirement is h·M(c) at the runner's window, plus vibey, PostgreSQL and the OS allowance, with h a declared headroom factor. The same line, solved for c, gives the largest context a given memory size holds.
- Cores follow Amdahl's law. CPU generation on n threads is fitted as T(n) = T₁ / ((1 − p) + p/n) through its reciprocal, which is linear in 1/n. Its marginal gain dT/dn = T₁·p / ((1 − p)·n + p)² falls monotonically, so the recommended core count is where it drops to a declared threshold θ, n = (√(T₁·p/θ) − p)/(1 − p), and the minimum is the fewest cores reaching the minimum generation rate F, n ≥ p / (T₁/F − (1 − p)), which has no answer when F is at or above the asymptote T₁/(1 − p). The sweep is measured, idle-gated, by the weekly host run (CPU only, one thread count at a time); an architecture no host has swept says not measured*.
- The ledger's disk is an integral. The ledger is append-only, so with b bytes per job
(measured: the database after one project and one job, minus the empty one) and a job
rate r(t) = r₀ + r₁·t, the bytes after H days are ∫₀ᴴ b·(r₀ + r₁·t) dt. The rate and the
horizon are declared in
[minimum_specs.growth].
Measured on Mac17,2 · Apple M5 · 24 GiB · macOS 26.6.2 (25G83), aarch64 · unknown chip · 23 GiB · Ubuntu 24.04.4 LTS; figures dated 2026-09-30 to 2026-10-02 (UTC). Source: docs/architecture/evidence/minimum-specs.json, regenerated by scripts/minimum_specs.py; do not edit inside these markers.
Memory against context: least squares over the measured sweep
M(c) = M₀ + k·c
M₀ = 13,241 MiB (stale) k = 27.874 (stale) KiB/token R² = 0.99998 (stale) max |residual| = 9.9 MiB (stale)
Linux memory: h = 1.1 (declared headroom), the model held in system RAM
RAM(c) = h·M(c)/1024 + vibey side + OS headroom
minimum = RAM(32,768) = 19.18 GiB (stale) → 24 GB (stale)
recommended = RAM(131,072) + apps headroom (4 GiB) = 26.06 GiB (stale) → 32 GB (stale)
c_max(16 GB) = ((S·10⁹/2²⁰ − (side + OS)·1024)/h − M₀)/(k/1024) = 0 (stale) tokens; holds 32,768: no (stale)
CPU throughput against cores: Amdahl's law, per architecture
T(n) = T₁ / ((1 − p) + p/n) fitted as 1/T = (1 − p)/T₁ + (p/T₁)·(1/n)
dT/dn = T₁·p / ((1 − p)·n + p)²
knee: dT/dn = θ ⇒ n* = (√(T₁·p/θ) − p)/(1 − p), θ = 0.25 tok/s per core
floor: T(n) ≥ F ⇔ n ≥ p/(T₁/F − (1 − p)), F = 10 tok/s; none if F ≥ T₁/(1 − p)
x86_64: T₁ = not measured, p = not measured, R² = not measured, T∞ = not measured; knee not measured, minimum not measured
aarch64: T₁ = 1.2 tok/s, p = 0.6865, R² = 0.1871, T∞ = 4.0 tok/s; knee 4, minimum unreachable
Ledger growth: append-only, so the disk it needs is the integral of its rate
D(H) = ∫₀ᴴ b·(r₀ + r₁·t) dt = b·(r₀·H + r₁·H²/2)
b = not measured per job, r₀ = 50 jobs/day, r₁ = 0 jobs/day², H = 365 days ⇒ D = not measured
What is stale right now¶
Record generated 2026-10-02T13:55:54.709Z.
| Figure | Status | Since | Reason |
|---|---|---|---|
bench.gpt-oss:20b.ctx131072.device_mib |
stale | 2026-09-30 | no accounting line in the log |
bench.gpt-oss:20b.ctx131072.host_compute_mib |
stale | 2026-09-30 | no accounting line in the log |
bench.gpt-oss:20b.ctx131072.host_model_mib |
stale | 2026-09-30 | no accounting line in the log |
bench.gpt-oss:20b.ctx131072.kv_mib |
stale | 2026-09-30 | no accounting line in the log |
bench.gpt-oss:20b.ctx32768.device_mib |
stale | 2026-09-30 | no accounting line in the log |
bench.gpt-oss:20b.ctx32768.host_compute_mib |
stale | 2026-09-30 | no accounting line in the log |
bench.gpt-oss:20b.ctx32768.host_model_mib |
stale | 2026-09-30 | no accounting line in the log |
bench.gpt-oss:20b.ctx32768.kv_mib |
stale | 2026-09-30 | no accounting line in the log |
bench.gpt-oss:20b.ctx4096.device_mib |
stale | 2026-09-30 | no accounting line in the log |
bench.gpt-oss:20b.ctx4096.host_compute_mib |
stale | 2026-09-30 | no accounting line in the log |
bench.gpt-oss:20b.ctx4096.host_model_mib |
stale | 2026-09-30 | no accounting line in the log |
bench.gpt-oss:20b.ctx4096.kv_mib |
stale | 2026-09-30 | no accounting line in the log |
bench.gpt-oss:20b.ctx8192.device_mib |
stale | 2026-09-30 | no accounting line in the log |
bench.gpt-oss:20b.ctx8192.host_compute_mib |
stale | 2026-09-30 | no accounting line in the log |
bench.gpt-oss:20b.ctx8192.host_model_mib |
stale | 2026-09-30 | no accounting line in the log |
bench.gpt-oss:20b.ctx8192.kv_mib |
stale | 2026-09-30 | no accounting line in the log |
disk.ledger_growth_gb |
skipped | - | missing input(s): postgres.bytes_per_job |
disk.minimum_gb |
stale | 2026-09-30 | derived from stale input(s): disk.minimum_need_gb |
disk.minimum_need_gb |
stale | 2026-09-30 | derived from stale input(s): disk.ollama_app_bytes |
disk.ollama_app_bytes |
stale | 2026-09-30 | /Applications/Ollama.app is absent on this host |
disk.recommended_gb |
stale | 2026-09-30 | derived from stale input(s): disk.recommended_need_gb |
disk.recommended_need_gb |
stale | 2026-09-30 | derived from stale input(s): disk.minimum_need_gb |
fit.cpu.x86_64.asymptote_tok_s |
skipped | - | a fit needing 3 of its 6 measured points has 0; missing bench.gpt-oss:20b.cpu.x86_64.threads1.gen_tok_s and 5 more |
fit.cpu.x86_64.knee_cores |
skipped | - | a fit needing 3 of its 6 measured points has 0; missing bench.gpt-oss:20b.cpu.x86_64.threads1.gen_tok_s and 5 more |
fit.cpu.x86_64.minimum_cores |
skipped | - | a fit needing 3 of its 6 measured points has 0; missing bench.gpt-oss:20b.cpu.x86_64.threads1.gen_tok_s and 5 more |
fit.cpu.x86_64.p |
skipped | - | a fit needing 3 of its 6 measured points has 0; missing bench.gpt-oss:20b.cpu.x86_64.threads1.gen_tok_s and 5 more |
fit.cpu.x86_64.r2 |
skipped | - | a fit needing 3 of its 6 measured points has 0; missing bench.gpt-oss:20b.cpu.x86_64.threads1.gen_tok_s and 5 more |
fit.cpu.x86_64.t1_tok_s |
skipped | - | a fit needing 3 of its 6 measured points has 0; missing bench.gpt-oss:20b.cpu.x86_64.threads1.gen_tok_s and 5 more |
fit.memory.k_kib_per_token |
stale | 2026-09-30 | derived from stale input(s): bench.gpt-oss:20b.ctx4096.device_mib, bench.gpt-oss:20b.ctx4096.host_model_mib, bench.gpt-oss:20b.ctx4096.host_compute_mib, bench.gpt-oss:20b.ctx8192.device_mib, bench.gpt-oss:20b.ctx8192.host_model_mib, bench.gpt-oss:20b.ctx8192.host_compute_mib, bench.gpt-oss:20b.ctx32768.device_mib, bench.gpt-oss:20b.ctx32768.host_model_mib, bench.gpt-oss:20b.ctx32768.host_compute_mib, bench.gpt-oss:20b.ctx131072.device_mib, bench.gpt-oss:20b.ctx131072.host_model_mib, bench.gpt-oss:20b.ctx131072.host_compute_mib |
fit.memory.m0_mib |
stale | 2026-09-30 | derived from stale input(s): bench.gpt-oss:20b.ctx4096.device_mib, bench.gpt-oss:20b.ctx4096.host_model_mib, bench.gpt-oss:20b.ctx4096.host_compute_mib, bench.gpt-oss:20b.ctx8192.device_mib, bench.gpt-oss:20b.ctx8192.host_model_mib, bench.gpt-oss:20b.ctx8192.host_compute_mib, bench.gpt-oss:20b.ctx32768.device_mib, bench.gpt-oss:20b.ctx32768.host_model_mib, bench.gpt-oss:20b.ctx32768.host_compute_mib, bench.gpt-oss:20b.ctx131072.device_mib, bench.gpt-oss:20b.ctx131072.host_model_mib, bench.gpt-oss:20b.ctx131072.host_compute_mib |
fit.memory.max_residual_mib |
stale | 2026-09-30 | derived from stale input(s): bench.gpt-oss:20b.ctx4096.device_mib, bench.gpt-oss:20b.ctx4096.host_model_mib, bench.gpt-oss:20b.ctx4096.host_compute_mib, bench.gpt-oss:20b.ctx8192.device_mib, bench.gpt-oss:20b.ctx8192.host_model_mib, bench.gpt-oss:20b.ctx8192.host_compute_mib, bench.gpt-oss:20b.ctx32768.device_mib, bench.gpt-oss:20b.ctx32768.host_model_mib, bench.gpt-oss:20b.ctx32768.host_compute_mib, bench.gpt-oss:20b.ctx131072.device_mib, bench.gpt-oss:20b.ctx131072.host_model_mib, bench.gpt-oss:20b.ctx131072.host_compute_mib |
fit.memory.r2 |
stale | 2026-09-30 | derived from stale input(s): bench.gpt-oss:20b.ctx4096.device_mib, bench.gpt-oss:20b.ctx4096.host_model_mib, bench.gpt-oss:20b.ctx4096.host_compute_mib, bench.gpt-oss:20b.ctx8192.device_mib, bench.gpt-oss:20b.ctx8192.host_model_mib, bench.gpt-oss:20b.ctx8192.host_compute_mib, bench.gpt-oss:20b.ctx32768.device_mib, bench.gpt-oss:20b.ctx32768.host_model_mib, bench.gpt-oss:20b.ctx32768.host_compute_mib, bench.gpt-oss:20b.ctx131072.device_mib, bench.gpt-oss:20b.ctx131072.host_model_mib, bench.gpt-oss:20b.ctx131072.host_compute_mib |
gpu.discrete_vram_gb |
stale | 2026-09-30 | derived from stale input(s): bench.gpt-oss:20b.ctx32768.device_mib |
gpu.metal_limit_16gb_mib |
stale | 2026-09-30 | derived from stale input(s): gpu.working_set_limit_mib |
gpu.over_16gb.ctx32768_mib |
stale | 2026-09-30 | derived from stale input(s): bench.gpt-oss:20b.ctx32768.device_mib, gpu.metal_limit_16gb_mib |
gpu.over_16gb.ctx8192_mib |
stale | 2026-09-30 | derived from stale input(s): bench.gpt-oss:20b.ctx8192.device_mib, gpu.metal_limit_16gb_mib |
gpu.working_set_limit_mib |
stale | 2026-09-30 | no working-set line in the log |
linux.arch.aarch64.disk.minimum_gb |
skipped | - | missing input(s): linux.arch.aarch64.disk.minimum_need_gb |
linux.arch.aarch64.disk.minimum_need_gb |
skipped | - | missing input(s): linux.arch.aarch64.pkg.base.bytes, linux.arch.aarch64.pkg.postgres.bytes, linux.arch.aarch64.install.vibey-engine.venv_bytes, linux.arch.aarch64.install.vibey-engine.uv_cache_bytes |
linux.arch.aarch64.disk.recommended_gb |
skipped | - | missing input(s): linux.arch.aarch64.disk.recommended_need_gb |
linux.arch.aarch64.disk.recommended_need_gb |
skipped | - | missing input(s): linux.arch.aarch64.disk.minimum_need_gb, linux.arch.aarch64.pkg.desktop.bytes, linux.arch.aarch64.install.krypton-app.venv_bytes |
linux.arch.aarch64.disk.with_ledger_gb |
skipped | - | missing input(s): linux.arch.aarch64.disk.minimum_need_gb, disk.ledger_growth_gb |
linux.arch.aarch64.floor.desktop |
skipped | - | missing input(s): linux.arch.aarch64.packaged.glib, linux.arch.aarch64.packaged.json_glib, linux.arch.aarch64.packaged.gtk4, linux.arch.aarch64.packaged.libadwaita |
linux.arch.aarch64.floor.glibc |
skipped | - | missing input(s): linux.arch.aarch64.glibc, linux.arch.aarch64.install.glibc_floor |
linux.arch.aarch64.floor.postgres |
skipped | - | missing input(s): linux.arch.aarch64.packaged.postgres |
linux.arch.aarch64.floor.python |
skipped | - | missing input(s): linux.arch.aarch64.packaged.python |
linux.arch.aarch64.glibc |
skipped | - | no official Arch Linux image for arm64: archlinux:latest is amd64 only, and Arch Linux ARM is a separate project, not Arch |
linux.arch.aarch64.install.glibc_floor |
skipped | - | no official Arch Linux image for arm64: archlinux:latest is amd64 only, and Arch Linux ARM is a separate project, not Arch |
linux.arch.aarch64.install.krypton-app.cold_s |
skipped | - | no official Arch Linux image for arm64: archlinux:latest is amd64 only, and Arch Linux ARM is a separate project, not Arch |
linux.arch.aarch64.install.krypton-app.download_bytes |
skipped | - | no official Arch Linux image for arm64: archlinux:latest is amd64 only, and Arch Linux ARM is a separate project, not Arch |
linux.arch.aarch64.install.krypton-app.packages |
skipped | - | no official Arch Linux image for arm64: archlinux:latest is amd64 only, and Arch Linux ARM is a separate project, not Arch |
linux.arch.aarch64.install.krypton-app.uv_cache_bytes |
skipped | - | no official Arch Linux image for arm64: archlinux:latest is amd64 only, and Arch Linux ARM is a separate project, not Arch |
linux.arch.aarch64.install.krypton-app.venv_bytes |
skipped | - | no official Arch Linux image for arm64: archlinux:latest is amd64 only, and Arch Linux ARM is a separate project, not Arch |
linux.arch.aarch64.install.vibey-engine.cold_s |
skipped | - | no official Arch Linux image for arm64: archlinux:latest is amd64 only, and Arch Linux ARM is a separate project, not Arch |
linux.arch.aarch64.install.vibey-engine.download_bytes |
skipped | - | no official Arch Linux image for arm64: archlinux:latest is amd64 only, and Arch Linux ARM is a separate project, not Arch |
linux.arch.aarch64.install.vibey-engine.packages |
skipped | - | no official Arch Linux image for arm64: archlinux:latest is amd64 only, and Arch Linux ARM is a separate project, not Arch |
linux.arch.aarch64.install.vibey-engine.uv_cache_bytes |
skipped | - | no official Arch Linux image for arm64: archlinux:latest is amd64 only, and Arch Linux ARM is a separate project, not Arch |
linux.arch.aarch64.install.vibey-engine.venv_bytes |
skipped | - | no official Arch Linux image for arm64: archlinux:latest is amd64 only, and Arch Linux ARM is a separate project, not Arch |
linux.arch.aarch64.os_release |
skipped | - | no official Arch Linux image for arm64: archlinux:latest is amd64 only, and Arch Linux ARM is a separate project, not Arch |
linux.arch.aarch64.packaged.glib |
skipped | - | no official Arch Linux image for arm64: archlinux:latest is amd64 only, and Arch Linux ARM is a separate project, not Arch |
linux.arch.aarch64.packaged.gtk4 |
skipped | - | no official Arch Linux image for arm64: archlinux:latest is amd64 only, and Arch Linux ARM is a separate project, not Arch |
linux.arch.aarch64.packaged.json_glib |
skipped | - | no official Arch Linux image for arm64: archlinux:latest is amd64 only, and Arch Linux ARM is a separate project, not Arch |
linux.arch.aarch64.packaged.kernel |
skipped | - | no official Arch Linux image for arm64: archlinux:latest is amd64 only, and Arch Linux ARM is a separate project, not Arch |
linux.arch.aarch64.packaged.libadwaita |
skipped | - | no official Arch Linux image for arm64: archlinux:latest is amd64 only, and Arch Linux ARM is a separate project, not Arch |
linux.arch.aarch64.packaged.postgres |
skipped | - | no official Arch Linux image for arm64: archlinux:latest is amd64 only, and Arch Linux ARM is a separate project, not Arch |
linux.arch.aarch64.packaged.python |
skipped | - | no official Arch Linux image for arm64: archlinux:latest is amd64 only, and Arch Linux ARM is a separate project, not Arch |
linux.arch.aarch64.pkg.base.bytes |
skipped | - | no official Arch Linux image for arm64: archlinux:latest is amd64 only, and Arch Linux ARM is a separate project, not Arch |
linux.arch.aarch64.pkg.base.count |
skipped | - | no official Arch Linux image for arm64: archlinux:latest is amd64 only, and Arch Linux ARM is a separate project, not Arch |
linux.arch.aarch64.pkg.desktop.bytes |
skipped | - | no official Arch Linux image for arm64: archlinux:latest is amd64 only, and Arch Linux ARM is a separate project, not Arch |
linux.arch.aarch64.pkg.desktop.count |
skipped | - | no official Arch Linux image for arm64: archlinux:latest is amd64 only, and Arch Linux ARM is a separate project, not Arch |
linux.arch.aarch64.pkg.postgres.bytes |
skipped | - | no official Arch Linux image for arm64: archlinux:latest is amd64 only, and Arch Linux ARM is a separate project, not Arch |
linux.arch.aarch64.pkg.postgres.count |
skipped | - | no official Arch Linux image for arm64: archlinux:latest is amd64 only, and Arch Linux ARM is a separate project, not Arch |
linux.arch.x86_64.disk.with_ledger_gb |
skipped | - | missing input(s): disk.ledger_growth_gb |
linux.fedora.aarch64.disk.with_ledger_gb |
skipped | - | missing input(s): disk.ledger_growth_gb |
linux.fedora.x86_64.disk.with_ledger_gb |
skipped | - | missing input(s): disk.ledger_growth_gb |
linux.ram.max_context_at_probe |
stale | 2026-09-30 | derived from stale input(s): fit.memory.m0_mib, fit.memory.k_kib_per_token, ram.vibey_side_gib |
linux.ram.minimum_gb |
stale | 2026-09-30 | derived from stale input(s): linux.ram.minimum_need_gib |
linux.ram.minimum_need_gib |
stale | 2026-09-30 | derived from stale input(s): fit.memory.m0_mib, fit.memory.k_kib_per_token, ram.vibey_side_gib |
linux.ram.probe_fits_runner |
stale | 2026-09-30 | derived from stale input(s): linux.ram.max_context_at_probe |
linux.ram.recommended_gb |
stale | 2026-09-30 | derived from stale input(s): linux.ram.recommended_need_gib |
linux.ram.recommended_need_gib |
stale | 2026-09-30 | derived from stale input(s): fit.memory.m0_mib, fit.memory.k_kib_per_token, ram.vibey_side_gib |
linux.ubuntu-24.04.aarch64.disk.with_ledger_gb |
skipped | - | missing input(s): disk.ledger_growth_gb |
linux.ubuntu-24.04.x86_64.disk.with_ledger_gb |
skipped | - | missing input(s): disk.ledger_growth_gb |
linux.ubuntu-26.04.aarch64.disk.with_ledger_gb |
skipped | - | missing input(s): disk.ledger_growth_gb |
linux.ubuntu-26.04.x86_64.disk.with_ledger_gb |
skipped | - | missing input(s): disk.ledger_growth_gb |
postgres.after_one_job_bytes |
skipped | - | worker --once did not run on a scratch database |
postgres.bytes_per_job |
skipped | - | missing input(s): postgres.after_one_job_bytes |
postgres.empty_db_bytes |
stale | 2026-09-30 | $VIBEY_SPECS_PG_ADMIN_URL is not set |
postgres.migrations_applied |
stale | 2026-09-29 | $VIBEY_SPECS_PG_ADMIN_URL is not set |
postgres.server_version |
stale | 2026-09-29 | $VIBEY_SPECS_PG_ADMIN_URL is not set |
process.cli.new.max_rss_mib |
stale | 2026-09-30 | no scratch database |
process.cli.new.wall_s |
stale | 2026-09-30 | no scratch database |
process.cli.status.max_rss_mib |
stale | 2026-09-30 | no scratch database |
process.cli.status.wall_s |
stale | 2026-09-30 | no scratch database |
process.cli.version.max_rss_mib |
stale | 2026-09-30 | no scratch database |
process.cli.version.wall_s |
stale | 2026-09-30 | no scratch database |
process.cli.worker_once.max_rss_mib |
stale | 2026-09-30 | no scratch database |
process.cli.worker_once.wall_s |
stale | 2026-09-30 | no scratch database |
process.krypton.idle_rss_mib |
stale | 2026-09-30 | no scratch database |
process.krypton.startup_s |
stale | 2026-09-30 | no scratch database |
process.serve.idle_rss_mib |
stale | 2026-09-30 | no scratch database |
process.serve.startup_s |
stale | 2026-09-30 | no scratch database |
ram.16gb_verdict |
stale | 2026-09-30 | derived from stale input(s): ram.design_only_need_gib |
ram.design_only_need_gib |
stale | 2026-09-30 | derived from stale input(s): ram.model_process.ctx8192, ram.vibey_side_gib |
ram.kv_per_token_kib |
stale | 2026-09-30 | derived from stale input(s): bench.gpt-oss:20b.ctx131072.kv_mib, bench.gpt-oss:20b.ctx4096.kv_mib |
ram.minimum_gb |
stale | 2026-09-30 | derived from stale input(s): ram.minimum_need_gib |
ram.minimum_need_gib |
stale | 2026-09-30 | derived from stale input(s): ram.model_process.ctx32768, ram.vibey_side_gib |
ram.model_process.ctx131072 |
stale | 2026-09-30 | derived from stale input(s): bench.gpt-oss:20b.ctx131072.device_mib, bench.gpt-oss:20b.ctx131072.host_model_mib, bench.gpt-oss:20b.ctx131072.host_compute_mib |
ram.model_process.ctx32768 |
stale | 2026-09-30 | derived from stale input(s): bench.gpt-oss:20b.ctx32768.device_mib, bench.gpt-oss:20b.ctx32768.host_model_mib, bench.gpt-oss:20b.ctx32768.host_compute_mib |
ram.model_process.ctx8192 |
stale | 2026-09-30 | derived from stale input(s): bench.gpt-oss:20b.ctx8192.device_mib, bench.gpt-oss:20b.ctx8192.host_model_mib, bench.gpt-oss:20b.ctx8192.host_compute_mib |
ram.recommended_gb |
stale | 2026-09-30 | derived from stale input(s): ram.recommended_need_gib |
ram.recommended_need_gib |
stale | 2026-09-30 | derived from stale input(s): ram.model_process.ctx131072, ram.vibey_side_gib |
ram.vibey_side_gib |
stale | 2026-09-30 | derived from stale input(s): process.cli.worker_once.max_rss_mib, process.serve.idle_rss_mib |
Not verified¶
- Windows hosts: nothing was measured.
- Linux as an installed system: every Linux cell ran in a container, so a distribution's own kernel, idle memory and desktop session were not measured; Linux memory uses the OS allowance assumed from the Mac (3.5 GiB).
- Linux on x86_64 at the seed: measured under QEMU emulation on the Apple M5, so only sizes and versions. uv itself crashed under QEMU (signal 11), so every x86_64 cell's install sizes and glibc floor, and every x86_64 timing, wait for the native CI runner.
- Arch Linux on arm64: there is no official image (archlinux:latest is amd64 only; Arch Linux ARM is a separate project).
- The model's memory on Linux: the line M(c) = M0 + k*c was fitted on the Mac's Metal backend; the weights and KV cache are the same bytes on Linux, but its CPU and CUDA compute buffers were not measured. The Linux memory figures assume no discrete GPU (the whole model in system RAM).
- CPU scaling (Amdahl's law): the thread sweep had not run at the seed (the host was busy with another client's model), and no x86_64 host with the model runs it, so the core counts are not measured on either architecture.
- The ledger's bytes per job: not measured yet (the next weekly run measures it); one job's growth is page-granular, so the measurement will be an upper bound.
- Ollama's Linux bundle: its unpacked size is the bytes of its tar stream (contents plus headers), not an installed tree.
- Other hardware: 16 GB and 32/64 GB Macs, other Apple chips, x86 CPUs, NVIDIA/AMD discrete GPUs. The 16 GB verdict and the discrete-GPU size are arithmetic.
- The macOS GPU working-set fraction on a 16 GB Mac: taken as this host's ratio (widely reported as about 2/3 there).
- CPU only with the weights held in memory: the measured CPU rates ran under memory pressure (weights paged in), so they are a lower bound.
- The minimum CPU core count: nothing below this 10-core M5 was tested.
- OS headroom on a headless or server install: 3.5 GiB is an assumption from this desktop Mac.
- A real gptossloop BUILD session end to end: runner turns are bounded by arithmetic from the depth test.
- Concurrent lanes: one request at a time was measured (ADR-0058 measured two slots on this host).
- PostgreSQL 14-17 and git older than 2.54: only PostgreSQL 18.4 and git 2.54 ran; the floors are declared.
- Python 3.12.0-3.12.12: only 3.12.13 ran. Python 3.15 does not install yet (no pydantic-core wheel).
- pip and pipx install paths: only uv was used.
- The krypton desktop GUI, the VS Code extension inside VS Code, and the React Native app at runtime; iOS and Android native builds.
- The desktop client's minimum library versions: only the installed versions were built.
- Paid engines end to end, the RabbitMQ bus, and the opt-in integrations (Azure, the operator, webhooks, SMTP).
- The LAN hub on a real network interface (mDNS multicast was not observed).
Reproducing and changing the figures¶
python scripts/minimum_specs.py measure # probe this host; merge into the record (long)
python scripts/minimum_specs.py derive # recompute the derived figures from the record
python scripts/minimum_specs.py render # rewrite this page's and the paper's tables
python scripts/minimum_specs.py check # CI: exit 1 if either is out of step
python scripts/minimum_specs.py cells # the Linux matrix the configuration declares
python scripts/minimum_specs.py cell --distro fedora --arch aarch64 --out fedora-aarch64.json
python scripts/minimum_specs.py merge fedora-aarch64.json # fold a cell into the record
A cell needs Docker (or a compatible docker CLI, [minimum_specs.linux] docker) and the
network, to pull the image, the distribution's packages and uv. The cell for
ollama_bundle_distro also streams Ollama's Linux bundle through zstd on the host.
To change an assumption, a threshold or a context size, edit scripts/minimum_specs.toml,
then run derive and render. check runs in CI through
tests/meta/test_minimum_specs.py. It fails when a derived figure disagrees with its inputs
or a generated table disagrees with the record. The measurement itself needs PostgreSQL,
reachable through the DSN named by admin_url_env, where it creates, migrates and drops a
scratch database. It also needs Ollama and uv. It never touches any other database.