Skip to content

Replace legacy backends with ida-nexus and migrate agent guidance - #45

Open
Ninja3047 wants to merge 19 commits into
mainfrom
mega-rewrite
Open

Ninja3047 wants to merge 19 commits into
mainfrom
mega-rewrite

Conversation

@Ninja3047

@Ninja3047 Ninja3047 commented Oct 2, 2026 •

Copy link
Copy Markdown
Collaborator

This replaces idac's bundled GUI bridge and custom headless daemon with ida-nexus for both live IDA sessions and managed headless workers. Users upgrading from 0.19 need to update target selectors, installation commands, and scripts that depend on the old lifecycle or persistent Python state.

Breaking changes and migration

The supported stack is now Python 3.11+, IDA Pro 9.4+, ida-nexus >=0.13.2, and ida-domain >=0.5.1. setup gui installs the GUI component matching the installed Nexus client through the runtime ida-hcli dependency.

Previous usage Replacement
-c db:sample.i64 -c sample.i64, or an existing binary path
-c pid:1234, module:, or a module name --instance RECORD_ID from targets list --json
.idb databases Convert to .i64 first
misc plugin install setup gui, then restart IDA or load the Nexus component
misc skill install Install the separate Agent Plugins v1 package through the client's marketplace
idac docs Subcommand --help, --full-help, and installed skill references
database open/close, targets cleanup Select a path with -c; Nexus manages worker lifecycle
database save DESTINATION database save checkpoints the selected database
py exec --persist Combine dependent Python work into one stateless invocation

targets list --json now returns Nexus discovery records with record_id, state, and database/input paths. With no selector, exactly one READY instance is required. A path can attach to a matching GUI database or reuse/start a headless worker. Selection, compatibility, and execution failures propagate without switching targets or retrying the operation.

Other user-facing changes

  • Headless opens wait for auto-analysis. Workers stay warm for five idle minutes; successful mutations checkpoint before the next remote request or lease release. Earlier successful batch steps remain saved if a later step fails. GUI saves remain explicit.
  • batch and preview own the target, timeout, and mutation artifacts. Children cannot override the target or timeout, and mutating batch children cannot set --out. Batch journals capture progress, interruption, and save/session-close failures.
  • Previews restore changes with IDA undo or operation-specific rollback. Failed previews and locally interrupted headless requests discard the affected worker without saving uncertain state. Output files cannot replace selected databases or command inputs.
  • function prototype set --preserve-cc keeps the stored calling convention. type check validates dependent declarations together without changing database types. misc rename supports batches and previews. Unfiltered struct/enum lists require --out.
  • Agent guidance is distributed separately from the Python package. Workspace scaffolding uses one shared AGENTS.md for Claude and Codex and points to the installed skill. Its preview and verification guidance now matches the skill's task-scoped workflow.
  • doctor compares installed idac Agent Plugin versions with the CLI version through available Codex and Claude clients. Mismatches warn with both versions without making the optional skill a runtime dependency.

The README includes an upgrade guide. The unreleased changelog, development docs, plugin references, and workspace prompt are updated consistently.

Validation

  • 187 unit tests passed on the final tree.
  • All 103 headless integration cases passed on a clean GitHub runner with IDA 9.4 and Python 3.12 after a successful Nexus startup probe (CI run). They also passed locally with isolated profiles; the five existing workspace tests passed after the scaffolding update.
  • The optional live GUI lifecycle test was skipped.
  • Formatting, Ruff, and type checks passed. Actionlint passed for CI and prepare-release; Zizmor found no issues in the migrated release workflow.
  • Checked 45 local documentation links and 151 CLI examples and batch-template commands against the current parser.
  • Merged current main and resolved the CI conflict while retaining its current action versions.
  • Integration CI keeps a version matrix starting at IDA 9.4, Nexus's minimum, with installer IDs per version so newer releases can be added when available. The check uses the existing Test (IDA 9.4) name.
  • CI installs IDA through the project's HCLI dependency and pinned uv setup action, avoiding the Hex-Rays wrapper's unpinned nested actions. A Nexus worker probe verifies startup before the integration suite.

Release

Changelog generation now uses the pinned Codex GitHub Action with OPENAI_CODEX_API_KEY, replacing Claude. The existing prompt still limits the release notes to user-visible changes. Actionlint validates the migrated workflow; a live changelog run is deferred until release preparation. The secret must be available to this repository through Actions secrets.

After this PR merges, prepare 0.20.0 through the release workflow on main:

gh workflow run prepare-release.yml --ref main -f version=0.20.0

The workflow updates the package, lockfile, and Agent Plugin versions and opens the release PR with the versioned changelog. Merging that release PR through the queue publishes the release.

@KernelClint

Copy link
Copy Markdown
Member

Tried this out on a firmware RE project that leans on idac heavily. We're blocked
from functional testing by the IDA 9.4+ requirement, but the attempt surfaced a few
things that may be worth addressing.

Environment

macOS 26.6.2, arm64
IDA Professional 9.3.260421.be7de18d
Python 3.13.14
branch mega-rewrite @ 248d141
installed idac 0.19.1, ida-nexus 0.13.2, ida-domain 0.5.1, ida-hcli 0.26.2

Installed with uv pip install -e . into a clean venv, IDAC_RUNTIME_DIR pointed
inside the workspace.

The version gate works, but doctor doesn't check for it

idac -c fixtures/idb/tiny.i64 function list fails correctly:

  idalib worker launcher 26692 exited with status 1
  [ida-nexus] IDA Nexus requires IDA 9.4 or newer

So the requirement is enforced. But idac doctor — the command whose job is
diagnosing the environment — doesn't check the IDA version. It runs:

  - [ok]    runtime.python
  - [ok]    runtime.idac
  - [ok]    runtime.ida_nexus
  - [ok]    runtime.ida_domain
  - [ok]    runtime.ida_hcli
  - [error] gui.plugin
  - [warn]  agent.codex
  - [warn]  nexus.discovery

It reports healthy: False for an unrelated reason (gui.plugin) and never mentions
the one hard requirement that actually blocks the install. On a 9.3 machine the first
signal a user gets is a worker exiting with status 1 during their first real command.
A runtime.ida check alongside the other five would turn that into a one-line answer.

The failure presentation buries the reason

The useful line (requires IDA 9.4 or newer) comes after a launcher PID and exit
status, and in our case after two unrelated RuntimeWarning: Unexpected value in sys.prefix lines from <frozen site>. Surfacing the version mismatch as the error
itself, rather than as worker output, would help.

agent.codex can cost 10s

  [warn] agent.codex: could not check the installed idac skill in codex:
  Command '['codex', 'plugin', 'list', '--json']' timed out after 10.0 seconds

Shelling out to optional third-party CLIs means doctor pays their timeout when they
are installed but not working — which is a fairly common state. Probably worth
shortening or making opt-in.

Install hits supply-chain cutoffs

ida-nexus 0.13.2 was published 2026-10-01. Anyone running uv with an exclude-newer
policy gets:

  Because only ida-nexus<=0.13.0 is available and idac==0.19.1 depends on
  ida-nexus>=0.13.2, we can conclude that idac==0.19.1 cannot be used.

Correct resolver behaviour, but the message points at idac rather than at the policy.
Not much you can do in code; may be worth a line in the migration notes.

Checked and fine: exit codes are correct — doctor and function list both
return 1 on failure. (We initially misread this through a pipe; head was masking the
status.)

Happy to retest once we're on 9.4 if that's useful — the migration table in the PR
body covers everything we actually use (-c db:, database open/close, py exec --persist).

@KernelClint

Copy link
Copy Markdown
Member

Followed up on the retest offer — we upgraded to IDA 9.4.260915 and ran this against real work. Short version: it works, and the worker lifecycle change fixes a problem that has cost us real time.

Same environment as above, now on IDA Professional 9.4.260915.

Exercised on ~300 big-endian AArch64 ILP32 firmware databases: function list, imports, xrefs, decompile, py exec --code, database save, targets list --json. Decompiler output on BE ILP32 objects is correct. Opening a raw binary directly with -c <binary>, with no pre-made database, is a genuine simplification for us. Unpacked databases (.id0/.id1/… beside the original binary) open fine. The inline output cap with the "rerun with --out" hint is well judged for agent use.

The worker lifecycle is the headline for us. Our repo has database open outnumbering database close 135 to 93 across sessions, orphaned idalib_server processes, and — when we went looking this week — 442 stale idac-idalib-open-*.lock files to clear. Under this PR, five successive commands reused one worker, and it self-terminated on its own after the 300s keepalive. That removes the whole class of problem for us.

Nothing to add to the earlier findings; the doctor IDA-version gap is moot on a 9.4 machine but would still have saved us the detour on 9.3.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants