# Product Requirements Document ## ghidra-cli - Status: Draft - Revision: 0.1 - Audience: maintainers and early users ## Summary `ghidra-cli` is a small Rust command-line interface for reproducible, read-only analysis and decompilation through Ghidra. It hides Ghidra project management and Java process details behind commands that accept a sample path directly. The first release favors a narrow, reliable workflow over a broad command surface. It is intended to work equally well for a person at a terminal and an automation agent consuming structured output. ## Problem Ghidra supports headless analysis, but its native interface exposes concepts such as project directories, project names, import and process modes, script paths, and JVM configuration. Answering a simple question about a sample therefore requires orchestration knowledge unrelated to reverse engineering. Existing wrappers commonly address this by becoming persistent services with background JVMs, sockets, job queues, project lifecycle commands, custom query languages, and mutating operations. Those capabilities are useful in mature interactive systems, but they increase the number of states that can fail and make a small tool harder to trust. ## Product principles 1. A sample path is enough. Users do not manage Ghidra projects. 2. Read-only by default and throughout the first release. 3. Content determines identity. Moving or renaming a sample does not invalidate compatible analysis. 4. Structured output is a public interface, not formatted log text. 5. Every failure is explicit, typed, and actionable. 6. No background process is required for the initial command set. 7. Concurrency is introduced only with a concrete concurrent workflow. 8. Ghidra remains the analysis engine; Rust owns orchestration and policy. ## Users ### Human analyst Wants to inspect an unfamiliar executable, discover functions, and decompile a selected function without opening the Ghidra GUI or learning its project model. ### Automation agent Needs deterministic commands, bounded output, stable JSON schemas, meaningful exit codes, and enough provenance to explain where a result came from. ### Maintainer Needs a small dependency graph, reproducible fixtures, a clear Java boundary, and compatibility failures that are discovered by tests rather than users. ## Goals for the first release - Diagnose whether the local Ghidra and JDK installation is usable. - Inspect basic sample and analysis metadata. - List discovered functions with stable identifiers. - Decompile one selected function. - Reuse compatible analysis without requiring user-managed project names. - Emit concise terminal output and versioned JSON. - Bound Ghidra execution by configurable time and memory limits. - Operate without network access after dependencies are installed. ## Non-goals for the first release - Replacing Ghidra's decompiler or analysis engine. - A daemon, TCP bridge, HTTP service, or MCP server. - Multiple concurrent Ghidra analyses. - An interactive shell. - Binary patching or exporting modified executables. - Renaming symbols, applying types, or changing function signatures. - Arbitrary Java or Python script execution. - A custom filtering or query language. - Automatically downloading Ghidra or a JDK. - Managing user-visible Ghidra projects. - Supporting every historical Ghidra version. ## Command-line experience ### Installation diagnosis ```console $ ghidra-cli doctor Ghidra 12.1: ready JDK 21: ready Analysis store: writable ``` `doctor` must not modify a Sample or create an Analysis. It may create and remove a temporary file inside the Analysis Store to verify writability. ### Inspect a sample ```console $ ghidra-cli inspect ./sample Sample: 8d969eef... Format: ELF Language: x86:LE:64 Functions: 42 ``` If no compatible Analysis exists, the command creates one automatically. The same bytes at another path reuse the Analysis. Different bytes at the same path create a different Analysis. ### List functions ```console $ ghidra-cli functions ./sample ADDRESS SIZE NAME 0x00401000 112 _start 0x00401120 248 main ``` The command supports an explicit result limit. It does not implement a filtering expression language in the first release; JSON consumers can use existing tools such as `jq`. ### Decompile a function ```console $ ghidra-cli decompile ./sample main int main(int argc, char **argv) { ... } ``` A function may be selected by address or a uniquely resolved symbol. An ambiguous symbol is an error and returns the candidates instead of silently choosing one. ### Structured output ```console $ ghidra-cli --format json inspect ./sample ``` JSON uses a versioned envelope: ```json { "schema_version": 1, "kind": "inspection", "provenance": { "sample_sha256": "", "ghidra_version": "", "analysis_profile": "" }, "data": {}, "warnings": [] } ``` Standard output contains only the requested result. Diagnostics and progress belong on standard error. ## Functional requirements ### Sample identity - Compute a SHA-256 digest from the Sample bytes. - Detect if the Sample changes while it is being read. - Treat identical bytes as the same Sample regardless of path. - Never write to the Sample. - Record the observed path as provenance, not identity. ### Analysis compatibility An Analysis is reusable only when its Sample digest and Analysis Profile match. At minimum, the Analysis Profile includes: - Ghidra version. - Ghidra language and compiler specification. - Enabled analyzer configuration. - Java adapter protocol version. An incompatible Analysis is preserved until normal retention policy removes it; it is not overwritten in place. ### Analysis execution - Invoke the official Ghidra headless launcher as a child process. - Pass requests to a small, bundled Java adapter. - Exchange structured request and response files rather than scrape logs. - Capture Ghidra logs separately for diagnostics. - Terminate the child process after each command in the first release. - Apply a configurable wall-clock timeout. - Report whether termination was graceful or forced. ### Analysis Store - Use a platform-appropriate user data location by default. - Allow an explicit store path through one documented option. - Use atomic writes for manifests and Artifacts. - Detect incomplete Analysis creation after interruption. - Serialize writers for the same Sample and Analysis Profile. - Treat the entire store as disposable. ### Output behavior - Default to concise human-readable output on a terminal. - Support explicit JSON output. - Never change JSON shape based on terminal detection. - Include schema version and provenance in every JSON response. - Use hexadecimal addresses with an explicit address space where necessary. - Place progress and logs on standard error. ### Errors and exit status Distinct error categories must cover: - Invalid arguments. - Missing or unreadable Sample. - Unsupported or ambiguous format/language. - Missing or incompatible Ghidra/JDK installation. - Analysis timeout. - Ghidra analysis failure. - Function not found or function selector ambiguous. - Invalid or incompatible cached Analysis. - Internal protocol violation. The exact numeric exit-code allocation is a design task before implementation. ## Architecture boundary ### Rust CLI responsibilities - Argument parsing and validation. - Sample hashing and stable identity. - Analysis Profile construction. - Analysis Store layout, locking, and atomicity. - Child-process lifecycle, timeouts, and log capture. - Typed request and response schemas. - Human and JSON rendering. - Exit-code policy. ### Java adapter responsibilities - Use supported Ghidra APIs. - Import and analyze a Sample. - Resolve Function Selectors. - Extract program and function metadata. - Invoke the Ghidra decompiler. - Serialize only the response defined by the protocol. The Java adapter must not listen on a network socket, download dependencies, or execute user-provided scripts. ## Synchronous execution decision The first release uses a synchronous command lifecycle because each invocation coordinates one Ghidra child process and returns one result. Ghidra performs the CPU-intensive work; an asynchronous Rust runtime would not make that work faster. The design must not prevent a future asynchronous frontend. A server, parallel batch analysis, interactive session, or responsive cancellation channel would constitute a concrete reason to add an async runtime. ## Security and safety - Treat every Sample and every Ghidra-produced value as untrusted input. - Do not execute the Sample. - Do not provide arbitrary script execution. - Do not require network access during analysis. - Recommend a sandbox or container for untrusted Samples. - Avoid including Sample bytes or extracted secrets in routine logs. - Validate all paths crossing the Rust/Java boundary. - Apply bounded output, memory, and execution-time policies. - Document the supported Ghidra version and relevant security advisories. ## Performance expectations Correctness and reproducibility take priority over startup latency in the first release. - A compatible cached Analysis should avoid repeating auto-analysis. - Metadata Queries should reuse exported Artifacts where correctness permits. - Targeted decompilation may start a new JVM. - The CLI should explain whether it reused or created an Analysis when verbose diagnostics are enabled. Persistent workers and interactive sessions are deferred until measured usage shows JVM startup to be the dominant usability problem. ## Test strategy - Unit-test Sample identity, profile identity, store layout, schema validation, selector parsing, and error mapping without Ghidra. - Test process lifecycle against a controllable fake child process. - Maintain tiny, source-controlled fixture programs with known properties. - Compile fixtures reproducibly for supported architectures. - Run integration tests against a pinned supported Ghidra release. - Compare JSON against reviewed golden fixtures after normalizing unstable values. - Verify interruption leaves no Analysis that can be mistaken for complete. - Verify identical bytes at different paths reuse one compatible Analysis. - Verify changed bytes at the same path do not reuse the old Analysis. ## Delivery milestones ### Milestone 0: design - Review this PRD and the domain language. - Choose the project and executable name. - Choose a license. - Pin the initially supported Ghidra and JDK versions. - Define protocol schemas and exit codes. ### Milestone 1: vertical inspection slice - Create the Rust crate and minimal Java adapter. - Implement `doctor`. - Implement `inspect`. - Prove Rust to `analyzeHeadless` to Java to JSON end to end. ### Milestone 2: function discovery - Implement `functions`. - Define stable address and Function Selector representations. - Add reproducible native fixtures. ### Milestone 3: targeted decompilation - Implement `decompile`. - Add ambiguity handling and decompiler time limits. - Stabilize the first public JSON schema. ### Milestone 4: packaging - Provide a reproducible Nix development and test environment. - Document installation and sandboxed use. - Publish the first pre-release. ## Release acceptance criteria The first release is acceptable when: - A new user can run `doctor`, `inspect`, `functions`, and `decompile` without manually creating a Ghidra project. - Repeating a Query against an unchanged Sample reuses compatible analysis. - All commands provide versioned JSON and documented exit behavior. - An interrupted analysis cannot be mistaken for a complete one. - Tests exercise a real pinned Ghidra release and known fixture binaries. - No first-release command mutates the Sample or Analysis. - No analysis command requires a network connection. ## Open decisions - Final repository and executable name. - License. - Initially supported Ghidra patch release. - Whether the first supported host is Linux-only. - Default Analysis Store retention policy. - Numeric exit-code allocation. - JSON Schema publication and compatibility policy. - Whether Nix is the sole supported installation path for the first pre-release.