The pyghidra-mcp wrapper bakes in GHIDRA_INSTALL_DIR via makeWrapperArgs
referencing pkgs.ghidra, which makes ghidra a runtime closure dep.
Adding pkgs.ghidra explicitly to home.packages caused buildEnv to merge
*two* ghidra-12.0.4 store paths (one from pyghidra's propagatedBuildInputs,
one from the explicit list) and fail with a path collision on
GPL/DMG/LICENSE.txt.
Drop the explicit add. The agent-driven workflow doesn't need the GUI;
manual exploration via 'nix run nixpkgs#ghidra' is one command away if
ever wanted.
Adds two inline Python derivations to home/progs/pi.nix:
- ghidrecomp 0.5.9 (clearbluejar/ghidrecomp) — required by pyghidra-mcp,
not in nixpkgs.
- pyghidra-mcp 0.2.2 (clearbluejar/pyghidra-mcp) — headless MCP server
that exposes Ghidra's analysis primitives (decompile, disassemble,
list_strings, get_xrefs_to, etc.) over Model Context Protocol stdio.
The wrapper bakes in GHIDRA_INSTALL_DIR=${pkgs.ghidra}/lib/ghidra so
pyghidra discovers the Ghidra install at runtime without env munging.
Wires into OMP via:
- home.packages: pyghidra-mcp + pkgs.ghidra (GUI for occasional manual
exploration alongside the agent-driven flow).
- ~/.omp/agent/mcp.json: registers a 'ghidra' MCP server that spawns
pyghidra-mcp on stdio when any of its tools are invoked.
- ~/.omp/agent/skills/ghidra/SKILL.md: tells the agent when to reach
for Ghidra (static binary RE) vs. usbmon (dynamic capture) vs. the
built-in tools, and gives the canonical exploration workflow.
Replaces the previously-recommended LaurieWired/GhidraMCP, which has
been stale since June 2025. clearbluejar/pyghidra-mcp is actively
maintained (last commit 3 days ago), pure-Python via pyghidra+jpype, and
multi-binary capable in a single session.
Verified: pi.nix parses, the yarn NixOS closure evaluates, both
derivations build, and the wrapped binary's --help works (Ghidra runtime
discovered correctly via GHIDRA_INSTALL_DIR).
`bun2nix.hook` (used by upstream omp's package.nix) sets
patchPhase = bunPatchPhase
at the end of its setup-hook unless `dontUseBunPatch` is already set.
`bunPatchPhase` only runs `patchShebangs` plus a HOME mktemp; it never
iterates over `$patches`. The standard nixpkgs `patches` attribute
therefore went into the derivation env but was silently ignored at
build time, leaving the deployed omp binary unpatched.
Switch to applying the two patches via `prePatch` (which `bunPatchPhase`
does call). Verified with strings(1) over the rebuilt binary that both
patch hunks land:
/wrong_api_format|...|invalid tool parameters/ (patch 0001)
stubsReasoningContent ... thinkingFormat == "openrouter" (patch 0002)
DeepSeek V4 Pro (and similar reasoning models reached via OpenRouter) reject
multi-turn requests in thinking mode with:
400 The `reasoning_content` in the thinking mode must be passed back
to the API.
omp's existing kimi placeholder injection (`requiresReasoningContentForToolCalls`)
covered this requirement only for `thinkingFormat == "openai"`. OpenRouter
sets `thinkingFormat == "openrouter"`, so the gate never fired even though
the underlying providers behind OpenRouter (DeepSeek, Kimi, etc.) all enforce
the same invariant.
This patch:
1. Extends `requiresReasoningContentForToolCalls` detection: any
reasoning-capable model fronted by OpenRouter now sets the flag.
2. Extends the placeholder gate in `convertMessages` to accept
`thinkingFormat == "openrouter"` alongside `"openai"`.
Cross-provider continuations are the dominant trigger: a conversation
warmed up by Anthropic Claude (whose reasoning is redacted/encrypted on
the wire) followed by a switch to DeepSeek V4 Pro via OpenRouter. omp
cannot synthesize plaintext `reasoning_content` from Anthropic's
encrypted blocks, so the placeholder satisfies DeepSeek's validator
without fabricating a reasoning trace. Real captured reasoning, when
present, short-circuits the placeholder via `hasReasoningField` and
survives intact.
Side benefit: also closes a latent gap where Kimi-via-OpenRouter
(`thinkingFormat == "openrouter"`) had the compat flag set but the
placeholder gate silently rejected it.
Applies cleanly on top of patch 0001.
new site-config.nix holds values previously duplicated across hosts:
domain, old_domain, contact_email, timezone, binary_cache (url + pubkey),
dns_servers, lan (cidr + gateway), hosts.{muffin,yarn} (ip/alias/ssh_host_key),
ssh_keys.{laptop,desktop,ci_deploy}.
threaded through specialArgs on all three hosts + home-manager extraSpecialArgs +
homeConfigurations.primary + serverLib. service-configs.nix now takes
{ site_config } as a function arg and drops its https namespace; per-service
domains (gitea/matrix/ntfy/mollysocket/livekit/firefox-sync/grafana) are
derived from site_config.domain. ~15 service files and 6 vm tests migrated.
breakage fixes rolled in:
- home/progs/zen/dark-reader.nix: 5 stale *.gardling.com entries in
disabledFor rewritten to *.sigkill.computer (caddy 301s the old names so
these never fired and the new sigkill urls were getting dark-reader applied)
- modules/desktop-common.nix: drop unused hugepagesz=1G/hugepages=3
kernelParams (no consumer on mreow or yarn; xmrig on muffin still reserves
its own via services/monero/xmrig.nix)
verification: muffin toplevel is bit-identical to pre-refactor baseline.
mreow/yarn toplevels differ only in boot.json kernelParams + darkreader
storage.js (nix-diff verified). deployGuardTest and fail2banVaultwardenTest
(latter exercises site_config.domain via bitwarden.nix) pass.