Repository navigation
Releases: mcp-tool-shop-org/offrig
Release list
v1.2.0
Changed
- The project index embeds with EmbeddingGemma 2. The default model is now
embeddinggemma-2:f32: Google DeepMind'sgoogle/embeddinggemma-2at revision
914f7f8, an open model under Apache-2.0 with no gate, text only, float32, on the CPU.
It replaces nomic-embed-text. On a pre-registered benchmark of 93 studio queries over an
11,525-chunk index it scored dense nDCG@10 +0.112 over nomic-embed-text, with a one-sided
95% lower bound of +0.065 (code +0.174, prose +0.041). The 256-dimension cut lost 0.043,
so vectors stay at 768. The record is in the rnd library,experiments/embeddinggemma-2/. - Documents are sent with
title: none | text:and queries with
task: search result | query:. The code-retrieval prefix scored no better on code
queries (−0.008), so one query prefix serves every kind.
Added
tools/embed-serve, the CPU embed server. Ollama on Windows can't run EmbeddingGemma 2,
so this small loopback server answers the part of Ollama's API that offrig's embed client
uses, on the same defaultembed_url(http://127.0.0.1:11490).- It serves one name and answers 404 for any other, so no store can silently mix models.
- It refuses to start when torch can see CUDA, and
/api/psreportssize_vram: 0. - It encodes in sub-batches of 8.
- It's built for a shared machine, after an outside review:
- every thread pool (torch, OpenMP, MKL, OpenBLAS, the tokenizers) is capped before torch loads, by default to a quarter of the logical cores and at most 6 (
--threads); - it runs at below-normal priority, single-threaded, one request at a time;
- it bounds each request: 4 MiB, 256 texts, 12,000 characters a text, 2,048 tokens;
- it refuses browser requests, non-loopback hosts, chunked or non-JSON bodies and a bad
Content-Length.
- every thread pool (torch, OpenMP, MKL, OpenBLAS, the tokenizers) is capped before torch loads, by default to a quarter of the logical cores and at most 6 (
- Measured on a Core Ultra 9 285K with 6 threads on 6 efficiency cores: a 201-chunk rebuild took 163 s, the server used about 20% of the machine (never more than 22%), and peaked at 2.8 GiB. Its vectors match the benchmark's (cosine at least 0.9999999).
- Python 3.12.
requirements.txtpins every package exactly, with torch's CPU build. - Its tests use a stub encoder and run in CI's new
embed-servejob, gated at 90% line
coverage.
- Atlas:
tools/embed-serveis its own part.
Upgrading: what a nomic-indexed store does
A project index built with nomic-embed-text keeps its vectors, and nothing mixes them with
the new model's:
offrig indexis refused: "the project index was built with nomic-embed-text, not
embeddinggemma-2:f32; runoffrig index --rebuild". It writes nothing.- Hybrid search, the default once an index exists, embeds the query with the index's
own model. The new server doesn't serve that model, so the search is refused, never
downgraded. The refusal namesoffrig index --rebuildandmode=keyword. - The side-car's background embed leaves new records keyword-only until the rebuild.
Re-embed each such project once, with embed-serve running:
offrig index --rebuild --project <dir>. Chunks are kept, and only vectors are replaced. A
project with no vectors yet needs nothing.
The refusal and start-line messages name embed-serve for the default model, and a CPU-only
Ollama for a model chosen with --model.
v1.1.3
Fixed
- The embed server cannot take the GPU without offrig refusing. On Ollama 0.40 a server started with only
CUDA_VISIBLE_DEVICES=-1still reaches an NVIDIA card through Vulkan, so the documented start line could load the embedding model onto it. The start line in the refusal message, the handbook and the design doc now also setsGGML_VK_VISIBLE_DEVICES=-1 OLLAMA_VULKAN=0 OLLAMA_IGPU_ENABLE=0. After each embedding call offrig reads the embed server's/api/ps; if the model sits in GPU memory it refuses, shows the start line and writes nothing. A server that does not answer/api/psis not blocked.
v1.1.2
- Build fake credentials at runtime in
trace.rsunit tests so secret scanners do not flag test fixtures.
offrig 1.1.1
Draft. Windows x64 only. Verify the zip against SHA256SUMS before use; see CHANGELOG.md for what changed.
offrig 1.1.0
Draft. Windows x64 only. Verify the zip against SHA256SUMS before use; see CHANGELOG.md for what changed.
offrig 1.0.0
The first release. offrig has run real work since 2026-10-02, and since 2026-10-07 two
projects have used it at once, each in its own lane: training runs on job pods and
singing renders on jam pods. This release adds downloadable Windows binaries, structured
errors and exit codes, and a handbook. Everything below the next list was built before the
first release and ships in it.
-
Windows release binaries. Each version tag builds
offrig.exe,offrig-mcp.exeand
offrig-app.exein CI intooffrig-<version>-windows-x64.zip, withSHA256SUMS. The
workflow stages a draft release and never publishes it; a person checks the draft first.
Windows only, because offrig relies on Windows' OpenSSH,%APPDATA%and Zed's paths. -
Side-car errors carry a code. Every tool failure, including bad arguments and an
unknown tool, returnsok:false, a stablecode, theerrortext, anext_actionand
retryable.errorandnext_actionare unchanged, so existing callers keep working.
Budget refusals have their own code,budget_exceeded. -
CLI exit codes:
0success,1something to fix on your side,2a runtime
failure. Bad arguments now exit1instead of clap's2. -
CLI log levels:
-q(errors and a command's own results only),-v(each RunPod
call and its timing, on stderr) and--debug(failed response bodies and full error
chains). The API key and anyBearertoken are redacted at every level, and a panic
prints one line unless--debugis set. -
offrig-mcp --helpand--versionanswer instead of starting to serve stdio. -
The app has its icon, in the window and on the
.exe. -
scripts/verify.shandscripts/verify.ps1run the format check, clippy, the tests
and a smoke run of each binary in one command. -
Every project can see the others' pods.
offrig_statuslists each other offrig
pod with its lane, project, GPU, price and status, and the open plan behind it: plan id,
note, deadline and committed worst case. The other project's store is read strictly
read-only, leaving no files behind. Pods offrig did not create are only counted and
named. Pods carryOFFRIG_LANE, and on side-car launchesOFFRIG_PLANand
OFFRIG_DEADLINE, as environment variables visible in RunPod's console. -
A failed launch says what it cost.
offrig_jobgives the failure'scode,
retryable, and whether a pod was rented and billed. The new codepod_not_readymeans a
pod was rented but never became ready, and was terminated;no_capacitymeans nothing was
rented.rentedis filled as soon as the pod exists. Ajob_idthat is really a plan id
gets a message naming the right call. -
Turning auto-stop off now survives a restart. It used to be saved as a missing key,
which reloads as the 30-minute default. It is now saved asauto_stop_idle_minutes = "off"; numbers, including0, are still minutes. -
Coverage of at least 90% of lines, enforced. The tests grew from about 250 to more
than 450 and cover 94% of lines. CI fails below 90% (cargo llvm-cov --fail-under-lines 90), andcodecov.ymlsets 90% targets for the project and for each change. -
CI adds an OSV scan of
Cargo.lock, and uploads coverage to Codecov over OIDC. -
A handbook and landing page at https://mcp-tool-shop-org.github.io/offrig/.
-
Launch follow-ups from the first real runs of the hardware-limits build (issue #15).
- A narrowed plan waits for capacity instead of failing.
offrig_plantakes
wait_minutes(stored with the plan, shown in the reply;0fails at once), else the
profile'swait_for_gpu_minutes, and thejobprofile now defaults to 20 minutes.
Nothing is rented while waiting; the wait is cut to the plan's remaining time less a
five-minute reserve, so it stays inside the plan's deadline and adds nothing to its
worst case. Each retry is reported:offrig_jobshowsprogress.capacity_waitand a
step naming the check. Theno capacityerror now says how to set a wait. offrig_jobderives the step of a booting pod from the pod's state on each call (what
RunPod reports, and whether sshd answers on the published port), instead of reading the
launch thread's last event, which could be minutes stale on a slow host. The thread's
own last step is kept beside it asprogress.launch_step.- The host's CUDA version is measured: after ssh is up the launch runs
nvidia-smionce
through the lane, readsCUDA Version: 12.8or the newerCUDA UMD Version: 13.4, and
fillsrented.cuda_version(rented.cuda_sourcesaysnvidia-smi). A host below the
plan's floor keeps the loudwarningsentry; nothing is terminated. Ifnvidia-smi
gives nothing,rented.notessays the floor is unchecked.
- A narrowed plan waits for capacity instead of failing.
-
Job-tool gaps from real runs (issue #12).
offrig_getcreates missing local parent folders instead of refusing with "is not a
directory", asoffrig_putalready does on the pod side. A path that exists but is a
file is still refused.offrig_exec action=runruns a short command synchronously with a timeout (default
30 s, at most 120 s) and returnsstdout,stderr,exit_codeandtimed_out. The
command is sent as base64 and run undertimeoutwith stdin closed; output is capped
at 64 KB per stream.nameis now optional (only start, status and stop need it).offrig_exec action=statuscollapses carriage-return progress lines in its log tail to
their last frame, and takessave_logto copy the job's whole log to a local file.
-
Lane names in the plan, stale ssh blocks, and a container disk size (issue #6 and a new
need from a slow-/workspacehost).offrig_planshows the lane'sssh_aliasand thepod_nameits launch will create
(for exampleoffrig-ai-jam-sessionsandoffrig-ai-jam-sessions-jam), so a session
can confirm its lane before launching without readinglanes.toml.offrig_status
showsssh_aliason each open plan too.offrig_shutdownremoves the lane's own~/.ssh/configblock once the pod is gone, but
only when the block's label names that plan's pod. Another lane's block, and a block
already rewritten for a newer pod, are never touched. A launch that fails and terminates
its pod does the same. The reply reportsssh_block_removed. A pod ended by the
watchdog at its deadline still leaves its block, as before; the next launch rewrites it.offrig_plantakescontainer_disk_gb(1 to 2000), overriding the profile's
container_disk_gbfor that plan. The plan stores it and the launch sends it as
containerDiskInGb; the reply andoffrig_statusshow the size in force. The disk is not
priced into the worst case (offrig prices GPU time only).
-
Hardware limits on a plan (issues #9 and #10). A plan could land on a fallback card the
work could not use: ajobplan rented an A100 host on a CUDA 12.8 driver, and the job's
CUDA 13 PyTorch failed after setup. Worst-case pricing also always used the profile's top
price ($3.49/hr), so a 3.5 h pod expecting a $2.09 card committed $12.22 instead of $7.32.- Profiles gain
min_cuda(oldest host CUDA; the pod create sends every version at or
above it asallowedCudaVersions, and a job profile uses the newer of this and its
image's floor) andmin_vram_gb(least total VRAM; smaller offers are never chosen).
Both default to unset, so existingconfig.tomlfiles load unchanged. Thejobprofile
now setsmin_cuda = "13.0". offrig_plantakesmax_price_hr(total $/hr; dearer offers are left out and the worst
case ismax_hours x min(max_price_hr, dearest listed price left)) andno_fallback
(only the profile's first GPU family; the two RTX PRO 6000 Blackwell editions count as
one). A plan with nothing left is refused with the reason for each dropped GPU. The reply
lists the GPUs that were left out.- The plan stores its GPU list, price and CUDA floor, and
offrig_launchnow rents only
from them. Before this the launch built the pod from the profile's full list and ignored
what the plan priced. offrig_job(and the launch result) report the GPU type and, when the pod API reports
it, the host CUDA version rented. A host older than the plan's floor, a GPU outside the
plan's list or a price above the plan's produces awarningsentry and aWARNING
next_action; nothing is terminated automatically.offrig_statusshows each open
plan's GPU list, price and CUDA floor, and each pod's GPU.- Unverified: whether the pod API reports the host's CUDA version, and under what field
name. offrig readsmachine.cudaVersionif present and says so inrented.noteswhen it
is not, rather than treating a missing report as a pass.
- Profiles gain
-
A per-project default side-car port (issue #11). The shell driver's default port,
11439,
was shared by every project on the machine, so another script taking it silently took a
running side-car down. A lane now has a side-car port derived from its slot the same way
its tunnel port is:11700+ slot (11700to11763), a range above every lane tunnel
and runner port (11500to11627), the plain lane and the local Ollama. Nothing is
stored inlanes.toml; existing registries work unchanged.offrig-mcp --sidecar-port --project <dir>prints the project's port (allocating its
lane if it has none), orOFFRIG_SIDECAR_PORTwhen set. That value is refused when it is
not a port, is below 1024, or is11434,11435,11436or inside the lane tunnel range.--checkalso exits 1 when the port is held, naming the port and, if an offrig side-car
answers there, the project it reports. The probe is a request the driver refuses before
calling any tool, so a running side-car is not touched.offrig_statusreports the lane'ssidecar_port.- Not in this repository: t...