jevhooks.git / crates / README.md
1# Chapter 6: crates, three Rust packages and why there are three
2
3Everything the plugin does that is not plumbing is Rust, in three crates (a
4crate is Rust's package: one `Cargo.toml`, the way an npm package has one
5`package.json`). Each exists because of where it has to run.
6
7- **jevhooks-events** runs everywhere. It is the vocabulary: the hook
8  events, what is done with each, the verdict and the decision. It does no
9  I/O at all, so it compiles both into the mod and into the daemon.
10- **jevhooks-mod** runs inside Claude Code. It is compiled to WebAssembly
11  and then to JavaScript, and decides in well under a millisecond what to
12  do with each event.
13- **jevhooks-daemon** runs as its own process. It is the `jevhooks` binary:
14  the daemon that asks Jev, the per-session MCP server, and the
15  command-line tools that read them.
16
17```mermaid
18flowchart BT
19  events["jevhooks-events<br/>the vocabulary, no I/O"]
20  modc["jevhooks-mod<br/>routing, as JavaScript"]
21  daemon["jevhooks-daemon<br/>the jevhooks binary"]
22  jevcrates["jev-http, jev-protocol<br/>(third-party/jevcrates)"]
23  rmcp["rmcp<br/>(the deizel/rust-sdk fork)"]
24  modc --> events
25  daemon --> events
26  daemon --> jevcrates
27  daemon --> rmcp
28```
29
30(An arrow means "depends on".)
31
32The point of the split is the line down the middle. The mod and the daemon
33live in different processes and speak JSON over a socket; the only way to
34be sure they agree on what `"PreToolUse"` or `"ask"` means is for both to be
35compiled from the same Rust enum. That enum is in `jevhooks-events`, and
36it is the only thing the two sides share.
37
38> **Aside: the fork everyone trips over.** The root `Cargo.toml` asks for
39> rmcp 3.5.0, the Rust MCP library, and then patches it with a commit on
40> `deizel/rust-sdk`, a private fork. rmcp 3.5.0 refuses every request
41> after the way Claude Code 2.1.287 opens a stdio server (`server/discover`,
42> then `initialize`), so without the patch the MCP server connects and
43> offers no tools. The fork carries one fix on top of the `rmcp-v3.5.0` tag.
44> It is fetched over SSH and is not public yet, and Cargo resolves the
45> whole workspace before building any part of it: without access, even
46> `cargo test -p jevhooks-events`, which does not use rmcp, stops at
47> fetching it.
48
49> **Try it.** With access to the fork, `cargo test` from the repository
50> root runs every crate's tests. None of them needs the network, a Jev
51> key or a daemon: the verdicts are tested on made-up probabilities, and
52> the routing on made-up events.
53
54The chapters that follow take them in the order an event meets them: the
55vocabulary (7 and 8), the routing in the mod (9 and 10), the daemon (11 and
5612).
57
58## For the people who maintain it
59
60All three are version `0.1.0`, edition 2024, `publish = false`, and take
61every dependency's version from the root `Cargo.toml`'s
62`[workspace.dependencies]`. The release profile there (`opt-level = "s"`,
63`lto`, `panic = "abort"`, one codegen unit) applies to the wasm build and
64to the daemon alike.
65
66### In this folder
67
68| Crate | What |
69| --- | --- |
70| [jevhooks-events/](jevhooks-events/) | Chapters 7 and 8: every hook event, its role, the verdict and the decision. Depends on `serde` only. |
71| [jevhooks-mod/](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. |
72| [jevhooks-daemon/](jevhooks-daemon/) | Chapters 11 and 12: the `jevhooks` binary. |
73
74← Previous: [Chapter 5, tests/](../plugin/tests/) · Up: [jevhooks](../) · Next: [Chapter 7, jevhooks-events/](jevhooks-events/) →