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.
| Normally | With a unit | |
|---|---|---|
| Change a pricing rule | edit, build, test, deploy | edit the file, interlock sync code |
| Time to live | minutes to hours | seconds |
| Can it read your database? | yes | yes, the same connection and transaction |
| Can it call your own classes? | yes | yes, the same objects in the same JVM |
| Who can ship it | whoever can deploy | whoever 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:
| Step | Realistically |
|---|---|
| First unit, on our server | a minute |
Adding the SDK to your host and building the Engine | an afternoon, most of it your build file |
| Your first unit that touches your own entities | read 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 are | Go to |
|---|---|
| Starting from nothing | Start here |
| Wiring this into your own application | Embedding the SDK |
| On Quarkus, touching entities | Quarkus, before your first unit |
| Staring at an error right now | Failure catalogue, search your error text |
| Looking something up | Reference, 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.
https://docs.interlock.sh/llms-full.txtThe entire corpus as one plain-text document, in dependency order. One fetch, no HTML parsing. For the index alone, use /llms.txt.
https://docs.interlock.sh/skills.jsonEvery 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.