diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-02 10:58:21 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-02 10:58:21 -0400 |
| commit | 002e3d7d13c414ea874abbee8d82803dd3a72fe5 (patch) | |
| tree | 6627c12fa07f6a1cf73e5aaeb7390e698dd3a896 /debug/risc-v-gsp-rm-vm/docs/26-ARCHITECTURE.md | |
| parent | 2f249817df327f5dcccf0464ad517fa60d6071d3 (diff) | |
| download | open-iov-002e3d7d13c414ea874abbee8d82803dd3a72fe5.tar.gz open-iov-002e3d7d13c414ea874abbee8d82803dd3a72fe5.zip | |
debug: green-eye wave 3 - RM harness, local simulator, device map, MCP plugin, value-validation demos, CI pipeline, toolchain docs; add host RM tools rmtool/rmtop
Diffstat (limited to 'debug/risc-v-gsp-rm-vm/docs/26-ARCHITECTURE.md')
| -rw-r--r-- | debug/risc-v-gsp-rm-vm/docs/26-ARCHITECTURE.md | 449 |
1 files changed, 449 insertions, 0 deletions
diff --git a/debug/risc-v-gsp-rm-vm/docs/26-ARCHITECTURE.md b/debug/risc-v-gsp-rm-vm/docs/26-ARCHITECTURE.md new file mode 100644 index 0000000..326533b --- /dev/null +++ b/debug/risc-v-gsp-rm-vm/docs/26-ARCHITECTURE.md @@ -0,0 +1,449 @@ +# 26 — green-eye architecture: full picture, workflow and API surfaces + +> **# TOOL IN DEVELOPMENT - NOT READY YET!** +> The architecture map for the green-eye suite: how the pieces fit, which +> surface to call, and which document specifies each part. Every claim here is +> implemented in the tree or explicitly marked as a gated roadmap design. +> Development-only: never shipped, never a driver dependency, never in a guest. + +## 1. What green-eye is (and is not) + +green-eye runs GSP/uproc firmware in a controllable machine, observes it with +Frida-like primitives, records a durable session, changes the machine's inputs +(including injected code), and compares runs as evidence for `open-iov-vfio` +emulation changes. + +| It is | It is not | +|---|---| +| A firmware runtime microscope (execution + memory, L0/L1) | A time, interrupt or device model (L2/L3 absent — [06](06-ACCURACY.md)) | +| A scriptable `Session` API with two backends | A GDB wrapper; there is no GDB dependency | +| A recorded-evidence system with a versioned format | A product component or a guest agent | +| A static/dynamic join with Ghidra | A decompiler or a CUDA-corpus replacement | +| An automation layer (harnesses, MCP, CI) | A fuzzing service against hardware; simulator/re-hosted code only | + +## 2. The suite as a whole + +```text + front doors control plane evidence + CLI/REPL/scripts ─┐ + MCP (stdio) ──────┼─► GreenEyeAPI ─► Session ─┬─► QEMU-RSP backend ──┐ + Ghidra bridge ────┤ (plugins) Memory │ qemu-system-riscv64│ + harness/ ─────────┤ SymbolDB └─► native backend ────┼─► session dir + rmharness ────────┘ Recorder sim/machine.py │ events.jsonl + └─► analyze / compare / + export_hits / bundles +``` + +```mermaid +flowchart LR + CLI["CLI/REPL/scripts"] --> CORE + MCP["MCP server"] --> CORE + GH["Ghidra bridges"] --> CORE + HAR["harness/*.py"] --> CORE + RMH["rmharness (read-only)"] --> CORE + CORE["GreenEyeAPI + Session<br/>hooks, watchpoints, call, run_until<br/>Memory, SymbolDB, Recorder"] + CORE --> QB["QEMU-RSP backend"] --> SESS + CORE --> NB["native backend (M1/M1.5)"] --> SESS + SESS["session dir, greeneye-session/1"] --> REP["analyze / compare / export_hits / MCP / bundles"] + RMH --> SESS +``` + +Status: transport, core, recording, both backends (native M1 trace included), +injection ABI v1, both Ghidra bridges, traces, device map, harnesses, MCP (R3) +and `rmharness` are implemented and exercised. The `Backend` protocol +refactor (16 §5), the H2.2 QEMU device model, RM-core injection (H1.3) and the +H3 M2–M6 milestones are design/roadmap only ([16](16-CUSTOM-SIMULATOR.md)). + +## 3. Session core: hooks, watchpoints and dispatch + +`Session` ([greeneye/session.py](../greeneye/session.py)) owns all stop-driven +state and is backend-agnostic: every stop reads registers, resolves the PC, +records an event, invokes matching hooks/watchpoints, then resumes. + +```mermaid +sequenceDiagram + S->>FW: set breakpoint / watchpoint + loop run(duration, max_stops) + FW-->>S: stop reply (T/S/W) + S->>S: resolve PC via SymbolDB + S->>R: bp_hit / watch_hit / stop / step + alt symbol entry + S->>H: on_enter(hit) with a0..a7 + S->>S: arm return breakpoint from ra (LIFO) + S->>FW: continue + else return breakpoint + S->>H: on_leave(hit), a0 as retval + else watchpoint (Z2/Z3/Z4) + S->>FW: clear watch, step over store, re-arm + S->>H: callback + store PC + end + S->>R: on_event(kind, hit) + end + S->>R: session_end on close +``` + +| Primitive | Semantics / limits | +|---|---| +| `breakpoint(addr, kind=sw\|hw, temporary=)` | sw is stepped over transparently; hw uses the backend's `Z1` | +| `hook(target, on_enter, on_leave)` | leave is a return breakpoint on `ra`; tail calls/interrupt returns are not tracked | +| `watch(target, size, kind=w\|r\|a, callback)` | QEMU stops *at* the access; Session clears/steps/re-arms; store PC is recorded | +| `run(duration, max_stops)` | services stops until a budget or exit; the deadline path interrupts once | +| `run_until(target, timeout)` | temporary breakpoint; raises `RspTimeout` when not reached | +| `call(target, args, timeout)` | Frida-like remote call; restores `RESTORE_REGS` around it | +| `step(n)`, `note(text)`, `on_event(kind, fn)` | single-step with dispatch; annotate and observe the stream | + +Instructions between stops are invisible unless the TCG plugin +(`qemu/greeneye_trace.c`) or the native trace (`sim/trace.py`) is on +(01 §11, 16 §1.1a). + +## 4. Backend abstraction: QEMU-RSP and native + +`Session` assumes a small RSP-shaped primitive set; `run_until` and `call` +live above it and work on every backend. M1 implements the same method +surface (`greeneye/sim_backend.py`) without the planned `Backend` protocol +refactor (16 §5); the table is the M2+ target. + +```mermaid +flowchart LR + S["Session (backend-independent)"] + S -->|"read/write reg+mem, step, continue, Z0-Z4"| RC + S -->|"same surface"| ST + subgraph QEMU["QEMU-RSP backend (default)"] + QT["QemuTarget<br/>qemu-system-riscv64 virt"] --- RC["RspClient gdbstub packets"] + end + subgraph NAT["Native backend (--backend native)"] + ST["SimTarget / SimClient"] --- MC["sim.machine.Machine<br/>RAM + UART + decode loop"] + MC --- TR["sim.trace.Trace exec/access"] + MC --- DM["DeviceMap lenient MMIO"] + end +``` + +| Session need | QEMU-RSP | Native | +|---|---|---| +| registers/memory | `g`/`p`/`P`, `m`/`M`/`X` chunked to `PacketSize` | direct register file and memory map | +| breakpoints/watchpoints | `Z0`–`Z4` | dispatch-loop tables; same stop semantics | +| snapshots | region copies over RSP | direct map copy; machine moments are M2+ | +| virtual time | host wall clock | deterministic instruction count (`SIM_IPS`) | +| full-access trace | TCG plugin (`-plugin`) | in-process `sim/trace.py`, `--trace` | +| device environment | QEMU `virt` only (no TU104) | device map + lenient MMIO; real devices H2.2 | +| injection | debugger writes + `Session.call` | same SDK ABI; RM interception is M5 | + +`--backend qemu|native` is accepted by `demo`, `run`, `attach`, `record`, +`inject` and `ghidra`; `attach --port` and QEMU `--trace` are QEMU-only; +`--trace` is native-only (16 §1.2). + +## 5. Recording and the evidence format + +A session directory is the durable artifact (`greeneye-session/1`, +[greeneye/record.py](../greeneye/record.py)). The event stream is the map; the +snapshot and blob files are the dump. + +```text +dumps/<stamp>-<name>/ + session.json # format, name, created, counts, snapshot inventory, meta + events.jsonl # append-only JSON lines: {"t": monotonic, "type": ...} + snapshots/*.bin.z # zlib-compressed region captures (sha256 recorded) + blobs/* # QEMU logs, trace.jsonl, rmharness stdout, plugin dumps + summary.md # rendered analysis +``` + +```mermaid +flowchart LR + E["Session stops, memory ops,<br/>probe/plugin/injection events"] --> R["Recorder.event()"] + R --> J["events.jsonl (append-only)"] + M["Memory.snapshot()"] --> Z["snapshots/*.bin.z + sha256"] + B["Recorder.blob()"] --> BD["blobs/*: trace.jsonl, qemu.log"] + X["rmharness external runs"] --> J + J --> A["analyze: summary.md"] + J --> C["compare: A/B delta report"] + J --> G["export_hits: greeneye_hits.tsv"] + J --> S10["value_demos s10: manifest + M1-M6 skeleton"] +``` + +Event classes: core stop/memory events (`session_start`, `image_loaded`, +`hook`, `watch`, `bp_hit`, `leave`, `watch_hit`, `stop`, `step`, `mem_read`, +`mem_write`, `snapshot*`, `probe*`, `plugin*`, `note`, `exit`, `session_end` — +01 §6); injection (`service_injected`, `call_enter`, `call_return` — 12 §6); +traces (`exec`/`access`); M2/M3 native bus (`device`/`irq`/`schedule` — 16 +§10); `rmharness` (`rm_harness_run`, `t=0.0` with epoch `start`/`end` — 17 §5). + +Format invariants: append-only (partial sessions stay readable); additive +versioning with migration notes (V2); `compare` treats `t`, host paths and +backend identity as non-semantic; a claim without a session is not evidence +(06 §7, 25 §1). + +## 6. Injection SDK lifecycle + +The SDK (`sdk/greeneye_service.h`, `sdk/injected_demo.c`) builds +position-independent ELF64 `EM_RISCV` blobs; `greeneye/inject.py` loads, +registers and invokes them. ABI v1: `ge_u64 (*)(ge_u64 cmd, ge_u64 a0, +ge_u64 a1)`, magic `0x47455356`, entry `ge_demo_service_dispatch`. + +```mermaid +sequenceDiagram + Dev->>Inj: load_service(elf, slot=6, load_addr) + Inj->>FW: write PT_LOAD segments into RAM + Inj->>FW: patch gsp_ctrl.handlers[slot] entry + Inj->>Rec: service_injected + mem_write events + Dev->>Inj: invoke_remote(cmd, a0, a1) + Inj->>FW: Session.call(entry, args) + Rec-->>Rec: call_enter / call_return + Dev->>FW: mailbox = 16 + slot (arg0=cmd, arg1, arg2) + FW->>FW: dispatch handlers[slot] + FW-->>Rec: hook/watch events + result field +``` + +Demo registration: mailbox commands 16..23 select slots 0..7; slot 6 is the +SDK default; control-block offsets (`mailbox +0x20`, `handlers +0x150`) are +demo-specific and must come from `greeneye/inject.py` constants (12 §4). +Cleanup is not automatic in v1; record the injection so runs start from a +known state. Roadmap: RM-core shim, service registry and `log`/`stub`/ +`forward` interception at M5/H1.3 (12 §8, 16 §11). + +## 7. Ghidra bridges (two directions) + +The static/dynamic join uses three address spaces — file vaddr, Ghidra program +addr, runtime addr — with a bias per source (01 §5, 05 §1). + +```mermaid +sequenceDiagram + G->>B: {"id":n,"cmd":"run_until","args":{"target":"0x..."}} + B->>S: run_until(addr) (temporary breakpoint) + S->>T: continue; service intervening stops + T-->>S: stop at addr + S-->>B: {addr, symbol, kind} + B-->>G: JSON response + G->>B: read_mem / read_reg / call / snapshot / hits / export_hits + B->>R: hits and notes recorded in the session + R-->>G: greeneye_hits.tsv (via MarkGreenEyeHits.java) +``` + +| Tool | Purpose | Doc | +|---|---|---| +| `symbols export` + `SymbolDB` loaders | Ghidra TSV/JSON → runtime symbol DB | [05](05-GHIDRA-BRIDGE.md) | +| `greeneye.ghidra.export_hits` | session hits → `addr<TAB>hits<TAB>symbol<TAB>annotation` | 05 | +| `ExportForGreenEye.java` | Ghidra → `addr<TAB>name<TAB>size` TSV | 05 | +| `MarkGreenEyeHits.java` | annotate functions with runtime hits + bookmarks | 05 | +| `GreenEyeControl.java` + `greeneye.sh ghidra` | drive a live session from GhidraScripts over localhost JSON-lines | [13](13-GHIDRA-CONTROL.md) | + +Bridge commands: `ping`, `info`, `resolve`, `bp_set`, `bp_clear`, `bp_list`, +`watch_set`, `run_until`, `run`, `step`, `read_mem`, `write_mem`, `read_reg`, +`write_reg`, `call`, `snapshot`, `note`, `hits`, `export_hits`, `close` +(13 §3). Unauthenticated by design; localhost only. + +## 8. MCP server tool surface + +`plugins/mcp_server.py` serves the active `Session` over JSON-RPC 2.0 on +stdio (or loopback TCP) with no MCP library; read/observe by default, +`write_mem`/`call_function` require `--allow-write`, `inject_service` +requires `--allow-inject` ([20](20-MCP-SERVER.md)). + +```mermaid +flowchart LR + AG["AI agent (Claude Code, opencode)"] -->|"JSON-RPC 2.0 over stdio"| M["plugins/mcp_server.py"] + M --> READ["read: session_info, symbols_resolve,<br/>read_mem, hexdump, snapshot, plugins_list"] + M --> OBS["observe: bp_set/clear, watch_set,<br/>run, run_until, step (recorded)"] + M --> WR["write (gated): write_mem, call_function"] + M --> INJ["inject (gated): inject_service"] + M --> TR["trace: trace_start/stop"] + M --> AN["analysis: analyze_current, compare_sessions, export_hits"] + M --> HN["harness: probe_firmware, mutation_run,<br/>portability_matrix_run"] + M --> CL["session: close"] + READ & OBS & WR & INJ & TR & AN & HN --> S["Session + Recorder"] +``` + +All results are one JSON text block; absent modules return a structured +`not_available` error; stdout carries protocol frames only (20 §4, §8). Full +argument schemas for all 24 tools are in 20 §5. + +## 9. Automation harness + +`harness/` turns the playbooks into repeatable, self-describing runs. Modules +bootstrap the tool root and use the QEMU backend by default (18, 19, 21); +`common.py` provides flat-image relocation, session helpers and bounded log +reads. + +```mermaid +flowchart TB + FW["firmware ELF / .fwimage"] --> PM & MU & CD & RE & VD & LV + SYM["Ghidra exports + symbol DBs"] --> PM & MU & CD & RE & VD & LV + STIM["stimulus / probe spec"] --> PM & MU & CD & RE & VD & LV + SEED["seed + budgets"] --> PM & MU & CD & RE & VD & LV + subgraph H["harness/"] + PM["portability_matrix.py R6/H4.1"] + MU["mutation_harness.py R5/H4.3"] + CD["control_discovery.py H4"] + RE["reach.py H4.1/H4.5"] + VD["version_diff.py H4.1"] + CR["ci_release.py"] + LV["value_demos/ s01..s10"] + end + VD --> CR + PM --> CR + MU --> CR + PM & MU & CD & RE & VD & LV & CR --> OUT["reports + sessions + findings.jsonl<br/>+ compare/ + bundle-manifest.json"] +``` + +| Entry point | Purpose | Outputs | Doc | +|---|---|---|---| +| `portability_matrix.py` | probe + symbols + fixed scenario + compare across targets; M1–M6 skeleton | `portability-report.json/.md`, `symbols/`, `sessions/`, `compare/` | 18 §1 | +| `mutation_harness.py` | seeded one-input mutation, outcome classes, per-attempt session | `findings.jsonl`, `summary.md`, `sessions/` | 18 §2 | +| `control_discovery.py` | sweep command/call surfaces; classify handler/default/trap/fault | `findings.jsonl`, `discovery-report.json`, `control-discovery.md` | 19 §3 | +| `reach.py` | reachability, trigger discovery, required args and effects for one symbol | `reach-report.json/.md`, `probe.json`, sessions | 19 §4 | +| `version_diff.py` | old vs new image: shape, symbols, corpus hints, probe, behavior, M1–M6 | `version-diff.json/.md`, `corpus/`, `compare/` | 21 §2 | +| `ci_release.py` | version_diff → matrix → mutation smoke, then hash the bundle | `release-report.json`, `bundle-manifest.json` | 21 §4 | +| `value_demos/run_all.py` | ten fixed scenarios (reply, counters, discovery, reach, diff, inject, version, mutation, Ghidra, evidence) | `artifacts/results.json` + per-demo JSON | 25 | +| `common.py` | shared setup (relocate to `0x80000000`, open session, bounded logs) | library | 18 §1.2 | + +Harness limits are intentional: QEMU/re-hosted/demo code only; no hardware, +guest, kernel module or existing gdbstub; bounded budgets; deterministic +findings for a fixed seed (18 §5, 19 §8, 21 §7). + +## 10. Device map and lenient MMIO data flow + +`greeneye-device-map/1` is versioned JSON keyed by +`(address, width, direction)` with values, hit counts and firmware PCs +([24](24-DEVICE-MAP.md)). It records what the firmware touched first and lets +the native interpreter step past unmodelled registers to collect the next +ones; it feeds the H2.2 QEMU device model and the H3 profile DSL. + +```mermaid +flowchart LR + P["greeneye probe (raw .fwimage in QEMU)"] -->|"blobs/qemu.log + qemu_first_fault"| H["device_map.py harvest"] + H --> MAP["greeneye-device-map/1<br/>entries + meta (bias, sha256)"] + MAP --> LOOK["DeviceMap.lookup<br/>zero / value / unimplemented"] + LOOK --> LEN["Native Machine(unknown_mmio=lenient)"] + LEN --> LOG["machine.mmio_log / mmio_unknown"] + LEN --> LEARN["learn_mmio=True: source trace entries"] + LEARN --> MAP + MAP --> PROF["docs/16 profile DSL<br/>falcon/csb/ptimer/pmu stubs"] + PROF --> H22["H2.2 QEMU device model"] + LOG --> NEXT["next unmapped wall; promote to manual"] + NEXT --> MAP +``` + +Honest scope: lenient execution proves an unmodelled access *can* be served +deterministically; it does not model read-back, side effects, interrupts or +timing, and a zero that keeps the firmware running is a hypothesis, not a +silicon fact (24 §4). + +## 11. Workflow architecture: scenario → evidence → emulation change + +The canonical loop (H4.5, 06 §5, 07 P4, 25 §7). Each stage names its gate. + +| Stage | Action / instrument | Success criterion | Failure signal | +|---|---|---|---| +| 1. Scenario | state the firmware question and the smallest stimulus (one command/control/boot phase) | question maps to a symbol or surface | no observable exists → add one (probe/plugin) | +| 2. Choose instrument | hooks/watchpoints for semantics; TCG/native trace for coverage; `run_until`/`call` for reachability; injection for new code | stop set answers the question without flooding | session too large/noisy → filter PCs, watch fields only | +| 3. Run | fresh bounded session; fixed stimulus, seed and budgets | run completes; UART/banner liveness holds | timeout, trap loop, wedged target → capture and retry clean | +| 4. Record | `greeneye-session/1` with snapshots and notes | events + snapshots + hashes written | missing `session.json`/empty analyzer → recording off | +| 5. Compare | `compare` against a reference (old firmware, hardware/VFIO capture, self-diff) | deltas explained symbolically; self-diff is zero | unexplained hit/watch/write delta → investigate first | +| 6. Decide | fill M1–M6 with the reference method (06 §5); mutation/triage for error paths | metric table improves or stays equal | metric claimed without a reference capture | +| 7. Implement | change `open-iov-vfio` (never the simulator for a product fix); keep the stimulus | physical confirmation for behaviour changes (06 §6, 14 §7) | product change from a simulator-only observation | +| 8. Regression | re-run step 3 with an unchanged stimulus; golden sessions/CI (`make test`, `greeneye.yml`) | profile improves or stays equal; CI green | regression → revert and re-derive | + +```mermaid +flowchart LR + Q["Question"] --> OBS["Observe<br/>probe / hooks / watch / trace"] + OBS --> ORI["Orient<br/>analyze + symbol decode + snapshots"] + ORI --> DEC["Decide<br/>compare + M1-M6 + mutation"] + DEC --> ACT["Act<br/>implement in open-iov-vfio"] + ACT --> REG["Regression<br/>same stimulus, same budgets"] + REG -->|"improved / equal"| SHIP["Evidence pack<br/>sessions + report + hashes"] + REG -->|"regressed"| Q + SHIP --> OBS +``` + +OODA compression evidence (analog, measured) is in 25 §7: hardware cycles are +minutes and campaigns hours/days; the green-eye path for the same question +class is sub-second to seconds plus scripting. + +## 12. API surfaces by tool + +### 12.1 CLI (`greeneye.sh`, `greeneye/cli.py`; usage in [02](02-USAGE.md)) + +| Command | Purpose | Not for | +|---|---|---| +| `info` | environment/tooling check | validating a firmware image | +| `demo` | canned boot + hooks + watch + record on the bundled uproc | booting real GSP (use `probe`) | +| `run <script.py>` | run a Python instrumentation session | shell pipelines (write Python) | +| `attach` / `launch` | interactive REPL / keep a target alive by port | injecting into a live product | +| `record` | boot and record with watches/snapshots/trace | post-processing an existing session | +| `probe <image>` | bounded run of a raw image; first divergence report | booting the image (no TU104 model) | +| `inject` | load/register/invoke an SDK service | shipping or hot-patching signed firmware | +| `ghidra` | serve a live session to GhidraScripts | network exposure (localhost only) | +| `analyze <session>` / `compare <a> <b>` | markdown report / symbolic A/B delta | proving accuracy without a reference | +| `symbols export\|resolve` | build/inspect a symbol DB | recovering types/decompilation | +| `plugins` | list discoverable plugins | loading untrusted code into the core | +| `python3 -m greeneye.device_map harvest\|show\|merge\|demo` | MMIO first-touch map data | a device model or timing source | + +### 12.2 Python entry points (recipes in [03](03-SCRIPTING.md)) + +| Entry point | Purpose | Not for | +|---|---|---| +| `ge.launch/attach`, `Session.hook/watch/run/run_until/call/step/note` | session lifecycle and instrumentation | in-process C instrumentation | +| `Memory.read/write/hexdump/search_*/snapshot/diff/render_diff` | memory inspection and region diff | full-machine snapshots (M2+) | +| `SymbolDB.load_elf/*_tsv/*_json`, `resolve/annotate` | address-space-aware symbols | type recovery from Ghidra | +| `structs.schema_from_json/decode/render` | declarative struct decoding | automatic struct discovery | +| `probes.load_spec/install` | JSON probe specs (no arbitrary code) | complex control flow (write a script) | +| `inject.load_service/invoke_remote/invoke_via_firmware` | ABI v1 service load/invoke | product features or persistence | +| `ghidra.build_symbol_db/export_hits/corpus_location` | Ghidra TSV in/out and corpus location | replacing Ghidra analysis | +| `analyze.render_summary/compare_sessions/parse_qemu_faults/load_trace` | offline reports and QEMU fault parsing | live control | +| `record.Recorder/load_events/load_meta` | session writing/reading | a database (files are the interface) | +| `device_map.harvest_*/DeviceMap.*` | MMIO map authoring/merge | device semantics | +| `sim_backend.launch_native`, `sim.trace.Trace/make_trace` | native backend + trace knobs | timing/concurrency claims | +| `errors.*` (`GreenEyeError`, `RspTimeout`, ...) | typed failure surface | retrying a wedged target blindly | + +### 12.3 Plugin API (`register(ge)`, [04](04-PLUGINS.md)) + +`ge` exposes live objects (`session`, `memory`, `symbols`, `recorder`), +instrumentation (`hook`, `watch`, `on_event`, `resolve`), CLI extension +(`add_command(name, fn, help)`, `commands`) and audit logging (`log`). A +raising plugin is recorded as `plugin_error` and skipped. Not for: shipping +dependencies, assuming a specific firmware, or hiding unrecorded state. + +## 13. Extensibility and invariants + +Where extension lives: + +* **Plugins** (`plugins/`, `GREENEYE_PLUGINS`) — runtime RE tools, harnesses, + environment updates, agent integrations (04). +* **Harness modules** (`harness/`) — new sweeps/reports over the public + `Session`/analysis primitives; a new call surface is a `Surface` subclass + (19 §5.1). +* **SDK services** (`sdk/`) — PIC blobs behind ABI v1; v2 adds an RM-core call + table (12 §8). +* **Device maps/profiles** — versioned data (`greeneye-device-map/1`, the + `greenmachine-profile/1` DSL in 16 §7), never core special cases. +* **MCP tools** (`plugins/mcp_server.py`) — add tools in the plugin, keep the + core stdlib-only (20 §10). + +Invariants (enforced by tests or review): + +1. **stdlib-only core**: `greeneye/` and `sim/` import only the standard + library; optional deps live in plugins/harnesses. +2. **Session format**: `greeneye-session/1` is append-only and evolves + additively with migration notes (V2). +3. **Backend parity**: every backend satisfies the same `Session` primitives, + so scripts, probes, plugins, recordings and comparisons survive backend + changes (16 §4, R0). +4. **ABI**: blobs declare an ABI version and capability metadata; breaking + changes need a shim and a doc note (12 §8, V3). +5. **Markers**: the `TOOL IN DEVELOPMENT` banner stays until the acceptance + list in 00 passes; never shipped, never in a guest, never a driver + dependency. +6. **Evidence**: no accuracy, portability or security claim without a recorded + A/B session and an explicit L2/L3 fidelity statement (06 §5–6, 25 §10). +7. **Provenance**: tracked docs never name local source trees; public + descriptions first, local provenance in the local notes (V6). + +## 14. Diagram and document index + +Section diagrams and their backing docs: §2 suite (00, 01, README); §3 +dispatch (01, 03); §4 backends (01 §8, 16 §4–5); §5 recording (01 §6, 09, 12 +§6, 16 §10, 17 §5); §6 injection (11, 12); §7 Ghidra (05, 13); §8 MCP (20); +§9 harness (18, 19, 21); §10 device map (24, 16 §7); §11 OODA (06, 07 P4, 25 +§7). Roadmap keys: R3 = MCP (08), R5 = mutation (18), R6 = multi-version (18, +21), H1.3 = RM service injection (GREEN-EYE-ROADMAP), H2.2 = QEMU device +model, H3/M1–M6 = native simulator (16), H4 = adversarial navigation (14), +H7 = agentic platform (15, 20). |
