interlock.sh docs

Interlock #

Why this exists #

Some of your code changes far more often than the rest of it. Pricing rules. A validation someone keeps refining. A report a customer wants shaped differently. The copy on one page.

That code lives inside the same build as everything else, so changing one line of it costs a build, a test run, and a deploy. Twenty minutes when nothing goes wrong. Another twenty when it does. The part of your system that moves fastest is stuck behind the slowest process you own, and everybody learns to batch up changes and ship them on Thursday.

Interlock takes that code out of your build without taking it out of your application.

A unit is one file in your git repo. Your application embeds the Interlock SDK, fetches the unit, compiles it, and runs it inside its own JVM, against your own classes, your own database, and your own transactions. Edit the file, sync it, and the behaviour changes. No redeploy. No restart. No second service to operate.

NormallyWith a unit
Change a pricing ruleedit, build, test, deployedit the file, interlock sync code
Time to liveminutes to hoursseconds
Can it read your database?yesyes, the same connection and transaction
Can it call your own classes?yesyes, the same objects in the same JVM
Who can ship itwhoever can deploywhoever can merge

The catch, stated up front rather than discovered later: a unit is real code running in your real process. It is not a sandbox. A unit that loops forever in Java is a thread in your application. That is the trade. You get your own data and your own speed, and in exchange the trust boundary is project membership, not a jail. JavaScript units do get a wall-clock limit. Java units do not, because in-process sandboxing of Java is not something anyone can currently do honestly.

The other reason #

If your product writes code, that code needs somewhere real to run.

A sandbox cannot reach your data, so whatever it produces is a toy. Your own application can reach everything, but running freshly written code directly inside it is how you get a 3am page. So most teams generate something impressive, demo it, and quietly never ship it.

Interlock is the space between. Generated source runs against real capabilities, under a real identity, with the run recorded. Code that turns out to be worth keeping gets committed to git, versioned, reviewed and promoted like anything else. Code that was only ever for one request goes away.

You do not need this half to get value from the first half. Most people arrive for hot-reloadable business logic and find this later.

Is this for you? #

Probably yes if you run a JVM application, you have logic that changes weekly or faster, and you want that logic in git under review rather than in a database table someone edits by hand.

Probably not yet if you need to run untrusted third-party code, you are not on the JVM, or you need units to survive without the host application running. Interlock does not solve any of those today.

The first unit takes about a minute #

Three commands, in a repository you already have:

interlock init
echo 'export default (req, il) => ({ ok: true, msg: "hello " + req.str("name", "world") });' > code/hello.js
interlock sync code && interlock run hello
{ "ok": true, "msg": "hello world" }

That is the whole loop. Change the file, run interlock sync code again, and the next call answers differently. Ids are paths: code/hello.js is hello, and code/billing/quote.java is billing/quote.

Being honest about the rest #

Getting a unit to run took a minute. Getting units running inside your application is the real work, and it is worth knowing the shape of it before you start:

StepRealistically
First unit, on our servera minute
Adding the SDK to your host and building the Enginean afternoon, most of it your build file
Your first unit that touches your own entitiesread Quarkus first

That third row is the one that matters. If your host is Quarkus and your units touch Panache entities, one rule will cost you days if you meet it by accident: give unit-facing entities private fields with hand-written getters and setters, and read them through the getters. A field named id appears to work, which is exactly why the problem hides. Any other name fails at runtime after compiling cleanly.

We would rather tell you that on the front page than let you find it on day three.

Where to go next #

If you areGo to
Starting from nothingStart here
Wiring this into your own applicationEmbedding the SDK
On Quarkus, touching entitiesQuarkus, before your first unit
Staring at an error right nowFailure catalogue, search your error text
Looking something upReference, for lookup and not for reading

How these docs are written #

Every rule here is paired with the failure it prevents and the literal error text you would otherwise paste into a search box. If a rule has no failure attached, it is not on the site.

Claims are marked MEASURED or INFERRED. A measurement names the version it was taken on and includes the command to re-run it, because a measurement you cannot reproduce is a rumour with a table around it.

Every page is also raw markdown: add .md to any URL.

Pointing an AI at this? Give it https://docs.interlock.sh/llms-full.txt. That is the entire site in one fetch, in dependency order.

Point your agent here
https://docs.interlock.sh/llms-full.txt

The entire corpus as one plain-text document, in dependency order. One fetch, no HTML parsing. For the index alone, use /llms.txt.

Generate this project’s skills
https://docs.interlock.sh/skills.json

Every skill with its name, trigger and body — enough to write .claude/skills/<name>/SKILL.md, ~/.codex/skills/interlock-<name>/SKILL.md and .cursor/rules/<name>.mdc with no further reading.

Start hereWhat Interlock is, the shape of a project, and the first unit that works.The modelWhat a unit is: ids are paths, envs promote, the handler contract, library units, the per-unit store.Java units importing Java unitsDirectory is package. Resolution is javac's sourcepath, not a shared classloader, and each importer gets its own compiled copy.Embedding the SDK in a hostWiring, compile anchors, trust and the shadow boundary, context vs session, requests and responses, Refusal.Quarkus and Panache entitiesQuarkus builds every entity twice and the JVM loads the transformed copy. The entity accessor rule, measured. The most expensive page here.TestingThe two-second compile gate, the unit-harness test, assert effects not status codes, and why every measurement needs a control arm.Operating a project.interlock-key binds a tree to a project, the sync index is the base, and how conflicts surface.Durable tasks and workersSubmit work once, run it on outbound-only Java or Node workers, and understand leases, retries, cancellation and unknown outcomes.Search over your own dataAn index inside your own JVM, a synonym graph that knows what your things are, and results that say when they cannot be trusted.Self-hosted AI workersInstall Qwen on your Mac or Linux hardware, choose an explicit route policy, and operate recoverable AI runs.Model fleetsScaffold a model project, enroll narrow trainer and inference machines once, and operate verified self-updating workers.Failure catalogueSymptom to cause to fix. Start here if you arrived by pasting an error.ReferenceThe flat API surface: Request, Interlock, Store, Result, Refusal, handler contracts.Verify a hostThe eight commands that prove a host is wired correctly, in order. /start tells you to run these, so they belong in the corpus.