Architecture
Jevalan is a local daemon with three ways in, one engine in the middle, and a stream of events that the terminal UI watches.
agents, hooks, scripts │ ┌────────────┼──────────────────────────┐ │ │ │POST /{backend}/v1/systemone POST /v1/decide/{flow} POST /v1/hub(raw System One question) (one specific flow) (by capability) │ │ │┌──▼────────────▼──────────────────────────▼─────────────────────┐│ JEVALAN ││ ││ passthrough ─────────┐ flow engine ◄──── capability registry││ (forwards untouched) │ rule · noul · ├─ flow → your flows│ │ choice · score · ├─ http → a service│ │ batch └─ exec → a process│ ▼ │ ││ System One backends (OpenRouter, TypeSafe, Ollama, ││ local models): keys, deadlines, cost ││ ││ event bus ──── WebSocket /events ────► terminal UI │└────────────────────────────────────────────────────────────────┘ ▲ │ same path, in process, no HTTP jevalan lab (offline · record · live)Three principles
Section titled “Three principles”- The model judges, your code acts. A model returns typed probabilities. Local rules and thresholds, which you can read and test, turn them into a decision. The agent applies the effect. Jevalan never performs it.
- The agent does not know who decides. It asks for a capability. Whether your own flow or a third-party specialist answers is a choice in configuration, so you can change it without touching the agent.
- Telemetry is explicit. Every step emits an event, and you can see it in the TUI. Events have no field for the original input, but they can carry answers, outcomes, signals, and errors, so treat the stream as sensitive.
The pieces
Section titled “The pieces”| Piece | Role |
|---|---|
| Passthrough | Forwards a System One request to a backend untouched and holds the key, the deadline, and the cost accounting. |
| Flow engine | Walks a YAML graph of rules and model questions and returns an outcome with the path it took. |
| Capability registry | Maps a typed question to a provider: a flow, an HTTP service, or a local process. |
| Event bus | Publishes what happens, node by node, over a WebSocket. |
| Terminal UI | Shows the topology, metrics, decisions, and traces live, and edits flows as drafts. |
| Lab | Runs the same cases through the hub path, offline or against real backends. |
| Hook adapters | Let Claude Code and Crush ask tool.risk before every tool call. |
A decision, end to end
Section titled “A decision, end to end”Take a Claude Code session that is about to run rm -rf /tmp/build:
- Claude Code runs the
PreToolUsehook, which reads the tool call from stdin. - The hook finds the daemon through its discovery file and sends
POST /v1/hubfortool.risk. - The hub resolves
tool.riskto thetool-risk-localflow. - Two fixed rules run first: a recursive delete of the root or the home folder is denied, and a download piped into an interpreter asks. Neither calls a model.
- If neither matches, one
batchnode asks four risk questions in a single call to a local model and gets four probabilities. - Routing rules apply the thresholds: any risk of 0.85 or more is
deny, any of 0.55 or more isask, otherwiseallow. - The hub validates the decision against its schema and returns it. Every step was published as an event.
- The hook records the decision in the shadow log. In
enforcemode it also answers Claude Code; inshadowit stays silent.
The hook can only tighten Claude Code’s permissions, never relax them. In the worst case it asks too much.
Go deeper
Section titled “Go deeper”- Flows and Capabilities and hub explain the two ways to define a decision.
- Lab explains how to measure one.
- The repository has the full design with more detailed diagrams:
docs/architecture.md.