Documentation

OnPremo inspects a machine and tells you which open models it can actually run; profiles and memory admission are the mechanism that enforces the answer. Reference for the CLI, device profiles, the memory admission model, optional local HTTP API (web chat + status), the macOS/Windows tray during serve, benchmark report shape, uninstall (Unix and Windows), and building from source. Published installer assets are llama-backed (see bin/FLAVORS); the mock backend remains the default for development and CI.

▶ On this page

CLI reference

Binary name: onpremo. Canonical first-run path: recommendpullrun / chat → optional serve. Commands below match the live binary; also see agent and discover in onpremo --help and docs/agents.md.

onpremo inspect <model.gguf>

Inspect GGUF metadata without loading weights.

--json emit machine-readable JSON instead of a table

onpremo inspect smollm2-135m-instruct-q4_k_m.gguf
onpremo inspect smollm2-135m-instruct-q4_k_m.gguf --json

onpremo check <model.gguf>

Test model compatibility with a device profile without loading the model.

--profile <name|path> built-in name or TOML path (default: pi-1gb-safe)
--json emit machine-readable admission outcome

Exit codes: 0 admitted or downgraded, 2 rejected, 1 for bad paths or invalid profiles.

onpremo check model.gguf --profile micro-500mb
onpremo check model.gguf --profile pi-1gb-safe --json

onpremo run <model.gguf>

One-shot inference with streaming tokens on stdout and immediate cleanup.

--profile <name|path> built-in name or TOML path; see onpremo profile suggest for a host-fit profile (default: pi-1gb-safe)
--prompt <text> required prompt string
--max-tokens <n> default: 64
--temperature <f> default: 0.7
--seed <u64> optional RNG seed for reproducible sampling
--report print machine-readable run report JSON to stderr
--chat treat --prompt as a single user chat message (model chat template, or a role-prefixed fallback)
--system <text> system message prepended when --chat is set (requires --chat)
--json-schema <path> JSON Schema file compiled to a GBNF grammar for constrained generation; output is validated after generation
--lora <path> optional LoRA adapter file (file size counted in admission)

Exit codes: 0 admitted or downgraded, 2 rejected, 3 schema violation (with --json-schema), 1 other errors (bad paths, invalid profiles, load failures).

onpremo run model.gguf \
  --profile pi-1gb-safe \
  --prompt "Return the intent as JSON" \
  --max-tokens 32

onpremo run model.gguf \
  --profile pi-1gb-safe \
  --prompt "hello world" \
  --max-tokens 4 \
  --report 2>report.json

onpremo run model.gguf \
  --profile pi-1gb-safe \
  --chat --system "Reply in one sentence" \
  --prompt "What is admission?" \
  --json-schema schema.json

onpremo chat <model.gguf>

Interactive multi-turn terminal chat. Loads the model once; streams each turn. In-REPL: /exit quits, /clear resets history (keeps the system message). EOF / Ctrl-D also exits cleanly.

--profile <name|path> default: pi-1gb-safe (Pi certification identity; on a laptop prefer the profile recommend printed)
--system <text> optional system message kept across /clear; with --agent, appended after the manifest system
--agent <path> agent manifest TOML (master prompt + tool registry). Tool calls print for the operator to type a result at tool result>, except the implicit escalate tool which is auto-executed
--max-tokens <n> per turn (overrides agent default; default 128)
--temperature <f> overrides agent default; default 0.7
--seed <u64> optional
--lora <path> optional LoRA adapter (overrides manifest lora = "…")

onpremo chat model.gguf --profile pi-1gb-safe
onpremo chat model.gguf --profile pi-1gb-safe --system "Be brief"
onpremo chat model.gguf --agent examples/agents/workshop-gateway.toml

onpremo benchmark <model.gguf>

Repeatable workload; writes a machine-readable JSON report (schema version 1).

--profile <name|path> default: pi-1gb-safe
--context <n> optional context override
--batch <n> optional batch override
--max-tokens <n> default: 64
--iterations <n> default: 3
--warmup <n> default: 1
--prompt <text> default: Summarize: the pump pressure is low.
--output <path> write JSON here (default: stdout)

onpremo benchmark model.gguf \
  --profile pi-1gb-safe \
  --context 512 \
  --batch 16 \
  --max-tokens 64 \
  --iterations 3 \
  --warmup 1 \
  --output result.json

onpremo serve <model.gguf>

Bounded local HTTP API. Memory budget is owned by profiles + admission (there is no free-form --memory-limit flag). Binds loopback by default. On start, serve prints both GUI URLs on stderr:

onpremo serve: listening on 127.0.0.1:11500 (profile=…, backend=…)
  chat:   http://127.0.0.1:11500/
  status: http://127.0.0.1:11500/status

Web chat at / is the primary GUI (embedded browser UI). /status is the secondary operator page (model, admitted config, live memory). Both are always printed so discovery does not require reading docs.

--profile <name|path> default: pi-1gb-safe
--bind <addr> default: 127.0.0.1:11500
--idle-unload <secs> unload after idle seconds; 0 disables (default: 30)
--max-request-bytes <n> hard body ceiling (default: 65536)
--agent <path> agent manifest applied to every conversation
--lora <path> refused: the server load path does not apply LoRA in this build (use run/chat)
--no-tray skip the menu bar / system tray icon (macOS, Windows, and Linux builds made with --features tray-linux; board builds have no icon to skip)

onpremo serve model.gguf \
  --profile pi-1gb-safe \
  --bind 127.0.0.1:11500

Menu bar / system tray (built-in on macOS and Windows; Linux via opt-in tray-linux): while serve runs, a mascot icon appears in the menu bar (macOS), notification area (Windows), or StatusNotifier tray (Linux when compiled in). Menu items, in order: model label (disabled), Open chat (opens /), Open status page (opens /status), Quit OnPremo. The icon is rasterized at startup from the same 19×19 mascot grid as site/mascot.svg — no image assets. The mark is black-and-white and matches the bar it sits on: macOS registers it as a template image so the menu bar recolors it per appearance, and Windows reads the taskbar theme at startup and picks the black or white variant accordingly.

Desktop Linux gets the same menu when the binary is built with the opt-in tray-linux cargo feature: a StatusNotifierItem over the session D-Bus (pure Rust, no GTK), shown out of the box on Ubuntu, KDE Plasma, and XFCE; stock Debian GNOME needs the AppIndicator shell extension. Default Linux builds — including every published board binary — omit the tray so the certified musl/armv7 images stay lean; use the printed URLs (or onpremo status) instead. Headless SSH sessions: pass --no-tray. If the icon cannot be created (no window server, no D-Bus), serve falls back to a plain foreground process with a warning.

Non-loopback binds are refused unless ONPREMO_ALLOW_NONLOCAL=1 is set. When allowed, serve prints a one-time startup warning: the API has no authentication.

onpremo pull [NAME]

Download a model via the system curl (falling back to wget): a catalogue name, or any GGUF by URL with --url. The OnPremo binary links zero network crates; only this convenience command uses the network, and only by shelling out — same approach as the installer. Inference itself never needs the network.

NAME catalogue model name (case-insensitive exact or unique substring); with --url, optionally overrides the saved filename (must end in .gguf)
--list list catalogue entries (name, quantization, size, license, status, verified)
--dir <path> destination (default: $ONPREMO_MODELS_DIR or ~/.onpremo/models)
--catalogue <dir> TOML manifests (default: embedded catalogue)
--allow-unverified allow download when the catalogue entry has no SHA-256 pin
--url <URL> direct URL to a .gguf (bring your own model); filename derived from the URL
--sha256 <hex> with --url: verify a fresh download, or verify an already-downloaded file in place without re-fetching

Bring your own model. With --url the verification story inverts: no pin exists until you create one, so the first pull is unverified and the computed SHA-256 is printed prominently — pin later pulls (and CI installs) with --sha256. Split multi-part GGUFs (-00001-of-000NN) are refused: download all parts into one directory, then point onpremo run at the -00001-of- file (llama.cpp loads the rest). And pull is a convenience, not a gate — every model-consuming command accepts a plain path to any local .gguf, and recommend ranks uncatalogued files in the models directory alongside catalogue entries.

pull --list prints a VERIFIED column: sha256 for pinned entries, or UNVERIFIED for unpinned ones. A footer reports how many entries are SHA-256-pinned. onpremo pull <name> refuses unverified entries unless --allow-unverified is passed; after an unverified fetch it prints the observed hash and size so you can pin them in the manifest. Size column uses exact size_bytes when known, else admission approx_weight_bytes.

onpremo pull --list
onpremo pull SmolLM2-135M
onpremo pull some-unpinned-model --allow-unverified
onpremo pull --url https://huggingface.co/<repo>/resolve/main/<model>.gguf
onpremo pull --url <same-url> --sha256 <printed-hash>   # pin: verifies in place

onpremo recommend

Probe live memory and rank catalogue / local models by admission against a profile. When --profile is omitted, the profile is derived with the same pure suggestion math as profile suggest.

--profile <name|path> built-in name or TOML path; omit to auto-suggest from the host
--catalogue <dir> catalogue TOML directory (default: embedded)
--models-dir <dir> local *.gguf directory (default: $ONPREMO_MODELS_DIR or ~/.onpremo/models)
--json stable JSON array of recommendation rows

Ranking (first-experience default). Candidates order by admission verdict first: admitted → downgraded → rejected → no-specs. Within admitted (and within downgraded):

  1. Already downloaded locally (a real .gguf beats a pull)
  2. Catalogue-verified (non-empty SHA-256) before unverified
  3. Smaller weight first
  4. Name ascending as a stable tie-break

Unverified multi-GB catalogue entries therefore sink toward the bottom of the admitted band even when they fit. Human output prints up to two headlines when they differ:

  • headline: best first pick = … — top of the first-experience order (safe default next step)
  • headline: largest admitted = … — heaviest admitted model (capability signal; omitted when it is the same row)

Every run action line carries an explicit --profile <name-or-path> matching the profile used for admission. When recommend auto-suggested the profile (no --profile flag), suggested names are not loadable as built-ins, so actions are a two-step form: onpremo profile suggest --out <path> then run … --profile <path>. Pull actions stay onpremo pull <name> (pull does not take a profile).

Exit codes: 0 if any candidate is admitted or downgraded, 3 if nothing fits. run/chat/serve still default to pi-1gb-safe when you type them by hand — that is the Pi certification identity; prefer the profile recommend printed.

onpremo recommend
onpremo recommend --profile pi-1gb-balanced
onpremo recommend --json

onpremo profile suggest

Probe host memory and emit an honest hardware profile whose process budget, context/batch tiers, and safety margin scale with actual RAM (and a tighter cgroup limit when present). The three fixed built-ins cover the certified tiny tier; this command is the first-class path for every other host.

--name <n> default: suggested-<total-GiB-rounded>gb
--out <path> write the profile here instead of stdout; refuses to overwrite unless --force
--force allow replacing an existing --out file
--json emit the same Profile as JSON
--streaming include a [streaming] table when this build's Profile supports it

Default stdout is TOML with documented budget-math comments. Unprobed platforms (total_bytes == 0) print an error and exit 1. Suggested TOML loads with --profile <path> on check / run / chat / serve / agent like any other profile.

onpremo profile suggest
onpremo profile suggest --out ~/.onpremo/profiles/suggested-16gb.toml
onpremo profile suggest --json

onpremo status

Scripting twin of the embedded /status page. Fetches GET /v1/status and, when present, GET /v1/agent via system curl/wget (no network crates). Requires a running onpremo serve.

--url <base> server base URL, no trailing path (default: http://127.0.0.1:11500)
--json one merged JSON object (status + optional agent) on a single stdout line

Human output is a compact table. Exit 0 when healthy, 1 when unreachable or malformed.

onpremo status
onpremo status --json
onpremo status --url http://127.0.0.1:11500

Profiles

Built-in device profiles ship as TOML under profiles/; the three fixed tiny profiles below are the certified tier. For any other machine, onpremo profile suggest probes host memory and emits a profile scaled to it. Pass a name (pi-1gb-safe) or a path to a custom TOML file.

Name Process budget OS reserve Default context / batch KV types Max out Notes
micro-500mb 300 MiB
(314572800)
200 MiB 384 / 16
(256–512 / 8–16)
f16, q8_0, q4_0 64 ~500 MB total RAM or 300 MB process budget; 135M Q4 and smaller; require_no_swap
pi-1gb-safe 600 MiB
(629145600)
400 MiB 512 / 16
(512–1024 / 16–32)
f16, q8_0 128 1 GB total RAM, conservative; 135M–360M Q4; CLI default
pi-1gb-balanced 750 MiB
(786432000)
250 MiB 512 / 16
(256–1024 / 16–32)
f16, q8_0, q4_0 192 Minimal OS reserve; measured headroom for selected 0.5B models

Safety margin ratios: micro-500mb 0.15, pi-1gb-safe 0.10, pi-1gb-balanced 0.08.

Memory model summary

Memory management is the central product feature. The runtime distinguishes model file size from peak process memory. Required memory is estimated as:

Required memory =
    model residency
  + KV cache
  + compute buffers
  + tokenizer/runtime overhead
  + operating-system reserve
  + safety margin

The process budget (weights + KV + compute + overhead + margin) is distinct from the OS reserve. Admission enforces the process ceiling; the OS reserve is reported for device-level capacity.

Admission flow

  1. Read the device profile and current available memory
  2. Inspect model metadata and quantization without fully loading
  3. Estimate a conservative initial configuration
  4. If needed, reduce batch, context, and KV precision in a fixed order
  5. Reserve OS and emergency margin
  6. Load only after the estimate passes
  7. Monitor RSS and terminate cleanly if the hard process budget is approached
  8. Record measured peak memory vs. the estimate

Downgrade order

  1. Start with the requested profile
  2. Reduce prompt batch size
  3. Reduce context length
  4. Select a lower-memory KV-cache type
  5. Reduce maximum output tokens
  6. Reject with a machine-readable reason code if still incompatible
Rejected with a reason code beats an OOM kill. The OS OOM killer is never treated as normal control flow. Authoritative certification metric: cgroup v2 memory.peak / memory.current when a cgroup limit applies; process RSS is a secondary field.

Measured footprint

Idle process RSS and release binary sizes are measured and labeled honestly (development-host aarch64 macOS figures; Pi / musl board certification still pending) in docs/footprint.md — re-run with scripts/measure-footprint.sh. Those numbers are not a substitute for admission math or cgroup peak under load.

HTTP API

Bounded local interface served by onpremo serve. One request at a time, request-size ceiling, optional idle unload, loopback by default. On start the process prints chat: (GET /, primary GUI) and status: (GET /status, secondary operator page).

Method Path Description
POST /v1/generate JSON body GenerateRequestGenerateResponse (text + stats). Fields: prompt (required), optional max_tokens, temperature, top_p, seed, stop. max_tokens is clamped to the admitted config.
POST /v1/chat/completions OpenAI-compatible chat (optional stream). Used by the embedded web chat and peer escalation.
GET /v1/health Liveness: {"status":"ok","model_loaded":bool}
GET /v1/model Model GGUF summary and active RunConfig
GET /v1/models OpenAI-compatible model list
GET /v1/status Live status: model, config, model_loaded, requests_served, memory (current / peak RSS vs process budget)
GET /v1/agent Loaded agent registry (name, system, tools). 404 JSON when no manifest. Never includes executor wiring.
GET / Primary GUI — embedded web chat
GET /status Secondary status page (mirrors /v1/status + agent)
curl -sS http://127.0.0.1:11500/v1/generate \
  -H 'content-type: application/json' \
  -d '{"prompt":"Summarize: the pump pressure is low.","max_tokens":64}'

# Scripting twin of the status page
onpremo status --json
curl -sS http://127.0.0.1:11500/v1/status

Surfaces: CLI, API, UI, tray

Four surfaces, one capability contract — CLI, HTTP API, embedded web UI, and (where compiled in) the desktop tray are thin views over the same engine. Operating-loop capabilities have full CLI/API/UI parity; provisioning (pull, discover, benchmark, recommend) is CLI-first by design: a LAN-facing gateway page must not mutate the machine. The tray is built-in on macOS and Windows during onpremo serve; Linux gets it only via the opt-in tray-linux feature so default board and desktop images stay lean.

Capability CLI API UI Tray (built-in macOS/Windows; Linux via opt-in tray-linux)
Chat onpremo chat POST /v1/chat/completions GET / (primary GUI) Open chat → browser /
Tool calls + confirmation chat --agent, agent step/loop OpenAI tools on chat (no exec) Web chat tool-call bubble —(by design)
Model / config inspect, check, status GET /v1/model, /v1/status /status model + config Open status page → /status
Live memory onpremo status GET /v1/statusmemory /status memory meter via Open status page
Agent registry agent validate, --agent GET /v1/agent /status agent section —(by design)
Structured one-shot run --json-schema response_format on chat —(by design) —(by design)
Pull / discover / benchmark pull, discover, benchmark —(by design) —(by design) —(by design)
Process lifecycle Ctrl-C / process exit —(by design) —(by design) Quit OnPremo (built-in macOS/Windows; Linux via opt-in tray-linux)

Benchmark report fields

Schema version 1. Every compatibility claim should be reproducible from a documented command. Top-level JSON object:

{
  "schema_version": 1,
  "device": { "...": "DeviceInfo" },
  "model": { "...": "ModelSummary" },
  "backend": "mock",
  "profile_name": "micro-500mb",
  "admission": { "decision": "admitted", "...": "..." },
  "config": { "...": "RunConfig or null" },
  "prompt_chars": 42,
  "iterations": [ { "...": "IterationResult" } ],
  "aggregate": { "...": "Aggregate or null" }
}

Field groups

Group Contents
Device hostname, os, kernel, arch, cpu_model, total_memory_bytes, runtime_version
Model GGUF summary: name, architecture, quantization, file/tensor sizes, layer/head/context/vocab counts
Admission admitted | downgraded (with steps) | rejected (reason + detail). Rejected runs leave config/aggregate null and iterations empty.
Config context, batch, kv_cache_type, max_output_tokens, threads
Per iteration GenerationStats (prompt/generated tokens, load/TTFT/total ms, tokens/s), RssReport (start/peak/end RSS, optional cgroup peak), swap before/after, completed
Aggregate Mean/min/max generation tokens/s, mean prompt tokens/s, mean load and first-token ms, max peak RSS, max swap delta

Certification expects repeatable completion without swap use or OOM termination, peak memory under the declared process budget with a documented safety margin, and disclosure of thermal throttling.

Uninstall

Default install prefix is $HOME/.onpremo on Unix and %USERPROFILE%\.onpremo on Windows (override with ONPREMO_PREFIX). Uninstallers remove the binary and PATH entry; they do not stop services (none are installed). Models under the prefix are left alone unless you delete the whole prefix when prompted.

Linux / macOS

curl -fsSL https://onpremo.org/uninstall.sh | sh

Non-interactive: set ONPREMO_UNINSTALL=1 and/or ONPREMO_NONINTERACTIVE=1 as documented by the script. Manual removal: delete $HOME/.onpremo/bin/onpremo and the # >>> onpremo PATH >>># <<< onpremo PATH <<< markers from your shell profile.

Windows

irm https://onpremo.org/uninstall-windows.ps1 | iex
# Or:
powershell -ExecutionPolicy Bypass -c "irm https://onpremo.org/uninstall-windows.ps1 | iex"

Same env knobs: ONPREMO_PREFIX, ONPREMO_NONINTERACTIVE=1, and ONPREMO_UNINSTALL=1 (required in non-interactive mode to proceed). Removes onpremo.exe from the prefix bin dir and the matching user PATH entry.

Build from source

Requirements: stable Rust toolchain, cmake, and a C++ toolchain when enabling the real inference backend. Linux (ARM64 / x86-64) or macOS for development.

# Mock backend (default — development and CI)
cargo build --workspace
cargo build -p onpremo-cli
# Binary: target/debug/onpremo

# Real llama.cpp backend
cargo build -p onpremo-cli --features backend-llama
# or
cargo install --path crates/onpremo-cli --features backend-llama

One-liner installer that clones and builds with the llama backend:

curl -fsSL https://onpremo.org/install-from-source.sh | sh
Do not enable backend-llama in CI; mock-backend workspace tests are the default gate. Published installer / bin-dist assets are llama-backed for all six targets (see bin/FLAVORS — every line is tagged llama) and SHA256SUMS. The mock backend is for development and CI only, not the shipped binary.