Chapter 6: crates, four Rust packages and why there are four
Everything the plugin does that is not plumbing is Rust, in four crates (a
crate is Rust's package: one Cargo.toml, the way an npm package has one
package.json). Each exists because of where it has to run.
- jevhooks-events runs everywhere. It is the vocabulary: the hook events, what is done with each, the verdict and the decision. It does no I/O at all, so it compiles both into the mod and into the daemon.
- jevhooks-mod runs inside Claude Code. It is compiled to WebAssembly and then to JavaScript, and decides in well under a millisecond what to do with each event.
- jevhooks-rules runs wherever it is asked to, and does nothing by itself. It is the rule book: what Jev is asked, and the rules that turn its answers into what happens. The daemon runs it; the website draws it.
- jevhooks-daemon runs as its own process. It is the
jevhooksbinary: the daemon that asks Jev, the per-session MCP server, and the command-line tools that read them.
flowchart BT events["jevhooks-events<br/>the vocabulary, no I/O"] modc["jevhooks-mod<br/>routing, as JavaScript"] rules["jevhooks-rules<br/>questions and rules, no I/O"] daemon["jevhooks-daemon<br/>the jevhooks binary"] jevcrates["jev-http, jev-facts, jev-protocol<br/>(third-party/jevcrates)"] rete["rete<br/>(third-party/rustcrates)"] rmcp["rmcp<br/>(crates.io, 3.5.1 or later)"] modc --> events rules --> events rules --> rete rules --> jevcrates daemon --> events daemon --> rules daemon --> jevcrates daemon --> rmcp
(An arrow means "depends on".)
The point of the split is the line down the middle. The mod and the daemon
live in different processes and speak JSON over a socket; the only way to
be sure they agree on what "PreToolUse" or "ask" means is for both to be
compiled from the same Rust enum. That enum is in jevhooks-events, and
it is the only thing the two sides share.
Aside: the version that matters. The root
Cargo.tomlasks for rmcp, the Rust MCP library, at 3.5.1 or later, and the "or later" is not decoration. rmcp 3.5.0 refuses every request after the way Claude Code opens a stdio server (server/discover, theninitialize), so on 3.5.0 the MCP server connects and offers no tools, with no error anywhere a person would look. For a few days this repository carried its own fix on a fork; rmcp 3.5.1 fixed it upstream, and the fork is gone.
Try it.
mise run test(orcargo test) from the repository root runs every crate's tests. None of them needs the network, a Jev key or a daemon: the verdicts are tested on made-up probabilities, and the routing on made-up events.
The chapters that follow take them in the order an event meets them: the vocabulary (7 and 8), the routing in the mod (9 and 10), the rule book (11 and 12), the daemon (13 and 14).
For the people who maintain it
All four are version 0.1.0, edition 2024, publish = false, and take
every dependency's version from the root Cargo.toml's
[workspace.dependencies]. The release profile there (opt-level = "s",
lto, panic = "abort", one codegen unit) applies to the wasm build and
to the daemon alike.
In this folder
| Crate | What |
|---|---|
| jevhooks-events/ | Chapters 7 and 8: every hook event, its role, the verdict and the decision. Depends on serde only. |
| jevhooks-mod/ | Chapters 9 and 10: the routing and the read-only filter, built as a cdylib for wasm32 and an rlib for native tests. |
| jevhooks-rules/ | Chapters 11 and 12: the questions, the facts and the rules, as rete networks. No I/O; builds for wasm32 too. |
| jevhooks-daemon/ | Chapters 13 and 14: the jevhooks binary. |
← Previous: Chapter 5, tests/ · Up: jevhooks · Next: Chapter 7, jevhooks-events/ →