Changelog
Changelog
Copied from CHANGELOG.md on main at ef708a8. Its Unreleased section is on main and not in a release yet.
What Hydra Engine adds on top of Ghidra. Ghidra's own changes are in its change history.
A version is the Ghidra version a release is built on, then -hydra. and a counter (for
example 12.3-hydra.1); its tag is v and the version. Releases are on
GitHub.
Unreleased
Not yet released; a release renames this heading to its version and names the upstream commit it is based on.
Added
-
Holder information in
busy,conflictandnot_owner(HeadlessSession). The error details name who holds the project, program or lock:holderInfo(client,name,harness,label,command,heldMs; for another process, the user, host and time from Ghidra's project lock file and whether that process is alive), the project or program identity (project,gpr,programkey,path,sha256), andsuggest:retry,fork_project(with a readysuggestionrequest) where a working copy is the answer,share(join a shared program),reread(a value changed since it was read), oropen_read_only.holderstays a string as before; everything else is new fields. -
fork_project(HeadlessSession): copies a project into a working copy for the caller while the source and its read-only views stay open and in use. A block clone on ReFS (Windows 11 24H2 and later), else a full copy;modeauto(clone, or a full copy up toforkCopyLimitMb, default 2048),copy-on-write(clone or refuse) orfull. The answer has the new project id, the paths, the source's identity and base revision,method,cloned(measured),bytes,filesand times. The fork records its source, base revision and lineage in<name>.fork.jsonnext to its.gpr, shown asforkbyproject. A source another client or process writes is refused, naming it. New launcher settingforkCopyLimitMb(GHIDRA_SESSION_FORK_COPY_LIMIT_MB), daemon option-forkCopyLimitMb. -
Revisions and
refresh(HeadlessSession): every project and program has arevision(a fingerprint of the saved files), shown byopen_project,project,projects,open_program,info,saveandstatuswithdiskRevision,staleandgeneration. Answers carrystale(a read-only view whose project another process saved) and, once,refreshed(another client refreshed it). New commandrefresh(aliasreopen) reopens a read-only view for all its clients at once, under the same keys. The Python client gainscall_rawandlast_response. -
Integration contract (
Ghidra/Features/HeadlessSession,support/ghidra_session.py):hello,statusand the endpoint file now carryprotocol,minClient,hydraVersion,ghidraVersion,installIdandstartedAt; the Python client has aCLIENT_VERSION(1.0.0) and refuses, withIncompatibleError(exit code 4 on the command line), an engine of another protocol or one whoseminClientis newer than itself;statusandstopstill work on such an engine.ghidra_session.py versionprints the client's version. README: "Pinning and upgrading", and a compatibility statement for projects analysed with Ghidra 12.1.4. -
Client release artifact: the release workflow also publishes
hydra-client-<version>.zip(the standard-library-onlyghidra_session.pystamped with the release,LICENSE, a shortREADME.txt), listed inSHA256SUMS..github/scripts/package-client.shbuilds it and checks the packaged file; CI runs it as a dry run. -
Status shows requests: each client in
statuslists its unanswered requests (id,cmd,artifact,ms,message,progress,maximum) and itsclientVersion. -
Script exports:
scripttakes an optionalartifact(a path the script writes), echoed instatusand in the result;examples/scripts/ExportJsonl.javais a sample export with progress and an inspectable partial file; README "Scripts" states the export contract (output outside the project, not capped bymaxChars, partial files after cancel and shutdown). -
Concurrency documentation: README "Who can do what at the same time": readers, writers, projects, imports, and what
conflict,busyandnot_ownermean. -
Tests:
examples/contract_tests.py(identity fields, client compatibility, a 600,000-line export with progress, cancel and shutdown), run by CI and the release workflow. -
The import queue is visible and cancellable. Imports that cannot run yet (one at a time when this was added; up to
maxImportssince "Imports run in parallel" below) queue first-come, first-served without holding a scheduler slot.status.importsshows the holder (client label, project, file, running time), the queue with each entry's position and an estimated wait (from the durations of earlier imports), and the history. A queuedimportsendsprogressevents "waiting behind ..." and its answer reportsqueue(waitedMs,positionAtEntry,behind). Cancelling a queued import removes it without touching the running one; cancelling the running one starts the next. A numericwaitnow limits the queue wait; without one a queued import waits until it runs or is cancelled. -
Per-request
clientandharnesslabels. Any request may carry them; they replace the connection's name for that request inbusy/conflictholders (holderandholderInfo),status(requests, scheduler lanes, owner of a writable program,youAre), the import queue and the log, and they scopecancel(allreaches only that label's requests;targetrefuses another label's request). Ownership is unchanged (still per connection). One MCP server serving several agents can now tell which agent holds a program or an import.ghidra_session.py: labels on anycall, andlabelled(client=, harness=)views withcancel_all(). -
daemon_tests.py:import_queue,import_isolation,caller_labels;--raw-mb. -
Shared writing (HeadlessSession):
open_programwith"writable": true, "share": truelets several clients write one program at once. Each change is its own transaction, committed one at a time under the program's exclusive lock and visible to all; last writer wins, andexpecton renames, labels and comments (also aseditoperations) turns a lost update intoconflict(reason: "changed") naming the last editor. Every change to a writable program is journalled with its author (journal;journalSeq,unsavedEdits,savedSeq); new commandsedit(operations, all or none) anddiscard_edits(revert one author's unsaved renames, labels, comments). A writer that dies loses only its uncommitted change; when it was the last writer, the unsaved work is kept (program and project stay open) until a client joins and saves or discards it. Owner and shared mode do not mix on one program. Tested with 4 clients x 2 threads, 3,200 edits and 64 saves: none lost, journal complete, reopen clean. -
Several writers in one project (HeadlessSession):
open_projectwritable while other clients use the read-only view now reopens it writable in place (same id and keys, their programs reopened, overlays rebuilt,refreshedonce) instead ofconflict; refused only when another process holds the project lock. A second writable open joins; writers of different programs of one project work in parallel (tested with concurrent renames, comments, analysis and saves).not_owneraddsalternatives: ["overlay", "fork_project"]and anoverlaySuggestion. -
Overlays (HeadlessSession): a client's private annotation view of a shared program.
open_programwithoverlayopens one: its own program object over the stored file (changeable, never saved), measured at 116 ms and +54 MB heap for a 243,855-function program.apply_overlayappliesrename,label,comment,signatureanddata_typeoperations atomically and logs them;info,functions,decompile,script(contained),renameandcommenttakeoverlay;drop_overlay,overlays;promote_overlayreplays the log into a writable copy (a project the client opened writable, or a new fork) and saves it. Each overlay showsoverlayVersion, the program's SHA-256 and revisions, in its answers,infoandstatus.overlays. A refresh rebuilds overlays from their logs; a client's disconnect drops its overlays.
Changed
- Imports run in parallel (HeadlessSession). Up to
maxImportsimports (new launcher setting and daemon option-maxImports; default one per four CPUs, 1 to 4) run at once, into different projects or into one; more queue as before. Imports no longer set the process-wide active project: nothing on Ghidra's load path reads it (the README's "Parallel imports" lists what the load path shares and why it is safe): four 12-25 MB PEs imported at once took 64 s against 165 s one after the other, with identical programs. The gate covers the load and the save;analyze: trueanalyses afterwards in the import's long slot, as before. The same file sent twice at once into one project is imported once: the second waits and answersexisting, withsameFileImport.status.importsgainsmaxImports,runningandhistory.peakRunning(holderis the oldest running import); an import'squeuegainsrunningAtStart. Opening an upgrade cache copy no longer goes through the import gate. daemon_tests.py:parallel_imports,import_kill,import_readers,import_speed(--big,--big-analyze);import_queueruns withmaxImports1.- The project files the upgrade cache keys and copies leave out Ghidra's transient files in a
.rep(tmp<n>.pspre-save copies of programs open for update,tmp<n>.tmp), so that a project another process has open for update keeps its identity until it is saved. Cache entries made before this are rebuilt once. - The
busydetails of a program lock carryprogramas the program's key (p1:/path), like every other error, instead of its project path (now inpath).
12.3-hydra.1
Released 2026-10-26. The first release. Based on upstream Ghidra master (12.3 DEV) as of
2026-10-26, commit 057a646233edfb21768a42a0aabc1d1a7709da19.
Downloads
One zip, hydra-engine-12.3-hydra.1-win_x86_64.zip: the headless install for Windows x64
(gradle zipHeadless), on which the release workflow ran both test suites after unzipping it.
You need a JDK 25 and Python 3.10 or later; the install has no JDK.
Known limits
- Projects saved by this build cannot be opened by Ghidra 12.2 or 12.1.4. Upstream
masterwrites data type databases at version 3; 12.2 and 12.1.4 read version 2. A project opened read-only is not written; open older projects writable only from a copy. Thelegacy-12.1.4branch serves projects that must stay on 12.1.4. - Based on a development snapshot. Upstream
masteris Ghidra's next version under work (12.3 DEV), not a Ghidra release. - Windows only. CI and releases build and test on Windows. The module and launcher have no
Windows-only path, and a one-off dry run of the release workflow on 2026-10-26 built Linux x64
and macOS arm64 installs that passed both test suites, but neither is released or tested
routinely; build from source there (
gradle buildHeadless). - Headless only. No window, help, javadoc or Python type stubs; the full Ghidra build is
still
gradle buildGhidra, from source.
Added
- HeadlessSession module (
Ghidra/Features/HeadlessSession): a shared, per-user headless session daemon. One JVM serves many projects and many clients over JSON lines on an authenticated loopback socket (or stdio), with protocol version 2.- Projects opened in place: read-only without a lock, or writable with one.
- Programs opened and switched without importing; imports with any Ghidra loader, deduplicated by SHA-256.
- Analysis, decompiling, renames, comments, saves, and GhidraScripts (Java or Python) with arguments; scripts on programs a client does not own run contained and are rolled back.
- Fair scheduling (quick and long lanes, round-robin by client), one writer and many readers per program, per-client cancel, clean release when a client dies, idle shutdown, warm programs kept open for the next client.
ghidra_session.py: a standard-library-only Python launcher, daemon manager and client (connect()), with no.bator.shlauncher. Fast start with a class search cache and, on JDK 25+, an AOT cache.- Tests:
examples/session_smoke.pyandexamples/daemon_tests.py. - Headless build (
gradle buildHeadless,gradle/hydra/headless.gradle): an install without the window-only modules, the help, javadoc and Python stubs, and with a check that no module of the install depends on one it leaves out. The full build is stillgradle buildGhidra. - Repository: this README (Ghidra's original is
README-GHIDRA.md),NOTICE-HYDRA, a security policy and contributor guide in.github/, issue and pull request templates, and CI (.github/workflows/ci.yml) that builds the headless install and runs both test suites. - Releases (
.github/workflows/release.yml): a tagv<version>builds the headless zip on Windows, runs both test suites on the unzipped install, and publishes the zip,SHA256SUMSand this file's section as a GitHub Release.
Branches
main: the above, on upstreammaster.legacy-12.1.4: the same module on Ghidra 12.1.4, kept for projects analysed with 12.1.4.