Inspiration Two questions: What leaves my machine? And where did that answer come from?
A personal AI is private until the first time it calls a bigger model. Most apps make that call invisibly — an API key in a settings page, and from then on your notes travel with every question. You can't tell what left, when, or to whom.
The second question came from watching small models work. In role-play test runs, two local models invented sensor readings ("500 kPa") and then listed them as "known for certain"; one named log files it had "written" when no files existed. A system-prompt rule against it, replayed at the moments it happened, changed nothing measurable. So Airlock stopped asking models to behave and started checking their work against the record instead.
What it does Airlock is a desk with two AI participants side by side, sharing one conversation: one runs
on your machine (NVIDIA Nemotron 3 Nano 4B through Ollama), the other can reach NVIDIA Nemotron 3 on Nebius Token Factory. Everything you write stays local by default.
The boundary. Before anything crosses to the cloud, a credential scanner checks it, then a separate local gate model (qwen2.5:14b) rules on it. The gate has to be local: to ask a remote model whether something may leave, you would first have to send it. If the gate is down, slow, or answers nonsense, nothing is sent. Every turn is gated, bound to the exact words and the exact destination, and every crossing — messages, tool results, web searches, briefs carried out by hand — goes on a permanent record you can open: what has a cloud model ever seen? is a query, not a guess.
The evidence. Every reply is checked against the thread's record — by code, not by a model:
- each number is traced to where it was first said: your message, a file a tool read, the calculator's working, or nowhere (underlined);
- a calculator result carries its expression and its inputs' sources — and if an input had no source, the result keeps its underline: the sum is checked, the premise is not;
- words copied from the other participant are dimmed;
- a reply that names a file nobody opened, claims a page it never fetched, or writes a link nobody gave it is marked.
Tools, on the record. Read-only workspace files, web search through Tavily (every query gated before it leaves), a calculator, and a way for a model to ask you for a result instead of inventing one. Delete any message and its words are erased from disk — but the record of where it went stays.
Colour carries meaning throughout: green stayed local, amber crossed, red was withheld.
How we built it Node.js and Express, the built-in node:sqlite, and a frontend with no dependencies, no build
step and no CDN. One npm package (Express).
- One stream, two tiers. Ollama locally and Token Factory remotely, adapted to a single streaming contract so the rest of the app never asks where a model runs.
- A kernel and one network door. Only one file in the codebase may call
fetch, and it refuses any content-bearing connection without a clearance the kernel issued — a test fails if any other file reaches the network. - Provenance in the store. An append-only provenance log records which model touched each message and which side of the boundary it was on. A message's tier is recorded when it is written, never inferred later.
- Measured, not asserted.
tools/gate_bench.jsruns labelled cases through the real gate and reports leaks and friction separately, with held-out sets written by a human and run once. About 860 assertions across twenty test suites; sixteen run offline on a clean clone with fakes for both the cloud and Ollama.
Challenges we ran into - The gate was briefly running in the cloud. The gate model was chosen from the app's
default model — which is Nemotron on Token Factory whenever a key is present. Nothing failed, because a remote model answers the gate prompt perfectly well; it just answers after the content has already crossed. The gate now resolves a local model itself and refuses a remote one.
- The gate withheld harmless things — because of us. Ordinary questions about pressure readings were being withheld. Replaying them from a copy of the database showed the gate was reading Airlock's own system prompt and judging it as the user's. It now reads only what the user wrote. Three tuning rounds took friction on the tuning set from 12/32 to 0, and no version has released a secret on any test set.
- The first real cloud tool round failed. Our fake test server accepted tool-call messages that Token Factory's validator rejected. Every remote tool — files, web, calculator — had only ever worked against the fake. The fake now validates as strictly as the real endpoint.
- Small models don't follow rules mid-conversation. A prompt rule against inventing data measurably did nothing; a nudge sent as a system message made the 4B act as if the conversation had just started (28 times out of 28). Deterministic marks and transcript-level notes worked where prompt rules didn't.
Accomplishments that we're proud of - A gate that has never released a secret on any test set, with its friction measured and
published rather than hidden.
- A crossing record that survives moves, renames and deletes: the words can go, the record of where they went stays.
- Marks that catch real model behaviour — invented numbers, unopened pages, copied words — without a model judging anything.
- A verification log that records what didn't work as carefully as what did.
What we learned Prefer a deterministic check over a prompt rule for small models. Measure the gate both ways
— what it leaks and what it wrongly blocks — because a gate that blocks everything is safe and useless. And a fake test server is only as good as its strictness.
What's next for Airlock - Redaction: crossing a message with only the sensitive parts removed, instead of all or nothing.
- A "what will cross?" preview before you send.
- The next gate tuning set built from real withholds seen in use.
- A hosted try-it mode: per-visitor sandboxes, spend caps and a workspace allowlist are already built and tested; they need a GPU host beside them for the local gate.
Built With
- express.js
- javascript
- nebius
- node.js
- nvidia
- ollama
- sqlite
- tavily