All blogs

The Zero-Dollar AI Stack

Most AI coding and research tools make one decision for you before you have typed a single prompt: which model you use, and whose server your code goes to in order to use it. Zorp does not make that decision. It is a Rust binary that runs on your machine, talks to whatever model endpoint you point it at, OpenAI, Anthropic, or anything else, local or not, and has no opinion about which one you pick.

That is not a philosophical stance. It is a specific, checkable design choice, and this post is about what it actually gets you: any model, a genuinely zero-dollar path if you want one, and a local-by-construction guarantee on the parts of the system that do not need a network at all.

Why this is not a niche preference

  • 81%of developers have security or privacy concerns about AI tools

    Security and privacy concerns rank as the single biggest deal-breaker for AI tool adoption among developers surveyed, ahead of every other objection. 84% are using AI tools anyway, which is the actual tension this stack is built to resolve: use the tool, keep the choice about where your code goes.

    Source: Stack Overflow, 2025 Developer Survey (AI section)

Developers want AI tools. Most of them do not trust where the data goes.Source: see each card

Any model, because the transport does not care

Zorp talks to a model through one interface: an OpenAI-compatible chat completions endpoint, plus a native path for Anthropic. That single interface is what Ollama, LM Studio, vLLM, and oMLX all speak, so “local model” is not a special mode bolted onto the side. It is the same code path as GPT-4o or Claude, pointed at 127.0.0.1 instead of a cloud domain.

Same three variables, four different backends

shell

export ZORP_BASE_URL="https://api.openai.com/v1"
export ZORP_API_KEY="sk-..."
export ZORP_MODEL="gpt-4o-mini"
cargo run -- "Summarize the second law of thermodynamics in one sentence."

shell

ollama serve
export ZORP_BASE_URL="http://127.0.0.1:11434/v1"
export ZORP_MODEL="qwen3:4b"
cargo run -- "Summarize the second law of thermodynamics in one sentence."

No ZORP_API_KEY. Ollama serves an OpenAI-compatible /v1/models endpoint, so it is a preset, not a special case.

shell

export ZORP_BASE_URL="http://127.0.0.1:8000/v1"
export ZORP_MODEL="<whatever that endpoint lists>"
cargo run -- "Summarize the second law of thermodynamics in one sentence."

Any server that speaks OpenAI-compatible chat completions works the same way. The web UI's settings panel lists the models the endpoint actually reports, rather than a hardcoded menu.

shell

export ZORP_API_KEY="sk-ant-..."
cargo run -p zorp-agent -- --provider anthropic --model <model> "<task>"

The one native path outside the OpenAI-compatible transport, because Anthropic's API shape differs enough to warrant its own client.

This is the actual environment-variable interface, unmodified. Swapping backends is a base URL, not a rewrite.Source: README.md, aviskaar/zorp

The web UI’s settings panel makes the same choice without an environment variable: pick a provider, point it at a base URL, choose from the models that endpoint actually lists. A setting saved there beats the matching ZORP_* variable, which beats the built-in default, and every field says which of the three it came from. The API key is the one exception: it lives in memory for the life of the server process and is never written to disk unless you also set it in the environment yourself.

Zero dollar, precisely

Two separate costs get bundled together in most “AI stack” pricing, and Zorp is worth being exact about which one it removes.

The software costs nothing, full stop. Zorp is MIT licensed, no subscription, no seat, no paid tier anywhere in the project. The install script pulls prebuilt binaries for your platform, verifies the published checksum, and puts zorp, zorp-agent, and zorp-web on your machine with no Rust and no Node required:

curl -fsSL https://raw.githubusercontent.com/aviskaar/zorp/main/install.sh | bash

Inference costs whatever your model costs, which can also be zero. Point ZORP_BASE_URL at a frontier API and you pay that provider per token, exactly as you would calling it directly. Zorp adds no markup and no toll of its own. Point it at Ollama, or LM Studio, or vLLM running on your own GPU, and the marginal cost of every completion is your electricity bill. The docker-compose path brings a full local stack up in one command, UI, agent server, and an Ollama sidecar together:

ZORP_WEB_TOKEN=$(openssl rand -hex 16) docker compose up --build
docker compose exec ollama ollama pull qwen3:4b
docker compose exec ollama ollama pull qwen3-embedding

That is a genuinely zero-dollar AI research and coding stack: free software, running a free model, on hardware you already own. It is also not the only option. The same binary, the same evidence record, the same validate/investigate/co-write/deliver loop runs unchanged against a frontier model the moment a question needs the ceiling only a frontier model currently offers. Nothing about switching costs you a rewrite, because nothing about the harness assumes which one you picked.

Local by construction, not by promise

“Your data stays local” is a claim almost every AI product makes and almost none of them can show you the code for. Zorp’s conversation search is a specific, narrow example of what actually enforcing it looks like, rather than just asserting it.

The recall feature lets the web UI search everything you have ever asked Zorp, by meaning, entirely on your machine. Getting that right took four separate, layered checks, because any one of them being wrong on its own would leak a conversation to the network:

The endpoint has to be a loopback literal or localhost, and it has to still resolve there

Naming a remote host in the embedding endpoint variable does not get you a remote embedder. It gets you a refusal that names the host. The check is on the resolved address, not the string you typed.

The HTTP client can only reach the addresses that resolution returned

A resolver that runs after the check passes could still be tricked into pointing somewhere else. The client is restricted to the addresses already validated, through a resolver that performs no lookup of its own at request time.

Redirects are refused, not followed

A loopback endpoint that responds with a 302 to somewhere else would otherwise take your conversation with it. The client does not follow that redirect; it fails instead.

Proxy detection from the environment is switched off

An HTTP_PROXY variable set anywhere on the machine, by another tool, another shell profile, another process, cannot silently route a loopback call through a third-party server. The embedding client ignores it.

There is no remote embedding provider, no flag that adds one, and no fallback when the local model is missing. If nothing answers on 127.0.0.1, the search box says so and searches nothing, rather than quietly starting to work by phoning out. A corpus of everything you have ever asked an agent that reads your files is exactly the thing a “local first” claim should mean, and here it is enforced four different ways rather than asserted once in a privacy policy.

The same posture shows up everywhere else in the stack, not just in recall:

Built for how engineers already work

A few details in here specifically are aimed at the person reading this in a terminal, not a boardroom.

Skills you already wrote work here unmodified. Zorp reads the same skill format Claude Code does: a directory with a SKILL.md, YAML frontmatter, a markdown body. Nothing to port, nothing to convert.

---
name: code-review
description: Review a diff for correctness bugs. Use when asked to review changes.
---

Read the diff first, then the surrounding code. Report findings by
severity, and say when you are unsure.

A skill can only add guidance, never permissions: it cannot enable a tool, loosen an approval preset, or reach past the command denylist, and a body over 64 KiB or one that tries to resolve outside its own directory gets skipped rather than loaded.

Every write or run asks first. Tool calls that touch the filesystem or a shell go through an approval prompt before anything happens. A long run can stand those prompts down for one session, and the toolbar says so the whole time it is on; it still cannot get a denylisted command past the policy underneath. MCP servers you connect declare a trust level explicitly in .zorp/mcp.toml, rather than inheriting a default.

Extend it with MCP, not with a plugin API to learn. Any MCP server you already run connects the same way tools do everywhere else. Memory across sessions is open-context, a separate MIT-licensed project, connected as an MCP server rather than baked in, so it stays optional and swappable instead of becoming a dependency Zorp carries forever.

The part this all sits under

None of the above is a separate “developer mode” bolted onto a hosted product. It is the substrate the actual research capabilities run on. validate, investigate, co-write, and deliver, the loop that turns a question into a pre-registered investigation and an evidence record, run against whatever model you configured in the sections above, local or frontier, with no code path that treats one as first-class and the other as an afterthought.

Zorp ships 24,965 lines of Rust across a handful of crates, 605 tests, and an MIT license, all measured the same way the homepage measures them: checkable against the repository, not asserted here.

Where this actually is

Zorp is pre-alpha, and this post is not going to round that off. The install script and the OpenAI-compatible transport work today. The published Docker image does not pull yet, the package is published but still private, tracked in issue #30, so docker build or the install script are the working paths until that flips. The research feature (validate/investigate/co-write/deliver) needs a source build today, because zorp-track bundles DuckDB and is not yet in the prebuilt binaries.

None of that changes the shape of the argument. The stack underneath is free, it runs any model that speaks an OpenAI-compatible API or Anthropic’s, and the parts of it that do not need the network do not touch it. That is a specific set of engineering decisions, not a slogan, and every one of them above is something you can go read in the repository yourself.