Core Concepts
Which project, which program, and who may change it.
Almost every request answers two questions: what it's about, and whether its client may change that. This page is the vocabulary; Projects has the commands.
Projects and program keys
open_project answers with the project's id (p1, p2, ...), stable while it's open. A program's key is the project id and its path inside the project: p1:/libs/version.dll. Keys are unique across projects, so one client can hold programs from several projects at once.
Opening a project or program also makes it the client's active one. Commands without a project or program field act on it:
{"cmd": "open_project", "path": "/re/Game"} -> {"id": "p1", ...}{"cmd": "open_program", "path": "game.exe"} -> active program p1:/game.exe{"cmd": "decompile", "address": "rva:0x5d53a0"} -> runs on p1:/game.exe{"cmd": "decompile", "program": "p2:/tool.exe", "name": "main"}Addresses
| Form | Means |
|---|---|
0x140001000, va:0x140001000 | A virtual address in the default space. |
rva:0x1000 | An offset from the program's image base. |
space:offset | An address in a named space. |
Answers print addresses as 0x... in the default space.
Read-only views and writable projects
By default a project opens the way Ghidra's *File > View Project* opens one: no lock file, no index, no timestamp, nothing written. Any number of clients, and a Ghidra window, can read the same project at once, with no copy first.
Ask for writable: true and the project opens with its lock, as *Open Project* does. Only one process can hold that lock, but inside the daemon every client shares the one writable instance.
| Read-only view (default) | Writable | |
|---|---|---|
| Project lock | none | one, held by the daemon |
| Other processes | can read and write it | get conflict |
| Saves by other processes | flagged stale; refresh reopens | not possible |
Revisions
Every project and program has a revision: a 16-digit fingerprint of what's saved. When another process saves a project that a read-only view serves, every answer to that view's clients carries a stale entry. refresh reopens the view on what's saved now, for all of its clients at once, without anyone leaving:
{"id": 12, "ok": true, "result": {...}, "stale": [{"project": "p1", "revision": "5b0c9e2d81f4a7c3", "diskRevision": "e1d24f6b9a03c8d7", "suggest": "refresh"}]}Four ways to write
Several agents can change things at the same time, each in the way its work needs:
| Who sees the change | Saved | Good for | |
|---|---|---|---|
| Owner (default) | everyone reading the program | when the owner saves | one agent doing focused work |
| Shared writers | everyone, at once | when any writer saves | a team annotating one program |
| Overlay | only its client | never, unless promoted | trying out a hypothesis |
| Working copy | clients of the copy | into the copy | long or heavy work, bulk types, scripts |
A client that asks to write somebody else's program gets not_owner, with an overlay request and a working-copy request it can send as they are. Projects shows each one.
Warm by default
Each program keeps a pool of decompilers, so warm calls skip the start-up cost, and two clients decompiling the same program run in parallel. A read-only program nobody uses stays open for keepWarm seconds (an hour by default), so the next client gets it instantly. Writable projects close as soon as their last user leaves, releasing the lock.