Architecture
One body, many heads.
In the myth, the Hydra has one body and many heads. Here the body is one long-lived JVM holding Ghidra, its open projects, its analysed programs and its warm decompilers. The heads are the clients: agent harnesses, MCP servers, scripts. They come and go; the body stays.
The whole picture
heads body projects ----- ---- -------- Claude Code ----. +---------------------------+ .-> /re/Game (read-only view) Codex ----------+ | hello + token | | game.exe OpenCode -------+---->| quick lane | long lane |-----+ engine.dll Hydralysis -----+ | per-program locks | | your script ----' | warm decompiler pools | '-> /work/agent-a (working copy) +---------------------------+Clients speak JSON lines over a loopback socket. Requests take a slot in one of two lanes, then a lock on the program they touch. Programs stay open, and warm, between clients.
One daemon per user
The first client to connect starts the daemon; everyone after it finds the running one through an endpoint file in the per-user state directory. Its files there:
| File | Purpose |
|---|---|
default.json | The endpoint: port, token, install, versions. Written once the daemon listens, removed when it exits cleanly. |
default.lock | Held by the JVM for its whole life. Proves it's alive, and stops a second daemon of the same instance. |
default.start.lock | Held while starting, so two clients that find no daemon at the same moment start only one. |
default.log | The daemon's log, rotated at 32 MiB. |
default is the instance name. Pass --instance to run a second, independent daemon, for example from another install.
Security
- The socket binds to loopback only; other addresses are refused.
- The first request must be
hellowith the endpoint file's 256-bit token, compared in constant time. - The endpoint file is readable by its owner only.
Anyone who can read your state directory can drive the daemon with your rights, just as anyone who can run your Python can. A script is Java running inside the daemon, not a sandbox.
Two lanes
Quick requests (decompile, functions, list, open, rename) and long ones (analyze, import, script, save) run in separate lanes with their own slots. A whole-program analysis can't take the slots a 10 ms decompile needs, and within a lane, waiting requests are admitted round-robin by client. More in Scheduling.
When a head is cut off
A client that disconnects or crashes (the OS resets its socket) is cleaned up at once:
- its running and queued requests are cancelled;
- a change it hadn't committed is rolled back, and nobody else's;
- unsaved changes to programs it owned are discarded and logged;
- those programs go back to read-only for everyone else.
Other clients never notice, except that the program they were waiting for comes free. In shared mode, a writer's committed changes survive it; see Projects.
Still Ghidra inside
Hydra Engine is upstream Ghidra master kept in sync, plus one module, Ghidra/Features/HeadlessSession, two small fixes to Ghidra classes, and a headless-only build. There are no .bat or .sh launchers for the daemon: ghidra_session.py builds the classpath and starts the JVM itself.
A legacy-12.1.4 branch carries the same module on Ghidra 12.1.4, for projects that must stay on that version.
Measured
Windows 11, JDK 25, 24 threads; a 66.7 MB x64 executable with 243,855 functions, opened read-only. Medians.
| Time | |
|---|---|
| No daemon running: start it and connect | 533 ms |
| Daemon start to first decompile answer | 1,637 ms |
Warm decompile | 6.7-7.1 ms |
The same question through stock analyzeHeadless | 7.0-7.8 s |