Chapter 6: crates, three Rust packages and why there are three
Everything the plugin does that is not plumbing is Rust, in three 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-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"] daemon["jevhooks-daemon<br/>the jevhooks binary"] jevcrates["jev-http, jev-protocol<br/>(third-party/jevcrates)"] rmcp["rmcp<br/>(the deizel/rust-sdk fork)"] modc --> events daemon --> events 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 fork everyone trips over. The root
Cargo.tomlasks for rmcp 3.5.0, the Rust MCP library, and then patches it with a commit ondeizel/rust-sdk, a private fork. rmcp 3.5.0 refuses every request after the way Claude Code 2.1.287 opens a stdio server (server/discover, theninitialize), so without the patch the MCP server connects and offers no tools. The fork carries one fix on top of thermcp-v3.5.0tag. It is fetched over SSH and is not public yet, and Cargo resolves the whole workspace before building any part of it: without access, evencargo test -p jevhooks-events, which does not use rmcp, stops at fetching it.
Try it. With access to the fork,
cargo testfrom 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 daemon (11 and 12).
For the people who maintain it
All three 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-daemon/ | Chapters 11 and 12: the jevhooks binary. |
← Previous: Chapter 5, tests/ · Up: jevhooks · Next: Chapter 7, jevhooks-events/ →