LaC (LLM as Code) - reference engine. Declarative behavior for LLM apps: law in files, perimeter in code.
Project description
LaC - LLM as Code
Work in progress - not finished, not stable, and it holds plenty of bugs. This is an active research build. The engine runs, but features are half-cured, the injection defense is an open front (see Known limitations), and there are no tests or CI yet. APIs and the compose schema will change without notice. Read it, learn from it, break it - do not ship it.
LaC is a protocol for building LLM applications the way IaC builds infrastructure: behavior is declared in versioned artifacts, not improvised in prompts. An application is a folder of plain files - a compose declaration, law files with explicit authority levels, and a commands module. The engine is generic and knows nothing about any particular application.
This repository holds the LaC specification (SPEC.md) and the reference engine. The engine is intentionally small: a compose loader with a strict parser, a context assembler that loads law in trust order (L1 then L2, with L3 kept out of the system channel), a REPL, and an agentic loop where free-form language and canonical !commands are two roads into the same code.
Why LaC
The premise is a threat model, not a convenience: an LLM application must protect the user's machine from its own model. A model can be talked into anything - a note in a document, a line in a file, a stray instruction in retrieved data. So the guarantee cannot live in the model's good behavior; it has to live in structure and code the model does not control.
- Behavior as code. Prose asks; code enforces. Where a normal app improvises rules in a system prompt, LaC turns the load-bearing parts into deterministic mechanism - commands are modules, the write perimeter is a filesystem jail, consent is a real gate on stdin. Prose remains the law for the persona; it never guards the disk.
- Capability, not promises. A wrong action must be survivable. The engine's hands are caged (every path resolves through a realpath jail rooted at the memory folder); the model may misjudge, and the machine still stands.
- Levels are authority. L0 is code (the engine and command modules - executes, never enters the context). L1 is limits, L2 is behavior - both always in context. L3 is data - all stored content, with zero authority, never instructions. On any conflict the higher level wins, and nothing at L3 is promoted by being read, named, or retold.
- A protocol, not one app. The same law can run on more than one host. The Grimoire application runs this engine; its earlier form runs the same protocol on Claude Code with the perimeter enforced by harness permissions instead of engine code. One law, two hosts - that is the point.
A running theme of the work, and the reason for the honesty above: raising the model's system prompt was the wrong lever. Moving L3 data out of the system channel entirely - so the model receives law as law and data as data - did in one engine change what layers of defensive prose in an application could not. Place in the structure is authority. That result, and the ones still failing, are the substance; the numbers are being measured, not assumed.
Core mechanics
- Two roads, one code. Canonical
!commandsbypass the model entirely (zero tokens); free-form requests reach the same functions through tool calling. The model interprets and narrates; code executes. A canonical command is fed back to the model as a real synthesized tool exchange, not pasted prose, so the model never re-issues what code already ran. - System prompt is L1/L2 only. Boot data and all retrieved content arrive as conversation messages (marked L3), never in the system channel. The system carries law; the conversation carries data.
- Perimeter in code. Effects on disk are the engine's job: a write jail (realpath-rooted), a consent gate for destructive actions, and per-app write allowlists.
- Retrieval-first memory. Routing indexes stay in context; bulk content is fetched on demand and cited, not preloaded.
Quick start
pipx install llm-as-code
cd your-app
lac
An application folder looks like:
your-app/
.lac/
llm_compose.yaml # the anchor: all paths resolve relative to the app root
law/ # L1/L2 law files
souls/ # personas (L2)
commands.py # the app's command module (TOOLS + COMMANDS registry)
<memory folders> # L3 content
The engine finds .lac/llm_compose.yaml in the current directory (or in the directory passed as the first argument) and resolves every path in the compose against that root. No absolute paths anywhere.
Providers: Anthropic (raw HTTP, prompt + conversation caching, ANTHROPIC_API_KEY from env only), Mistral (MISTRAL_API_KEY), and Ollama for local models. The vendor surface lives in one dispatch table, adapter.PROVIDERS, behind adapter.send(); any OpenAI-shaped API is one new entry.
Trust model: the commands module is application code (L0) and the engine executes it deliberately - running an app means trusting its code, the same way installing any package means trusting its authors. The levels protect L1/L2 from the model and from L3 content, not the machine from the app you chose to run.
Status and roadmap
Pre-release. The spec (SPEC.md, methodology v0.2) is the canon; this engine tracks it through milestones M0-M3 (loader, retrieval commands, write perimeter, drift metrics), with an M4 opening on refusing destructive user commands. The current focus is a debt pass - stabilize every existing feature before adding more.
These are limitations of this engine and the protocol - application-level defenses and their bugs (datamarking, memory hygiene, persona discipline) live with each application, e.g. the Grimoire.
- Injection is not solved. Keeping L3 out of the system channel and enforcing the levels raises the bar, but no channel discipline makes a capable model incorruptible - residual behavioral drift is real. The engine's guarantee is that effects on disk are survivable (caged), not that the model always obeys. How hard a given application resists injection is measured, not assumed.
- No tests or CI, no drift numbers yet. The M3 harness (scripted turns, an LLM judge, deterministic checks, a model matrix) is designed but not built. Treat every claim here as a hypothesis under measurement.
- Unstable surface. APIs and the compose schema change without notice; SPEC.md still trails the engine on several points.
- Behavioral hygiene varies by model. Cheaper heads carry the mechanism but not the voice; stronger heads rationalize rather than obey. Perception is not a guarantee - which is the whole reason the perimeter is code.
First application
Grimoire - a personal memory system and the first LaC application. Its earlier implementation runs the same protocol on Claude Code, with the perimeter enforced by harness permissions instead of engine code: one law, two hosts.
License
Apache-2.0. The protocol is free; applications built on it license themselves independently.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file llm_as_code-0.2.0.tar.gz.
File metadata
- Download URL: llm_as_code-0.2.0.tar.gz
- Upload date:
- Size: 22.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4ad2670a9db61b64bddd4f6946b46b7fd343ff24777f3f50c7f7f1846f5963a3
|
|
| MD5 |
8c5d0e0ff7c2b6b4bcb8726abe80a0fc
|
|
| BLAKE2b-256 |
1ffb65f645062144f79190db1fb12dcf9e0e7e73e5fd1694b6e4f36d8228afec
|
File details
Details for the file llm_as_code-0.2.0-py3-none-any.whl.
File metadata
- Download URL: llm_as_code-0.2.0-py3-none-any.whl
- Upload date:
- Size: 16.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
127afc541a3b58da32062508cf09305b3f229041373e1315b3507c5abb96c5a9
|
|
| MD5 |
4f06a0f7a5b8580f41d0b339bce934db
|
|
| BLAKE2b-256 |
6574269898aab4ff0b0023169a332215b5fb7473103e48cd5ce2bcfc750674a5
|