Skip to content
Hydra Engine

Imports

Importing binaries through the daemon. SHA-256 deduplication, parallel imports with maxImports, a visible import queue, cancelling, and what a crash leaves.

On this page

import loads a file into a writable project with Ghidra's ProgramLoader, saves it, and opens it writable for the caller. Any format a Ghidra loader accepts works: PE, ELF, Mach-O, raw binaries and the rest.

s.call("open_project", path=r"C:\re\Work.gpr", writable=True)
r = s.call("import", file=r"C:\samples\target.exe", analyze=True)

With "analyze": true it runs auto-analysis the way analyzeHeadless does and saves again.

Never twice

A file whose SHA-256 is already in the project is not imported again. The answer is {"imported": false, "existing": [...]}, with the path of the program that is already there. Pass "duplicate": true to import a second copy anyway.

If two clients send the same file into the same project at the same moment, it is imported once: the second waits for the first to save, then answers imported: false with sameFileImport, saying whose import it waited for. If the first one fails or is cancelled, the second imports the file itself.

Several at once

on main · 12.3-hydra.2 Up to maxImports imports load and save at the same time, in one project or several. The default is one per four CPUs, between 1 and 4. Each also holds a long-lane slot, so at most min(maxImports, maxLong) run at once.

From the engine's README, each file into its own project, all sent at once (Windows 11 system binaries, 24 threads, 64 GB, a Dev Drive):

FilesmaxImports 1maxImports NPeak heap, 1 / N
4 PEs, load only (12.5 to 24.8 MB each)165.1 s63.9 s1.5 / 3.0 GB
2 random 128 MB raw files15.6 s8.8 s0.8 / 0.9 GB
2 PEs of about 10 MB, with analysis340.4 s320.1 s5.7 / 5.9 GB

With analysis the gain is small, because analyses already ran side by side: the import gate covers only the load and the save. Readers of the project are not slowed beyond noise: a decompile loop measured 5.7 ms median before two imports and 4.9 ms during them.

The queue

Imports beyond maxImports wait first-come, first-served, before they take a scheduler slot, so a queue of imports never blocks the analysis and scripts that also need long slots. The queue is visible:

  • status.imports lists what runs (who, project, file, size, time so far) and what waits, with each entry's position and estimatedWaitMs.
  • A queued import sends progress events, at most one a second, such as waiting behind client 2 (agent-a, claude-code) import of big.exe (position 1 in the import queue, about 40 s).
  • The final answer repeats how long it waited, in queue.

The estimate uses the last 12 finished imports' time per byte, so it is a guide, not a promise. In one measured pair the queue estimated 7.0 s and the wait was 17.5 s, because the import ahead ran slower than usual. Until one import has finished in a daemon, the estimate is null.

A queued import waits until it runs, unless you send a numeric wait; then it fails with busy after that long, with the queue details.

Cancelling and crashes

cancel by request id, or cancel with "all": true, takes a queued import out of the queue at once, and the running ones carry on. Cancelling a running import stops it at the loader's next check, frees its memory and temporary files, and leaves nothing in the project; the next queued one starts.

A daemon killed during an import leaves no partial program. The program reaches the project only in its final write. If the daemon dies during that write itself, Ghidra removes the half-written item the next time the folder is listed, so the program is either complete or absent, and the file imports again. The engine's tests kill the daemon at the first sight of the new database and part-way through the write to check this.

Limits

  • Ghidra's library search path, which loaders use to find a binary's libraries, is one per process, not per project. A script that changes it changes it for every later import.
  • Analysis results depend on time when the CPU is oversubscribed: timed analyzers give up. Under a saturated machine one measured analysis found 134,506 symbols instead of 190,423. Keep maxLong within the CPUs you can spare.
  • Listing or opening files of a project waits while an import writes its new program into that project. Decompiles of programs already open do not.

Checked against the engine on 2026-10-26. Every detail is in the README on GitHub.