mirror of
https://github.com/morluto/rea.git
synced 2026-11-09 00:07:18 +00:00
* feat(artifacts): inventory and extract ESP32 firmware images ESP32-family app and bootloader images (magic 0xE9) had no reader, so open_binary refused OTA downloads, app partitions and bootloaders. Add the esp-image container format behind inspect_artifact and extract_artifact. A pure decoder reads each byte once and keeps only fixed-size fields. Derived facts cover the chip, from a table cited to ESP-IDF v6.1; segment classification as ESP-IDF's should_map and should_load; checksum and appended SHA-256 results; app and bootloader descriptors; Secure Boot v2 signature blocks, observed and unverified; and undescribed trailing byte ranges. Truncation and undescribed bytes make coverage unknown. ESP8266 images are refused with a specific reason, and ciphertext stays unrecognized. Add the format to every target, Evidence, snapshot, live-summary and artifact vocabulary, document it, and add an optional verify:esp-images lane that compares esptool-built images with esptool image-info and espsecure signature-info-v2. Refs #3615 * test(artifacts): run the ESP image lane on esptool v5.6.0 The lane compared descriptor fields only when esptool image-info printed one, so a descriptor that REA reports and esptool does not print passed without comparison. Compare the descriptor kind in both directions. Record that the lane passed for all 13 images with esptool and espsecure v5.6.0, the version the layout facts cite, as well as with 4.12.0. * refactor(artifacts): define ESP image layout sizes once The decoder and the derived facts each defined the segment-header, appended-digest and flash-sector sizes, the erased-byte scan and the offset formatter. Define them once with the other fixed-layout structures and import them in both places. The signature slot count is the one constant that drives the slot scan. Correct the observedEnd comment: it is the end of the last chunk read. * fix(artifacts): report ESP-IDF's segment length limit by chip ESP-IDF's verify_segment_header rejects a segment of 16 MiB or more unless the chip maps flash through 32-bit cache addresses (SOC_SPI_MEM_SUPPORT_CACHE_32BIT_ADDR_MAP: ESP32-S3, P4, C5, C61, H4 and S31 in v6.1). Record that capability in the chip table and add a limitation for such segments; unknown chips get none. `loaded` repeats should_load. It does not mean the bootloader accepts the segment: verify_load_addresses still rejects one outside the chip's RAM, which REA does not model. Say so in the docs, the skill reference and the inventory limitation. * test(artifacts): check ESP lane tools first and verify extraction The lane started work before checking its tools, so a missing RISC-V target, ld.lld, esptool or espsecure surfaced as a raw spawn or command error. Check each one before building and name the variable to set. Warn when esptool is not v5.6.0, the release the layout facts cite. Assert each segment's class for the ESP32-C3 app, its variants and the bootloader, as the Xtensa image already did. Extract the app and the signed image through the CLI and compare every file with the bytes at esptool's segment offsets or in the appended signature sector; the source's SHA-256 must be unchanged and equal the reported root_sha256. * docs(artifacts): state what the ESP image lane does not establish The Verification section listed the lane's checks but not its gaps: only ESP32-C3 and ESP32 images come from a real producer, no real project build, OTA download or flash dump is inspected, no signature is verified, and the lane does not run in CI. Record the timing of the final lane run on esptool v5.6.0. * fix(artifacts): compare ESP segment declarations Segment entries hash only their data, so compare_artifacts reported a segment that moved to another load address with the same bytes as unchanged; only the image itself changed. Compare each segment's load_addr and data_len as its declaration, as Node SEA and PyInstaller members are compared, and count a changed declaration as a metadata change, also when content is unavailable. Header offsets are layout and the classification is derived, so neither is compared. * test(artifacts): run ESP lane commands as owned process trees The lane ran its tools through execFile. A timeout killed only the direct child, so a linker or assembler that Clang or GCC had started could keep running while the lane removed its workspace. Run every command through createVerifierCommands, check the tools inside withVerifierCleanup, and remove the workspace only after commands.close() has stopped every tree; a failed close keeps the workspace. Only a nonzero exit now counts as a refusal or an expected esptool failure; timeouts and spawn failures propagate. Supervising each command roughly doubles the lane's run time. * test(artifacts): cancel ESP decoding after its first decoded chunk The cancellation test requested cancel or close on setImmediate, which can land while the file is still being opened and checked, so it never showed that decoding had started. It also passed against a reader that ignored both requests while decoding. Acknowledge the decoder's first chunk, then cancel or close, join the operation, and assert that no later chunk reaches the decoder and that the reader ends closed. * docs(artifacts): record which ESP metadata comparison ignores Segments are compared by their declaration. The image's and the signature blocks' format_metadata are not: every field comes from their own bytes, which content comparison already covers, and a block's image_digest_matches follows the image's content. * feat(artifacts): inventory and extract ESP flash layouts A whole-flash dump of an ESP32-family chip was read as its bootloader image, with the partition table, apps and data left as undescribed bytes, or refused when it began with erased flash. Read it as an esp-flash-image container instead. The partition table is decoded as esp_partition_table_verify accepts it and is found at the caller's partition_table_offset, at the default 0x8000, or by scanning 0x1000-aligned sectors for a table whose MD5 verifies, which the inventory reports as derived. The bootloader is located at its chip's ROM bootloader offset. Each partition is an entry with its declaration in flat format_metadata; the bootloader and app partitions carry ESP image facts from the existing streaming decoder; the otadata records are reported with a selection derived without rollback or anti-rollback, and the running partition stays unknown. One unreadable partition does not hide the others, and extraction writes every partition, nvs_keys included, as ordinary data. Flash layouts are detected before single ESP images. The scan runs only when an ESP image sits at a ROM bootloader offset, and is refused when a bootloader at another chip's offset shows that the dump does not start at flash address 0. open_binary gains flash_offset and partition_table_offset, which require format esp-flash-image and are part of the snapshot target identity; the CLI takes them with --target-format esp-flash-image. Comparison treats a partition's label, type, subtype, range and flags as its declaration. * test(artifacts): verify ESP flash layouts with gen_esp32part, merge-bin and otatool Add the optional verify:esp-flash lane. It builds default and relocated partition tables with ESP-IDF's gen_esp32part.py, assembles dumps with esptool merge-bin, including one written from flash 0x1000, and writes OTA records with otatool through flash_file_tool.py, which replaces parttool's esptool calls with reads and writes of the dump file. It compares REA's bootloader and app facts with esptool image-info, every partition with gen_esp32part.py's decoding of the extracted table, and the OTA records and derived slot with otatool read_otadata after each switch. It also checks extracted bytes and source integrity, the scan and caller table offsets, flash_offset on the 0x1000 dump, a corrupted app beside valid partitions and a truncated dump. ESP32 linker scripts place the conformance bootloader and app in ESP32 address ranges. * fix(artifacts): read ESP flash layouts as the bootloader does Read the partition table as bootloader_utility_load_partition_table does: the last factory, otadata and same-subtype OTA declaration wins, and a limitation names repeats. Plan each otadata record within its partition and the dump. An otadata partition smaller than two sectors selects none, the bootloader's INVALID_INDEX; a record the dump lacks leaves the selection unknown while the other record stays reported. The fallbacks follow the bootloader's indexes: ota_0-fallback and factory-fallback name no partition when the table declares none, and the assumptions add GPIO holds and deep-sleep wake. Refuse a dump whose bootloader contradicts its origin before the default table offset is tried, whether flash_offset was omitted or given, and keep the ESP image reader's ESP8266 refusal. A table never lies inside the located bootloader image, so single app images are no longer scanned. An explicit esp-flash-image selection is decided before other container signatures, and the native commands' --target-format no longer takes it. Say when no bootloader confirms the default origin. Exempt the primary bootloader and partition_table entries of ESP-IDF 5.3 from placement limits, report every region inside a longer one, and name partitions by index when labels repeat ignoring case. Keep an undecodable label's bytes in label_hex and compare them. Word undescribed ranges as starting at a byte that is not erased, and treat a short erased region as erased. Add an MCP compare_artifacts test for partition declarations, reader identity and close tests, scan edge cases, the 0x2000 ROM offset and a literal otatool CRC. * test(artifacts): verify ESP primary layout entries and the otadata fallback Build a table with the primary bootloader and partition_table entries that gen_esp32part.py fills from --primary-bootloader-offset, and check that REA reads it with complete coverage, no overlap or misplacement limitation, and a table that agrees with gen_esp32part's decoding. Erase the otadata with otatool after the slot switches and check the records against read_otadata and the factory fallback. * fix(artifacts): keep ESP flash coverage complete for a dump cut short An esptool merge-bin dump ends after the last part it wrote, usually inside its last partition. Such a partition made the layout's coverage unknown, so artifact comparison of two merge-bin dumps reported every unchanged partition as unknown. The partition table names every partition whatever the file's length, so a partition cut short or left out by the end of the file hides no entry. As the ESP image reader does for a cut-short segment, its entry holds the bytes the file has and an entry limitation gives the declared size; a partition with no bytes stays in partitions_outside_dump. Coverage stays complete unless undescribed bytes remain or no bootloader is located. An MCP compare_artifacts test of two dumps that end inside ota_0 now classifies every partition, and fails on the old rule. * fix(artifacts): report written bytes in ESP partitions, erased slots included An app slot is erased for ESP-IDF's bootloader when its image header bytes are erased flash, and REA reported only that status. A slot can still hold data further in: a dump read from a device held a complete older app image 1 MiB into an OTA slot reported as erased, with no limitation. Each partition entry now records where the bytes the dump holds are written, relative to the partition: written_offset, the first byte that is not 0xFF, written_end, just past the last, and written_bytes, how many are not 0xFF. They are null for a partition outside the dump. The status stays erased, which is the bootloader's view, and a limitation names an erased app slot that holds written bytes and says they are not read as an image. The reader reads every partition byte once more to find them. * fix(artifacts): derive the ESP boot selection for every partition table The boot selection lived inside otadata, so a table without an otadata partition, the common factory-only layout, had none. ESP-IDF's bootloader_utility_get_selected_boot_partition returns the factory index when the table declares no otadata partition. Move it to a top-level boot_selection that every layout has. Its basis names the bootloader's branch: otadata, factory-fallback and ota_0-fallback from the records, no-otadata for a table without otadata, none for the bootloader's INVALID_INDEX, and unknown when the dump lacks a record the bootloader reads, which replaces the null selection. running_partition moves with it, and otadata keeps its partition index and records. The assumptions name what each branch leaves out: no records assumption without otadata, and none for none or unknown. The real-tool lane checks no-otadata on a table gen_esp32part built without otadata. * fix(artifacts): say when an ESP image is a flash dump read from 0x1000 An ESP32 dump read from flash 0x1000, as read-flash 0x1000 or merge-bin --target-offset 0x1000 writes it, opened automatically as its bootloader image with a megabyte of undescribed bytes and no next probe. The flash reader refuses that origin, but nothing told an agent which flash_offset to pass. espFlashDumpOrigin owns the inference: an image of a chip whose ROM loads the bootloader from flash R, at file offset F, is the bootloader of a dump read from R - F when a partition table sits where that origin puts 0x8000. The ESP image reader asks it for a non-app image at file offset 0, so the result does not depend on how the reader was created, and records flash_dump {flash_offset, table_file_offset}, null otherwise, with a limitation that says to reopen the file as esp-flash-image with that flash_offset. inspect_artifact proposes the same open_binary call as a next probe, and the flash reader's misplaced-origin refusal names the offset. * perf(artifacts): summarize nested ESP images in the flash layout summary view The summary view returned an ESP flash layout's root facts whole, so each app partition and the bootloader carried every segment and signature block, about half of a dump's summary. The summary view now gives each nested image a summary instead of its facts: status, chip, entry point, segment count, load ranges (each mapped, loaded or unclassified segment's address and declared length), the checksum and appended SHA-256 statuses, coverage and limitations. Complete detail keeps the full facts, every segment included, and the retained Evidence is unchanged. On a 16 MiB device dump the summary's root falls from 5,141 to 2,657 bytes, and the summary Evidence from 10,038 to 7,554. * test(artifacts): expect the misplaced image the ESP bootloader search reports8a748d3ccadded misplacedImage to the bootloader search, which the locator uses to name the origin a misplaced bootloader implies, but one test still compared the search result without it. Expect the image's chip and file offset, and no image for an unknown chip. * fix(artifacts): give every ESP partition from the dump's start an entry Afterecbc1a772kept coverage complete for a dump cut short, a partition wholly past the end of the file still had no entry, only a partitions_outside_dump row. Comparing a merge-bin dump with a full device read then reported that partition as added or removed, as if one table did not declare it. Every partition from the dump's first byte on is now an entry holding the bytes the file has: none for one past the end, with a limitation saying the file holds none of its bytes, no image facts and null written fields. partitions_outside_dump keeps only partitions that begin before the dump's first byte, and coverage is unknown when one does, since it has no entry. Two dumps that lack the same partition now compare it as unchanged, and a dump against a longer one reports the partition as changed; an MCP test of a merge-bin dump against a full one checks that no path is added or removed. Unlike the ESP image reader, which marks a truncated image's coverage unknown because later segments and signature blocks may be hidden, the flash reader can list every partition from the table whatever the file's length; only the entry limitation follows the image reader. * fix(artifacts): select no ESP app from a partition table the bootloader rejects boot_selection followed bootloader_utility_get_selected_boot_partition but not the table check before it. For a table whose MD5 does not match, or that has a second MD5 entry, esp_partition_table_verify fails, the bootloader selects no app and resets. REA still derived a slot from the otadata records, although the table's own limitation said the bootloader rejects it. The selection now has basis none, with no assumptions, for a table the decoder marks invalid. It also follows two more branches of the bootloader: an otadata partition at offset 0, which the bootloader takes for none, selects the factory index as no-otadata, and a table without an OTA app selects its fallback even when the dump lacks a record, since the bootloader then ignores the records. The bootloader also rejects a table whose partitions end past the flash size in its header. REA does not model that check; the format guide says so. * fix(artifacts): require the ESP dump origin's table to follow the image espFlashDumpOrigin named a later flash origin whenever a partition table decoded where that origin puts 0x8000. Bytes inside the image's own segments could be read that way, so an ESP32 bootloader whose segment data held table-shaped bytes at file offset 0x7000 was reported as a dump read from 0x1000. The table must now start at or after the image's end, the rule the locator already applies to the default table. The ESP image reader passes the image_end it decoded, or the file size for a truncated image; the flash reader's refusal passes the end of the misplaced image's segments. The bootloader search drops its misplaced flag, which only restated misplacedImage, and the next probe formats offsets with offsetText. Reader tests cover the 0x1000 and 0x2000 ROM offsets, a standalone bootloader, bytes that begin like a table, an app with a table at 0x7000 and a table inside a segment; without the boundary check, three of them fail. * fix(artifacts): give the ESP flash summary its own kind and each image's identity The summary view's ESP flash root kept kind esp-flash-image, so a consumer bound to kind could not tell an image's summary from its full facts, and the advertised schema held two objects with the same kind const. It is now esp-flash-image-summary. The per-image summary also dropped the descriptor, which is what tells two app slots apart, and named each image only by its partition index, which the summary view, having no entry rows, could not resolve. Each nested image now carries its entry path from the layout plan, in both views. The summary adds the descriptor kind with its identifying fields (an app's project_name, version, idf_ver, secure_version and app_elf_sha256; a bootloader's version, idf_ver and secure_version), the signature block count and each load range's classification. Its enums and fields derive from the schemas that own them. An MCP test reads a dump with an app in ota_0 and an erased ota_1 that holds older bytes: it checks both summaries, that the summary's inventory Evidence is the one the complete view returns with full facts, and the erased slot's written range. * test(artifacts): cover cancel and close during the ESP written-bytes scan The written-bytes pass reads every byte of each partition the dump holds, checking that the reader is open before each chunk, but only app decoding had a cancellation and close test. A data partition, which only that pass reads, now holds 9 MiB; once the scan has examined its first chunk, a cancel rejects preparation with the caller's reason and a close with unavailable, and the scan examines no further chunk. The erased-slot test now asserts the whole set of erased-slot limitations, so factory, ota_0 and nvs, which the bootloader reads or which are not app slots, are shown to have none. * docs(artifacts): re-time the ESP flash lane and state only observed causes The lane ran again after its assertions changed; both esptool runs passed, and the timed run's figures replace the earlier ones. The erased-slot paragraph no longer names a cause for the older bytes, which REA cannot observe, and the skill reference states that every table has a boot_selection in its own sentence, with the none basis for a table the bootloader rejects. * fix(artifacts): retire ESP flash members as the ESP image reader does #7498's maintainer commits made the ESP image reader keep a member owned until its consumer closes it, retire fully buffered bytes when cancellation arrives after production, and refuse to finish an inventory paused across close. The ESP flash reader was copied from the earlier reader. Main's shared stored-member lifecycle cases failed two of eight against it: a paused inventory completed after close, and buffered member bytes stayed readable after cancellation. Give the flash reader the same lifecycle and run the shared cases against it. * docs(artifacts): re-time the ESP flash lane after merging main The lane passed again with esptool v5.6.0 and 4.12.0 on the merge of upstream main. Record the v5.6.0 run's time and peak resident set. * fix(artifacts): read ESP image Evidence recorded before flash_dump The flash layout work made `flash_dump` a required field of the `esp-image` root metadata. Evidence recorded before it has no such field, so newer REA rejected records that were valid when they were made: `compare_artifacts` and `inspect_analysis_view` reported `evidence_integrity_mismatch` for them. The field is now optional when read. A record without it parses as it is, so the graph digest it commits to still matches; nothing fills the field in. The image reader still writes it for every image, and the inspection probe reads a missing field as no hint. A regression test recommits an image inventory without the field and passes it inline to `compare_artifacts` and `inspect_analysis_view`. Against a server built with the required field, `compare_artifacts` rejects it. * fix(artifacts): stop the ESP flash gap scan when the reader closes The scan for the first written byte in each undescribed gap checked only the caller's signal. `close()` marks the reader closed and waits for preparation, so it waited while the rest of a long erased gap was read. The gap scan now takes the reader's open check, as the written-bytes scan and the image decoder do, and checks it before each chunk. Tests cancel or close the reader after the scan's first chunk of a 9 MiB erased gap and confirm that no later chunk is examined. * fix(artifacts): name ESP partitions with Windows device labels by index The safe-label rule accepted labels such as `NUL`, `con` or `COM1`, so extraction wrote `partitions/NUL.bin`. Windows reserves those names before any extension, and ordinary Windows tools cannot use such a file. A label that is a Windows device name (`CON`, `PRN`, `AUX`, `NUL`, `COM0`-`COM9` and `LPT0`-`LPT9`, any case, alone or before a dot) now takes the existing index fallback. The partition metadata and the entry's source path keep the label. * fix(artifacts): read ESP flash partitions' written bytes in one pass Each partition got its own scan of the bytes the dump holds, so partitions declared over the same bytes read them again and again. Overlaps only add limitations, so a crafted table reached this normally: 94 overlapping partitions over a 32 MiB dump would examine about 3 GiB before the first entry. The reader now reads the bytes the partitions cover once. Partition boundaries cut them into segments; one pass counts each segment's written bytes and bounds, and each partition's extent is the sum of the segments inside it. Every partition keeps its own declaration, extent and limitations. The pass checks the reader's open state before each chunk, as the per-partition scan did. A test declares 60 partitions over one MiB, 40 over all of it and 20 over its first half, and checks each partition's extent and the overlap limitations. The reader reads 1,113,256 bytes of the 1,114,112-byte dump; one scan per partition read 52,493,480. * test(artifacts): fail the ESP lanes on failed commands and refusals `otatool` turned a nonzero exit into returned text, which its callers discarded, and `inspect()` returned `{ refused }`, which the flash lane never checked. A failed command then surfaced later as a slot assertion, an output-parser error or a TypeError, hiding its cause. Commands the lanes need to succeed now reject with the output they exited with: `otatool` and `inspect()` do so through a shared `failedCommand`. An expected refusal is opt-in: `refusal()` returns REA's output and fails the lane when REA opens the file. The ESP image lane's two ESP8266 cases use it. Checked by calling `otatool` on a file without a table, `inspect()` on a file REA refuses and `refusal()` on a file REA opens; each rejects with its cause. * fix(artifacts): require a partition entry in an ESP partition table The decoder counted the slot, not the partitions, so a lone MD5 entry over the empty prefix, followed by the terminator, decoded as a table with no partitions. Default and caller-selected detection then accepted that empty layout, although the guide requires one partition. A table now needs one partition entry before its terminator. An MD5 entry alone is refused as `entry 1 at 0x20 ends the table before any partition entry`. The cases cover the decoder and both automatic and caller-selected location. * fix(artifacts): decode an image shared by ESP app partitions once Each app partition got its own decode, so app partitions declared over the same bytes decoded and hashed the same image again and again, the same pattern the written-bytes scan had. With 60 app partitions over one 960 KiB image the reader read 64,029,736 bytes of a 1,114,112-byte dump. Regions with the same file range now share one decode; each keeps its own copy of the facts. Partitions with different ranges still decode separately, because truncation can change the facts: at most one decode per partition the table holds, each within its partition. A test declares 60 app partitions over one image, 40 holding all of it and 20 cutting it short, and expects three decodes: the bootloader, the whole image and the cut one. The 60-partition dump now reads 2,161,864 bytes, as a dump with one app partition does.
1282 lines
68 KiB
Markdown
1282 lines
68 KiB
Markdown
# CLI and Evidence
|
||
|
||
Use the CLI for a direct inspection or a script. It uses the same application
|
||
workflows and evidence contracts as REA's MCP server. Each CLI invocation is a
|
||
separate process; save results when you want to use them in a later command.
|
||
For a pinned, noninteractive workflow with retained reports, see
|
||
[static analysis in GitHub Actions](ci-analysis.md).
|
||
|
||
## Run a command
|
||
|
||
After [installing REA](installation.md), use `rea`. You can also run a command
|
||
without a global installation:
|
||
|
||
```bash
|
||
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
|
||
```
|
||
|
||
Use an extracted application directory or an ASAR as the target. Static
|
||
JavaScript analysis returns the application graph and Evidence directly.
|
||
Generic `rea analyze PATH` selects this workflow for directories and `.asar`
|
||
files when neither `--provider` nor `--snapshot` is supplied. See
|
||
[JavaScript artifact reconstruction](javascript-artifact-reconstruction.md)
|
||
for results, integrity checks and coverage.
|
||
|
||
The complete application Evidence can be hundreds of megabytes for a real
|
||
application. When an agent or a person reads the result, save it to a file and
|
||
project a summary, a module page or one module from the saved Evidence:
|
||
|
||
```bash
|
||
rea analyze-javascript-application /absolute/path/to/app --json > app-evidence.json
|
||
jq -c '{source: {kind: "inline", evidence: .}, view: {kind: "summary"}}' app-evidence.json > app-view.json
|
||
rea inspect-analysis-view app-view.json
|
||
```
|
||
|
||
The request can also name the saved file instead of embedding it:
|
||
`{"source": {"kind": "evidence-file", "path": "app-evidence.json"}, "view": {"kind": "summary"}}`.
|
||
The CLI resolves a relative path against the working directory; MCP clients
|
||
pass an absolute path. REA reads and authenticates the file, then projects it
|
||
like retained Evidence, so a large file is bounded by the file reader rather
|
||
than charged as inline input.
|
||
|
||
Local file inspection commands reject empty paths and NUL bytes before resolving
|
||
relative inputs, auxiliary files or output destinations. An empty string never
|
||
selects the working directory; use an explicit `.` when selecting that directory
|
||
is supported by the command.
|
||
|
||
For example, a 334 MB Obsidian application Evidence file yields a summary view
|
||
of about 10 KB. Replace the view with
|
||
`{"kind": "page", "collection": "modules", "offset": 0, "limit": 32}` for a
|
||
module page. See
|
||
[JavaScript application workflows](javascript-application-workflows.md) for
|
||
views, feature traces and comparisons that take the same saved Evidence.
|
||
|
||
Add `--timings` to a command to write one JSON timing record to stderr. For
|
||
example, `rea inspect-elf-notes program --json --timings > result.json` keeps
|
||
stdout as a parseable result and writes
|
||
`{"rea_timings":{"version":1,"command_ms":42}}` to stderr. `command_ms`
|
||
measures CLI command dispatch and result delivery; it is not provider-only or
|
||
analysis-only time. The flag is off by default, and the record omits stages
|
||
that REA did not measure. Root version reporting does not dispatch a command
|
||
and has no command timing record.
|
||
|
||
## JSON Lines record output
|
||
|
||
Use `--format jsonl` to consume row-oriented results one record per line:
|
||
|
||
```bash
|
||
cat > observations-view.json <<'JSON'
|
||
{"source":{"kind":"evidence-file","path":"app-evidence.json"},"view":{"kind":"javascript-facts","property_keys":[],"offset":0,"limit":32}}
|
||
JSON
|
||
rea inspect-analysis-view observations-view.json --format jsonl > observations.jsonl
|
||
```
|
||
|
||
This request selects up to 32 observation identities and their provenance,
|
||
without observation property values.
|
||
|
||
This changes JSONL object-collection framing. Scripts that need one complete
|
||
report or a scalar field should use `--format json`. For a full application
|
||
graph, first project a supported analysis view rather than selecting an
|
||
arbitrary nested graph array.
|
||
|
||
A top-level array emits its elements in source order. An object must have one
|
||
clear `rows`, `items`, `records`, or `occurrences` array, directly or under
|
||
`page`. Evidence uses the same boundaries inside `normalized_result`.
|
||
Any other selection is refused before a record is written, including an
|
||
arbitrary report, a scalar, a nested array elsewhere, and a columnar object such
|
||
as the `inspect-sqlite-database --table` `normalized_result.rows`
|
||
(`{columns, values}`). The refusal is the caller error `invalid_request`
|
||
(`invalid_input`) and names the selection and its observed shape; the analysis
|
||
itself succeeded, so use `--format json` for the complete document. If several
|
||
collections exist, select one with `--filter-output`, for example
|
||
`normalized_result.rows` for an analysis view.
|
||
|
||
Object collections finish with a control record:
|
||
|
||
```json
|
||
{
|
||
"$rea": "jsonl/v1",
|
||
"type": "metadata",
|
||
"records": {
|
||
"path": ["normalized_result", "rows"],
|
||
"emitted": 2,
|
||
"source": 10,
|
||
"filter": "normalized_result.rows[0,2]"
|
||
},
|
||
"context": {
|
||
"evidence_id": "ev_…",
|
||
"normalized_result": {
|
||
"coverage": {
|
||
"status": "page",
|
||
"examined": 10,
|
||
"total": 100,
|
||
"next_offset": 10,
|
||
"exhausted": false
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
This abbreviated example illustrates framing. The actual `context` preserves
|
||
all non-row fields, including Evidence subject, provider, authority, confidence,
|
||
locations, links, limitations, parent/view digests, pagination and coverage.
|
||
With `--full-output`, it also retains the command envelope and its metadata.
|
||
A data filter selects row fields/slices while this context remains available;
|
||
excluded peer record collections and unselected Evidence `raw_result` are not
|
||
restored. An explicitly selected `raw_result` remains selected. Retain the
|
||
original Evidence for complete observations.
|
||
|
||
`records.emitted` and `records.source` count the selected output and its source
|
||
array; they do not assert analysis completeness. Source coverage and unknowns
|
||
retain their original meaning. Existing row identities and indices remain
|
||
unchanged unless your field selection excludes them. Empty object collections
|
||
still emit metadata; an empty top-level array emits no lines. Bare top-level
|
||
arrays have no synthetic coverage record.
|
||
|
||
Command and output-validation failures emit `{"$rea":"jsonl/v1","type":"error","ok":false,"result":…}`
|
||
and retain the original diagnostic payload. SDK validation/sentinel errors use
|
||
the SDK's exit status rather than guessing from data fields. `$rea` is reserved
|
||
at the top level of a data record, so control records are unambiguous; collisions
|
||
are refused before rows are written. Nested `$rea` values are ordinary data.
|
||
A missing final metadata record or nonzero exit can indicate an interrupted
|
||
export; do not treat already emitted rows as a complete result.
|
||
|
||
`--token-count` estimates this exact representation, including metadata, one
|
||
line at a time. Errors remain error records even with token counting enabled.
|
||
Token windows cannot preserve JSON Lines and are rejected. JSON encoding and
|
||
stdout writes are incremental, including large escaped strings; this is output
|
||
streaming over a completed result, not incremental analysis. The analysis still
|
||
needs its normal resources. A single record must fit the runtime string limit
|
||
for token estimation; remove `--token-count` to stream a larger record.
|
||
|
||
Standalone version and MCP doctor diagnostics remain single JSON documents
|
||
in JSON Lines mode. Incur's streaming generator events and successful sentinel
|
||
responses retain their SDK protocol. MCP application results and Evidence
|
||
contracts are unchanged.
|
||
|
||
## Shell completion
|
||
|
||
The CLI includes optional shell completion. Run `rea completions --help` to
|
||
see the available shells and their setup commands. For Bash, load the hook
|
||
into the current shell:
|
||
|
||
```bash
|
||
eval "$(rea completions bash)"
|
||
```
|
||
|
||
For zsh, initialize completion before loading the hook. If your shell already
|
||
runs `compinit`, place the hook after that existing initialization:
|
||
|
||
```zsh
|
||
autoload -Uz compinit
|
||
compinit
|
||
eval "$(rea completions zsh)"
|
||
```
|
||
|
||
The Bash hook supports macOS system Bash 3.2. It uses optional completion
|
||
features when the shell provides them and falls back to portable registration.
|
||
Completion includes standard global options and the active command's options;
|
||
`rea mcp ` offers only `add` and `doctor`. Output-option values remain data when
|
||
resolving the command scope.
|
||
|
||
For future interactive non-login Bash sessions, add the command to
|
||
`~/.bashrc`. Interactive login Bash sessions read the first available file in
|
||
`~/.bash_profile`, `~/.bash_login`, and `~/.profile`; add the command to the
|
||
file your shell reads, or have that file source `~/.bashrc`. Remove the command
|
||
from the startup file to stop loading completion, and run `complete -r rea` to
|
||
remove the registration from the current shell. These are manual, opt-in
|
||
steps.
|
||
Completion is a discovery aid; use `rea COMMAND --help` to confirm the
|
||
available options and subcommands.
|
||
|
||
## Managed assemblies (.NET)
|
||
|
||
For a .NET PE/CLI assembly, inspect its identity and deployment form before
|
||
inspecting members. These commands read metadata and Common Intermediate
|
||
Language (CIL) without loading or executing the assembly and require no native
|
||
analysis engine:
|
||
|
||
```bash
|
||
rea inspect-managed-artifact /absolute/path/Assembly.dll --json
|
||
rea inspect-managed-members /absolute/path/Assembly.dll --json
|
||
rea inspect-managed-members /absolute/path/Assembly.dll --type Example.Parser --json
|
||
```
|
||
|
||
Replace `Example.Parser` with an exact `full_name` from the member inspection,
|
||
or a declaring type name you already know. Omit `--type` to inspect all members,
|
||
or repeat it to select several types.
|
||
Selection limits decoding to the selected types; member references remain an
|
||
assembly-wide inventory, so the output can still be large.
|
||
|
||
Select `--detail summary --evidence-bundle /absolute/path/members.json` to
|
||
receive identity, selection, coverage, limitations and collection counts while
|
||
saving the complete original Evidence in a new bundle. Summary requires that
|
||
explicit destination; existing files are preserved. Later
|
||
`inspect-analysis-view` calls can page the exact parent from the saved bundle
|
||
without reopening the assembly. See
|
||
[CLI summary and saved member rows](managed-code-analysis.md#cli-summary-and-saved-member-rows)
|
||
for the complete command sequence. Omitted or `--detail complete` retains the
|
||
full inline response.
|
||
|
||
Choose the workflow from the artifact's contents rather than its `.exe` or
|
||
`.dll` extension. The managed parser requires PE/CLI metadata; it does not
|
||
unpack .NET single-file hosts or decode IL2CPP. For the image, type and method
|
||
names in a Unity `global-metadata.dat`, use
|
||
[IL2CPP metadata](il2cpp-metadata.md). NativeAOT method bodies require native
|
||
analysis. See the [managed-code guide](managed-code-analysis.md)
|
||
for supported deployment forms, selection semantics, and coverage limits.
|
||
|
||
## SQLite snapshots
|
||
|
||
For a local database snapshot, use `rea inspect-sqlite-database PATH --json`.
|
||
Add `--table NAME --row-limit 20` to read a bounded sample from an ordinary
|
||
table. This command does not require a native analysis provider. See
|
||
[SQLite database snapshots](sqlite-databases.md) for database/WAL identity,
|
||
source-file handling and row-value semantics.
|
||
|
||
## Native analysis
|
||
|
||
Inspect embedded Go build metadata without installing Go or configuring a
|
||
disassembler:
|
||
|
||
```bash
|
||
rea inspect-go-binary /absolute/path/to/program --json
|
||
```
|
||
|
||
The shared CLI/MCP workflow reports compiler version, modules, replacements,
|
||
build settings, original bytes and SHA-256. See [Go binaries](go-binaries.md)
|
||
for supported containers and absent or malformed metadata.
|
||
|
||
Inspect a Godot PCK without running the game:
|
||
|
||
```sh
|
||
rea inspect-artifact game.pck --detail summary --evidence_output evidence.json --json
|
||
rea extract-artifact game.pck --json
|
||
```
|
||
|
||
Formats 2, 3 and 4 retain header, directory and integrity facts. Compact delivery
|
||
keeps complete Evidence in the requested bundle. See [Godot PCK](godot-pck.md)
|
||
for embedded packs, batch queries and unknown encrypted coverage.
|
||
|
||
Inspect a UnityFS AssetBundle without running Unity:
|
||
|
||
```sh
|
||
rea inspect-artifact content.bundle --detail summary --evidence_output evidence.json --json
|
||
rea extract-artifact content.bundle --json
|
||
```
|
||
|
||
Format versions 6 through 8 retain header, block and directory facts with
|
||
none/LZ4/LZ4HC block decompression. See [UnityFS](unityfs.md) for encryption
|
||
and LZMA coverage boundaries.
|
||
|
||
Inspect an ESP32-family app or bootloader image without esptool:
|
||
|
||
```sh
|
||
rea inspect-artifact firmware.bin --json
|
||
rea extract-artifact firmware.bin --json
|
||
```
|
||
|
||
The inventory lists each segment with its ESP-IDF load classification, the
|
||
checksum and SHA-256 results, the descriptor and any Secure Boot v2 signature
|
||
blocks. See [ESP images](esp-image.md) for recognition, coverage and refusals.
|
||
|
||
Inspect an ESP flash dump, or one read from a flash address other than 0:
|
||
|
||
```sh
|
||
rea inspect-artifact flash.bin --json
|
||
rea inspect-artifact dump.bin --target-format esp-flash-image --flash-offset 0x1000 --json
|
||
rea extract-artifact flash.bin --json
|
||
```
|
||
|
||
The inventory lists the bootloader, the partition table and each partition,
|
||
with ESP image facts for app partitions and the OTA records. See
|
||
[ESP flash images](esp-flash-image.md) for table location, inputs and limits.
|
||
|
||
Inspect a Node.js SEA carrier without running it:
|
||
|
||
```sh
|
||
rea inspect-artifact ./application --json
|
||
rea extract-artifact ./application --json
|
||
```
|
||
|
||
See [Node.js SEA](node-sea.md) for supported layouts, script and asset paths,
|
||
fuse observations, and the real-fixture verification lane.
|
||
|
||
Inventory and extract Intel HEX or S-record regions:
|
||
|
||
```sh
|
||
rea inspect-artifact ./firmware.hex --json
|
||
rea extract-artifact ./firmware.hex --json
|
||
```
|
||
|
||
See [Intel HEX and S-record images](hex-records.md) for recognition, region
|
||
names, metadata and limits.
|
||
|
||
Inspect and extract stored U-Boot legacy uImage payloads and components:
|
||
|
||
```sh
|
||
rea inspect-artifact ./firmware.bin --detail summary --evidence_output firmware.json --json
|
||
rea extract-artifact ./firmware.bin --json
|
||
```
|
||
|
||
See [U-Boot uImage](uimage.md) for verified CRCs, physical component ranges,
|
||
unknown names, resource limits and retained views. Compressed bytes stay stored.
|
||
|
||
`inspect-artifact` and `extract-artifact` also read ZIP members compressed with
|
||
Zstandard (standard method 93). They retain the shared ZIP CRC, declared-size,
|
||
overlap and archive expansion checks. The native streaming decoder keeps the
|
||
existing 128 MiB Zstandard window cap; frames requiring a larger window are
|
||
refused. Deprecated method 20 and other unregistered ZIP codecs remain unsupported.
|
||
|
||
Inventory and extract U-Boot FIT images:
|
||
|
||
```sh
|
||
rea inspect-artifact ./boot.itb --json
|
||
rea extract-artifact ./boot.itb --json
|
||
```
|
||
|
||
See [U-Boot FIT images](fit-images.md) for recognition, data sources, hashes
|
||
and limits.
|
||
|
||
Inspect local model declarations without loading weights:
|
||
|
||
```bash
|
||
rea inspect-model-artifact /absolute/path/model.gguf --json
|
||
rea inspect-model-artifact /absolute/path/model.safetensors --hash full --json
|
||
```
|
||
|
||
Default hashing reads only headers and leaves full-file identity unknown.
|
||
`--hash full` streams the complete file for SHA-256. Both return typed metadata,
|
||
original tensor declarations and derived totals. MCP summaries retain complete
|
||
Evidence for filtered metadata, tensor and type-total pages; CLI compact delivery
|
||
uses `--detail summary --evidence_output FILE`. See [model files](model-artifacts.md).
|
||
|
||
Inspect Unity IL2CPP metadata names without Unity or a disassembler:
|
||
|
||
```bash
|
||
rea inspect-il2cpp-metadata /absolute/path/global-metadata.dat --json
|
||
```
|
||
|
||
The command decodes a version 24, 27, 29, 31 or 39 `global-metadata.dat` and reports assembly
|
||
images, type, method and member names, string literals, index ranges, header
|
||
sections and SHA-256. With `--binary PATH` it also reads the matching
|
||
`GameAssembly.dylib`, `GameAssembly.dll` or `libil2cpp.so` (never runs it) and
|
||
links each method to its code address; `--generic-instances` adds generic
|
||
method instances and `--ghidra-seeds PATH` then writes Ghidra function seeds. It does not decrypt metadata. MCP defaults to compact counts
|
||
with retained Evidence; query indexed rows with filters, batch indices and
|
||
selected fields. The CLI defaults to complete Evidence;
|
||
`--detail summary --evidence_output FILE` exports the full result to a new file
|
||
and returns a compact overview. See [IL2CPP metadata](il2cpp-metadata.md).
|
||
|
||
Inspect an embedded cargo-auditable Rust dependency list without installing
|
||
Rust or configuring a disassembler:
|
||
|
||
```bash
|
||
rea inspect-rust-binary /absolute/path/to/program --json
|
||
```
|
||
|
||
The same workflow reports package names, versions, sources, the root package,
|
||
dependency edges, and the section's file range. A missing `.dep-v0` section is
|
||
null metadata, not proof that the binary has no dependencies. See
|
||
[Rust binaries](rust-binaries.md).
|
||
|
||
Inspect a PE Authenticode certificate table without executing the image or
|
||
verifying signatures:
|
||
|
||
```bash
|
||
rea inspect-pe-certificates /absolute/path/to/application.exe --json
|
||
```
|
||
|
||
The workflow reports `WIN_CERTIFICATE` entries and, for PKCS#7 signedData,
|
||
embedded X.509 serial, issuer, subject, and validity fields. A missing security
|
||
directory is observed absence of that table. See
|
||
[PE Authenticode certificate tables](pe-certificates.md).
|
||
|
||
Inspect PE import and delay-load tables without executing the image:
|
||
|
||
```bash
|
||
rea inspect-pe-imports /absolute/path/to/application.exe --json
|
||
```
|
||
|
||
The workflow reports DLL names, named and ordinal functions, delay-load
|
||
descriptors, and a pefile-compatible import hash of the standard import table.
|
||
A missing import directory is observed absence of that table. See
|
||
[PE import and delay-load tables](pe-imports.md).
|
||
|
||
Inspect ELF PT_NOTE and SHT_NOTE tables without executing the image:
|
||
|
||
```bash
|
||
rea inspect-elf-notes /absolute/path/to/program --json
|
||
```
|
||
|
||
The workflow reports note owners, types, descriptor hashes, and GNU build-id
|
||
or ABI-tag fields when those descriptors match. A missing note table is
|
||
observed absence of those declarations. See [ELF note tables](elf-notes.md).
|
||
|
||
Inspect an Apple `.ips` crash report without executing a target:
|
||
|
||
```bash
|
||
rea inspect-apple-ips /absolute/path/to/report.ips --json
|
||
```
|
||
|
||
The workflow reports first-line metadata, process/exception/termination facts,
|
||
threads, usedImages, lossless integer registers, and documented crash classes
|
||
when `bug_type` is `"309"`. Other DiagnosticReports types return no crash
|
||
result. See [Apple IPS crash reports](apple-ips.md).
|
||
Inspect Linux kernel module metadata without executing the module or requiring
|
||
`modinfo`:
|
||
|
||
```bash
|
||
rea inspect-kernel-module /absolute/path/to/module.ko --json
|
||
```
|
||
|
||
The workflow reports ordered `.modinfo` keys (duplicate `alias` and `parm`
|
||
entries preserved), `__versions` symbol CRCs, presence of
|
||
`.gnu.linkonce.this_module` and `__ksymtab` sections, observed `.init.text`
|
||
and `.exit.text` sizes, and an appended signature trailer. `struct module` is
|
||
not decoded and the signature is not validated. Other ELF types are not
|
||
applicable. gzip, xz, and zstd containers are unsupported and name
|
||
`external_decompression` as the next probe: decompress with an external
|
||
utility, then inspect the decompressed file. See
|
||
[Linux kernel modules](kernel-modules.md).
|
||
|
||
Inspect a CPython 3.7–3.13 `.pyc` header and nested marshal code objects
|
||
without executing bytecode or installing Python:
|
||
|
||
```bash
|
||
rea inspect-python-pyc /absolute/path/to/module.pyc --json
|
||
```
|
||
|
||
The workflow reports the pinned CPython version for the magic, PEP 552 hash or
|
||
mtime/size fields, nested function names, and bytecode SHA-256. Bytecode is
|
||
not decompiled. See [CPython pyc inspection](python-pyc.md).
|
||
|
||
Inspect an Erlang BEAM file without executing bytecode:
|
||
|
||
```bash
|
||
rea inspect-erlang-beam /absolute/path/to/app.beam --json
|
||
```
|
||
|
||
The workflow reports IFF chunk names, atom tables, exports, imports, lambdas,
|
||
and the Code chunk header. Missing optional tables stay null. See
|
||
[Erlang BEAM files](erlang-beam.md).
|
||
|
||
Configure [Hopper or Ghidra](installation.md#hopper), the
|
||
[IDA adapter](ida-provider.md), or [Kuna](kuna-provider.md) before analyzing a
|
||
native target. Substitute
|
||
your target, search text and function name or address in these examples:
|
||
|
||
```bash
|
||
rea analyze /absolute/path/to/program --provider ghidra --json
|
||
rea search /absolute/path/to/program "search" --provider ghidra --json
|
||
rea function /absolute/path/to/program main --provider ghidra --json
|
||
rea decompile /absolute/path/to/program 0x1000 --provider ghidra --json
|
||
rea xrefs /absolute/path/to/program 0x1000 --provider ghidra --json
|
||
rea trace /absolute/path/to/program "search" --provider ghidra --json
|
||
```
|
||
|
||
`analyze` and `inspect` share the native overview workflow. `function` returns
|
||
a function dossier; `decompile` returns pseudocode. For assembly instructions
|
||
alone, use `rea instructions`. A macOS `.app` bundle can be supplied directly.
|
||
|
||
A complete dossier can reach several megabytes of value-flow and references.
|
||
`rea function ... --detail summary --evidence_output /absolute/path/dossier.json` returns the
|
||
compact summary and saves the complete dossier as an Evidence bundle, never
|
||
overwriting an existing file. Page that bundle without reanalysis by naming it
|
||
as an `inspect-analysis-view` source, selecting the summary's
|
||
`normalized_result.parent_evidence_id` when the bundle holds several records:
|
||
`{"source": {"kind": "evidence-file", "path": "/absolute/path/dossier.json", "evidence_id": "ev_…"}, "view": {"kind": "native", "facet": "pseudocode", "offset": 0, "limit": 4096}}`.
|
||
MCP `analyze_function` defaults to the same summary and retains the complete
|
||
dossier in the session for `inspect_analysis_view`.
|
||
|
||
Run `rea --help` or a command's `--help` for arguments and output options.
|
||
The [generated catalog](mcp-contracts.md#generated-catalog) lists all CLI commands and MCP
|
||
tools. Provider-specific target support is described in the
|
||
[native guide](native-investigation.md), [DOS guide](ghidra-dos.md),
|
||
[raw binary guide](raw-binary.md), [GameCube DOL guide](ghidra-dol.md), and
|
||
[Windows Ghidra guide](windows-ghidra-p0.md).
|
||
|
||
## Choose a provider
|
||
|
||
List the available providers and their supported operations:
|
||
|
||
```bash
|
||
rea providers --json
|
||
rea capabilities --json
|
||
```
|
||
|
||
These commands describe binary-session providers and auxiliary capabilities.
|
||
For the complete MCP surface, use the connected server's tool list and the
|
||
session's advertised availability; individual guides describe prerequisites.
|
||
|
||
With automatic selection, REA uses the single available provider that supports
|
||
the target. When several providers support it, select one explicitly:
|
||
|
||
```bash
|
||
rea analyze /absolute/path/to/program --provider hopper
|
||
```
|
||
|
||
Set `REA_ANALYSIS_PROVIDER` for a standing preference. An explicit `--provider`
|
||
overrides it. In MCP, pass `provider_id` to `open_binary`:
|
||
|
||
```json
|
||
{
|
||
"path": "/absolute/path/to/program",
|
||
"provider_id": "hopper"
|
||
}
|
||
```
|
||
|
||
The selected provider remains bound to the session until an explicit switch
|
||
or close. Provider failures are returned with their original reason. For an
|
||
`ambiguous` selection error, choose from `details.candidate_ids`; for
|
||
`provider_unavailable`, follow `message`, `remediation.action`, and the original
|
||
reasons in `details.rejections`. Each rejection names its `origin`: `availability`
|
||
entries describe local readiness failures, including missing configuration or an
|
||
invalid existing registration; they do not establish target support. `target`
|
||
entries record a refused kind, format, architecture, or role. An availability-only
|
||
message describes the recorded rejections without claiming that every provider
|
||
is unconfigured or that target support is known.
|
||
For Hopper, Ghidra, IDA, JADX, and Kuna, run
|
||
`rea doctor --provider ID --json` to diagnose the selected engine. Other adapters,
|
||
such as `pwntools-elf`, report their configuration requirements directly and are
|
||
not accepted by `doctor --provider`. See
|
||
[task readiness](installation.md#check-readiness-for-your-task) and
|
||
[provider selection](mcp-contracts.md#identity-and-discovery).
|
||
|
||
The session reports work still in progress through `analysis_activity`.
|
||
A client timeout can end its wait while the provider continues analyzing.
|
||
`cleanup_incomplete` identifies resources whose shutdown or removal could not
|
||
be verified. See [MCP contracts](mcp-contracts.md) for session lifecycle and
|
||
[Ghidra first-query deadlines](mcp-contracts.md#ghidra-first-query-deadlines-and-recovery)
|
||
for import, client timeouts and recovery.
|
||
|
||
If one-shot analysis fails and closing its session also fails, the original
|
||
error details remain inline and `details.cleanup_error` describes the later
|
||
close failure. Each diagnostic retains its own reported resources; the later
|
||
failure does not establish whether an earlier resource was released. Captured
|
||
output and partial observations remain in the original details.
|
||
|
||
## Save and reuse analysis snapshots
|
||
|
||
A snapshot retains successful analysis results for later queries. REA reuses
|
||
an exact result when the target bytes, operation, parameters, provider and
|
||
settings match. Mutations and cursor-dependent calls are excluded from the
|
||
cache. Snapshot files are local and use owner-only permissions.
|
||
|
||
One-shot binary CLI Evidence reports the exact path argument in `caller_input_path`,
|
||
including relative spelling and symlink aliases. Its subject identifies the
|
||
inspected artifact; `caller_input_path` does not change the Evidence ID or
|
||
snapshot query. A replay reports the current invocation's path while leaving
|
||
the saved Evidence unchanged. Provider inspections continue to include the
|
||
established `parameters.input_path` field.
|
||
|
||
Importing an Evidence bundle refreshes retained request and subject display metadata.
|
||
It preserves cached analysis results, including observations for other targets.
|
||
Snapshot bindings that use a replaced record retain its updated display path.
|
||
|
||
Dispatching a provider metadata write invalidates the active session's snapshot cache,
|
||
even if its reply fails: the provider may already have applied the write.
|
||
Close and reopen the target before importing or saving another snapshot.
|
||
|
||
Saved snapshots include only cache entries whose subject identifies the selected
|
||
target. Unbound observations and observations of child artifacts remain available
|
||
in the open session and its Evidence bundle; they are excluded from the target's
|
||
saved replay entries without changing their subjects.
|
||
|
||
Snapshots retain eligible target-scoped question histories in full, including
|
||
mutation Evidence recorded while another target was active. Observations and
|
||
related questions must still belong to the saved target. If those dependencies
|
||
cannot be retained, the entire history is excluded instead of reverting the
|
||
question to an earlier revision or disposition.
|
||
|
||
```bash
|
||
rea analyze /absolute/path/to/program --provider ghidra --snapshot /absolute/path/to/analysis/program.json
|
||
# Repeat the same query to reuse its saved result.
|
||
rea analyze /absolute/path/to/program --provider ghidra --snapshot /absolute/path/to/analysis/program.json
|
||
```
|
||
|
||
An exact CLI cache hit is normally read before starting a provider process.
|
||
On Windows, Ghidra first admits the selected source through its native snapshot
|
||
owner, so replay can start that provider.
|
||
In MCP, `open_binary` accepts `snapshot_path` to import a snapshot atomically
|
||
for its matching target; an MCP provider may still start before returning a
|
||
cached result. `close_binary` accepts `snapshot_path` and optional
|
||
`overwrite: true` to save before releasing provider resources. A failed save
|
||
leaves the session open so the caller can resolve the output failure.
|
||
|
||
If a selected snapshot's file descriptor cannot close, `open_binary` reports
|
||
`cleanup_incomplete`. `binary_session` reports `cleanup_pending` even when no
|
||
target opened. Retry `close_binary` to release the retained descriptor before
|
||
opening another target.
|
||
|
||
If a snapshot switch fails after the previous target was retired, restoration
|
||
keeps that target's analysis commitments with the replacement client. This
|
||
includes the native dispatch workflow profile on a profileless route, so its
|
||
existing snapshot and fresh observations retain the same provenance.
|
||
|
||
For native dispatch, the snapshot binding retains the reader profile; workflow
|
||
Evidence and `workflow_entries` retain the composed profile, which commits that
|
||
reader and the workflow's analysis caveats. Restoration preserves both profiles.
|
||
|
||
The MCP save receipt reports `primitive_entries`, `workflow_entries`, and
|
||
`evidence_records` separately. Zero primitive bindings can still accompany retained workflow
|
||
results and Evidence. These are cached observations, not a saved provider
|
||
database: only eligible exact queries can reuse a result, and new or live
|
||
queries can still require provider startup. The CLI uses the same snapshot
|
||
format and preserves these records when loading and updating it.
|
||
|
||
## Import, export and compare Evidence
|
||
|
||
Evidence records retain artifact identity, source locations, observations,
|
||
inferences and unresolved findings. Validate an existing bundle, export its
|
||
canonical form, or compare two supplied bundles:
|
||
|
||
```bash
|
||
rea evidence-import /absolute/path/to/evidence/bundle.json
|
||
rea evidence-export /absolute/path/to/evidence/bundle.json /absolute/path/to/evidence/canonical.json
|
||
rea compare /absolute/path/to/evidence/left.json /absolute/path/to/evidence/right.json
|
||
rea compare left.json right.json --record_pairs '[{"left_evidence_id":"ev_<left-sha256>","right_evidence_id":"ev_<right-sha256>"}]' --json
|
||
```
|
||
|
||
Migration: replace `rea compare-bundles` with `rea compare`; the former command
|
||
has been removed. The MCP tool remains `compare_bundles`.
|
||
|
||
`rea compare` compares records by Evidence identity and explicit pairs. It does
|
||
not read artifact contents: the inventories of two builds are unresolved
|
||
records, or one opaque change when you pair them. Use `compare-artifacts`,
|
||
described at the end of this section, to compare their contents.
|
||
|
||
`--record_pairs` supplies an explicit JSON array of observation pairs, matching
|
||
MCP `compare_bundles.record_pairs`. Pair IDs must exist on their named sides and
|
||
be one-to-one. Without pairs, one-sided membership remains unknown; REA does
|
||
not infer correspondence from position or display names.
|
||
|
||
Exports preserve an existing destination unless `--overwrite` is explicit.
|
||
|
||
Atomic Evidence output stages on the selected destination's filesystem. If the
|
||
OS pathname ceiling prevents its internal staging name, REA retries in a shorter
|
||
ancestor on that same filesystem and removes the unused stage. Existing output
|
||
protection still applies. A genuine pathname refusal uses `resource_constraint`
|
||
with `details.reason: path-limit` and `boundary: filesystem-path`.
|
||
|
||
For application traces and version comparisons, see
|
||
[JavaScript application workflows](javascript-application-workflows.md).
|
||
MCP [retained Evidence references](mcp-contracts.md#retained-application-evidence-inputs)
|
||
belong to one connection. Separate CLI calls can consume full saved records.
|
||
`inspect-analysis-view REQUEST.json --evidence-bundle BUNDLE.json` resolves
|
||
the request's `retained-evidence` source ID from that authenticated bundle,
|
||
including bundles exported from an MCP session. The same view JSON works for
|
||
every supported parent kind; no target is reopened and no record is selected
|
||
by file order. Omit `--evidence-bundle` when the request supplies inline Evidence.
|
||
|
||
Compare two artifact inventories with `compare-artifacts`. It takes the same
|
||
`left` and `right` input as MCP `compare_artifacts`: complete Evidence, or an
|
||
`evidence-file` source with an optional `evidence_id` that selects one record
|
||
from a bundle. Relative paths resolve from the working directory:
|
||
|
||
```bash
|
||
rea inspect-artifact old.zip --json > old.json
|
||
rea inspect-artifact new.zip --json > new.json
|
||
rea compare-artifacts '{"left":{"kind":"evidence-file","path":"old.json"},"right":{"kind":"evidence-file","path":"new.json"}}' --json
|
||
```
|
||
|
||
A one-shot command keeps no session Evidence, so it refuses a
|
||
`retained-evidence` source with `details.reason: "missing"`. Select the record
|
||
from a saved bundle instead.
|
||
For an artifact summary selected from a bundle, include its complete parent
|
||
inventory in the same bundle. The command resolves that parent from the
|
||
authenticated file records; a summary-only file cannot supply missing facts.
|
||
|
||
## Import historical source
|
||
|
||
Historical source inventories preserve exact filenames, including literal backslashes
|
||
on POSIX hosts. Relative import lookup uses the recorded source file's owning
|
||
directory and decodes URL percent escapes once; encoded slash, NUL and malformed
|
||
escapes remain unresolved. A decoded backslash remains part of a filename rather
|
||
than a directory separator. This inventory lookup does not claim that Node can
|
||
load that filename or apply Node's runtime file-URL admission policy.
|
||
|
||
Import an older source tree to compare with the current artifact:
|
||
|
||
```bash
|
||
rea import-reference-source /absolute/path/to/source
|
||
rea import-reference-source /absolute/path/to/source --detail summary --outline-depth 2 --json
|
||
```
|
||
|
||
The default (`--detail full`) returns the complete `HistoricalSourceGraph`.
|
||
Choose `--detail summary` for a `HistoricalSourceSummary` of that same committed
|
||
inventory: its `HistoricalSourceManifest` contains the semantic graph and root
|
||
digests, with provenance, VCS facts, languages, manifest paths, entry and
|
||
relationship counts, parse-failure counts, file/directory states, exclusions
|
||
and limitations. The summary is a presentation view, not a graph accepted by
|
||
source-to-bundle comparison; keep the full output for that workflow.
|
||
|
||
Known filesystem failures commit the operation, parsed errno, entry-relative
|
||
location and recovery guidance. Entry `local_diagnostics`, when present,
|
||
retains the exact host message, including local paths, outside the semantic
|
||
graph commitment. Equivalent permission failures therefore retain their manifest
|
||
identity after relocation while preserving original source diagnostics in full
|
||
output and visible summary outline entries. Other substantive limitations still
|
||
participate in the graph commitment.
|
||
|
||
An unreadable child directory remains a partial inventory entry. Directory-scoped
|
||
ignore policy is required before enumerating a directory that can be opened;
|
||
an unreadable or invalid policy still fails the import. Project negations cannot
|
||
override defaults or caller exclusions; caller patterns retain their own ordered
|
||
negations. Configured secret exclusions remain final.
|
||
|
||
`--outline-depth` requires `--detail summary` and accepts a nonnegative safe
|
||
integer. Depth 1 shows root children, depth 2 (the default) also shows their
|
||
children, and depth 0 omits all outline entries. Paths retain canonical Unicode
|
||
code-point order. `outline.omitted_entry_count` counts only inventoried entries
|
||
hidden by the chosen depth; it does not count unexamined children of excluded or
|
||
unreadable directories, or change `inventory_state`. Exclusions and source
|
||
limitations remain separate from this presentation omission.
|
||
|
||
`file_sizes.known_bytes` is the exact sum of known inventoried file sizes as
|
||
decimal text, so large sums cannot lose integer precision. Unknown sizes count
|
||
in `file_sizes.unknown_file_count`; excluded or unexamined paths are not included
|
||
in that total. A zero known-byte sum therefore does not establish an empty source
|
||
tree. Full output retains individual file hashes, relationships and parse-failure
|
||
diagnostics. Summary mode performs the same import and cleanup, then projects
|
||
the committed result without rereading source files. It reduces the selected
|
||
output, not the amount of source examined. A shallow tree or many exclusions can
|
||
still produce a large summary; depth 0 removes the outline but preserves the
|
||
other facts. No fixed output-size or runtime improvement is promised.
|
||
|
||
Import failures use the shared error contract (`code`, `category`, `message`,
|
||
`retryable`, and `remediation`). When reads completed before failure,
|
||
`details.diagnostics.partial` retains the reader facts, with file bytes as
|
||
arrays of byte values. Unresolved cleanup uses `cleanup_incomplete` and
|
||
retains the source failure and resource paths in `details`.
|
||
|
||
The import records hashes and metadata for the supplied files, separately
|
||
from observations of the current app. For a directory inside a Git checkout,
|
||
it records the HEAD of the nearest enclosing repository; whether the working
|
||
tree matches that commit remains unknown. Root selection follows filesystem
|
||
symlink/parent semantics before canonical identity is derived. It honors
|
||
`.gitignore` files at the selected root and in traversed child directories,
|
||
with patterns relative to their containing directory and deeper negations
|
||
applied only when the parent was not excluded. Matching uses the nearest
|
||
repository's local `core.ignorecase` setting (false when unset), including the
|
||
shared configuration of linked worktrees. Policy and Git metadata reads retain
|
||
their descriptor cleanup under the import's resource owner; unreadable policy
|
||
is a typed failure, not an empty policy. Above-root `.gitignore` files, global
|
||
excludes, `.git/info/exclude`, config includes and worktree-specific config are
|
||
not loaded. The import also applies default exclusions for common generated and dependency paths such
|
||
as `node_modules/`, `dist/`, and `*.log`. Pattern-based exclusions record the
|
||
matched pattern and whether it came from project, default, caller, or sensitive
|
||
path policy. Links into excluded paths retain their internal target metadata;
|
||
the exclusion explains why the target has no inventory entry, and the inventory
|
||
remains partial. Exclusions record `entry_kind` when the importer observed it;
|
||
older saved exclusions without that field have unknown kind. An excluded
|
||
ancestor proves an internal descendant only when its kind is `directory`.
|
||
Set `REA_REFERENCE_SECRET_PATTERNS_JSON` to a JSON array of ignore
|
||
patterns when you want to mark selected paths as sensitive; those patterns are
|
||
reported separately as configured-secret exclusions.
|
||
|
||
Re-import saved graphs whose pattern-based exclusions lack `pattern` before
|
||
using them in source-to-bundle comparisons.
|
||
|
||
Entries that change during an import retain an unavailable-entry diagnostic
|
||
alongside readable siblings. If enumeration of an already admitted root fails,
|
||
the graph keeps its observed children and records the root failure as a
|
||
top-level limitation. That partial inventory does not establish absence among
|
||
unexamined children. POSIX directory-entry bytes that are not valid UTF-8 cannot
|
||
be represented as graph paths; the containing directory (or root) is marked
|
||
unsupported, and REA does not access the entry through a replacement-character
|
||
name. Symlink targets are read as bytes too: a target that is not valid UTF-8
|
||
is retained as an unreadable symlink with its bytes reported in the limitation,
|
||
and is never resolved through a replacement-character path. Readable siblings
|
||
remain inventoried.
|
||
|
||
JavaScript and TypeScript import parsing requires valid UTF-8. Malformed source
|
||
bytes retain their original hashes and sizes with a decoding diagnostic; REA
|
||
does not infer module targets from replacement characters. Exact dynamic-import
|
||
strings, expression-free templates and runtime-transparent TypeScript wrappers
|
||
retain their targets; computed imports remain unknown. TypeScript import-equals
|
||
dependencies remain `requires` relationships whether or not they are exported.
|
||
Historical dependencies also include TypeScript import types (`import("./dep").T`
|
||
and `typeof import("./dep")`) and declarations inside ambient module bodies;
|
||
these source relationships do not assert runtime loading. Language labels retain
|
||
the conventional case-sensitive `.C` (C++) and `.M` (Objective-C++) suffixes.
|
||
|
||
REA derives historical CommonJS paths only when the selected loader and its
|
||
base are proven: this includes the exact
|
||
`createRequire(new URL("./file.cjs", import.meta.url))` form and
|
||
`require.resolve` with `{ paths: [__dirname] }`. Exact optional-call forms such
|
||
as `require.resolve?.("./dep.cjs")` use the same loader and mutation facts. The
|
||
nearest inventoried package manifest supplies `.js`/`.ts` package facts to the
|
||
shared parser; explicit filename modes take precedence. A known ES module has no
|
||
implicit CommonJS wrapper, including when a factory is derived from an unbound
|
||
`require`. An unreadable, excluded or malformed manifest, or package scope outside
|
||
the selected inventory, leaves otherwise unproven loader resolution unknown with
|
||
the requested specifier and a reason. A nearest manifest without a `type` field
|
||
uses the shared parser's JavaScript module-syntax facts. Unresolved TypeScript,
|
||
JSX compilation and declaration contexts do not establish runtime wrapper bases.
|
||
Other custom bases and search options remain partial with unknown resolution and
|
||
the literal requested specifier. Overwritten resolver methods retain that uncertainty; they are not
|
||
attributed to Node. `require.main` is a module object, so calling it does not
|
||
establish a loader reference. These relationships do not establish execution;
|
||
Node's [`require.resolve`](https://nodejs.org/docs/v24.22.1/api/modules.html#requireresolverequest-options)
|
||
performs lookup without loading the target. An excluded `package.json`
|
||
also leaves package-directory selection unknown rather than treating the
|
||
manifest as absent and applying an `index` fallback.
|
||
|
||
Import requests use URL pathname semantics: query and fragment remain request
|
||
identity, percent escapes decode exactly once for inventory lookup, and encoded
|
||
separators or malformed escapes retain an unresolved relationship and diagnostic.
|
||
URL-bearing relationships retain the authored `source_specifier` separately from
|
||
the inventoried `to` path; distinct query/fragment requests remain distinct even
|
||
when they name the same file. CommonJS requests retain literal filename semantics
|
||
for percent signs, query marks and fragments.
|
||
|
||
Historical-source import requires safe no-follow file opens on Linux or
|
||
macOS. Native Windows returns `unsupported_host`; use Linux REA inside WSL
|
||
or another supported host. See
|
||
[source-to-bundle comparison](javascript-application-workflows.md#historical-source-to-bundle-comparison)
|
||
for mapping identities, inferences and unknowns.
|
||
|
||
## Capture runtime behavior
|
||
|
||
Choose the guide for the target and intended interaction:
|
||
|
||
- [Browser observation](browser-observation.md): inspect a selected page through CDP.
|
||
- [Browser scenarios](browser-scenario-contract.md): run declared interactions and capture their results.
|
||
- [Electron observation](electron-observation.md): inspect an Electron renderer or capture an application scenario.
|
||
- [Electron fuses](electron-fuses.md): read packaged fuse bytes before expecting `--inspect` to reach an Electron main process.
|
||
- [Node/Electron Inspector](javascript-runtime-observation.md): record script locations and execution contexts.
|
||
- [Process capture](process-capture.md): run an executable and compare terminal, exit and filesystem observations.
|
||
|
||
Runtime requests name the target and actions. Launched targets run with your
|
||
user permissions; consult the chosen guide for host requirements and effects.
|
||
|
||
## Serve MCP over streamable HTTP
|
||
|
||
`rea mcp` speaks stdio to the client that launched it. To serve the same MCP
|
||
catalog over streamable HTTP for one local client, such as an agent environment
|
||
runtime, start the dispatcher entry with `--http`:
|
||
|
||
```sh
|
||
rea mcp --http --port 8765
|
||
```
|
||
|
||
`--host` and `--port` default to `MCP_HOST` and `MCP_PORT` from the environment,
|
||
then to `127.0.0.1` and an ephemeral port. A bind outside loopback requires
|
||
`MCP_ALLOWED_HOSTS` or `--allowed-hosts`, a comma-separated list of client host
|
||
names or IP addresses without URLs or ports.
|
||
|
||
Repeat `--allowed-hosts` to append names, or put multiple names in one
|
||
comma-separated value. All occurrences are validated together and deduplicated
|
||
after hostname normalization; the explicit list replaces `MCP_ALLOWED_HOSTS`.
|
||
|
||
For example:
|
||
|
||
```sh
|
||
rea mcp --http --host 0.0.0.0 --allowed-hosts 127.0.0.1,rea.lan
|
||
```
|
||
|
||
Requests with a different `Host` or browser `Origin` receive `403`; a missing
|
||
`Origin` is accepted. The announced client URL uses the bound hostname when
|
||
admitted, otherwise the first declared hostname, with the actual bound port.
|
||
It is written to stderr as one JSON log line (`"msg":"MCP transport listening"`) whatever `REA_LOG_LEVEL`
|
||
is set to; nothing is written to stdout. `rea mcp --http --help` lists the accepted options. The listener serves
|
||
one client at a time: a session ends with the client's `DELETE`, and a client
|
||
that connects while another session is open takes it over, so a client that
|
||
exited without ending its session needs no restart. The listener runs until
|
||
SIGINT or SIGTERM and does not stop when stdin closes. See
|
||
[transports](mcp-contracts.md#transports) for the session, `Host` header and
|
||
shutdown contract. Client registrations written by setup and the MCP registry
|
||
entry keep stdio.
|
||
|
||
## Serve an environment manifest
|
||
|
||
`rea environment-serve <manifest.json> --evidence <capture.json>
|
||
[--replay-only] [--host H] [--port N]` serves an environment manifest
|
||
projected by `inspect-analysis-view` as an agent environment: an MCP server at
|
||
`/mcp`, the environment card at `/.well-known/agent-env.json`, and a
|
||
`/agentenv` data plane. The manifest's `parent_evidence_id` must equal the
|
||
evidence file's `evidence_id`; otherwise the command refuses with
|
||
`invalid_request` before binding. Before binding it derives the
|
||
`environment-state` view of the capture with the manifest's `template_rules`,
|
||
as `inspect-analysis-view` would, and serves from it: read tools whose views
|
||
are state-backed answer from a live state, write tools are served only where
|
||
the capture's later reads confirmed their effect, and
|
||
`/agentenv` serves `data/reset`, `data/add` and `data/get`. When the state
|
||
cannot be derived, or its catalog exceeds the discovery reservation, it logs a
|
||
warning with the error code and message and serves replay; `--replay-only` serves replay without deriving. In replay every
|
||
manifest tool replays the retained capture records it cites and `/agentenv`
|
||
answers every method with JSON-RPC `-32601`. The stopped document's `state`
|
||
member is `{"served": true, "evidence_id": …}`,
|
||
`{"served": false, "reason": "replay-only"}` or
|
||
`{"served": false, "reason": "unavailable", "code": …, "message": …}`; see
|
||
[serve from the state](agent-environments.md#serve-from-the-state).
|
||
|
||
```sh
|
||
rea environment-serve manifest.json --evidence capture.json --port 8765
|
||
```
|
||
|
||
`--host` and `--port` resolve like `rea mcp --http`: flags, then `MCP_HOST` and
|
||
`MCP_PORT`, then `127.0.0.1` and an ephemeral port. A non-loopback bind requires
|
||
`MCP_ALLOWED_HOSTS` with the same hostname rules and Host/Origin gates as above.
|
||
`environment-serve` uses this environment setting; `--allowed-hosts` belongs to
|
||
`rea mcp --http`. For example:
|
||
|
||
```sh
|
||
MCP_ALLOWED_HOSTS=127.0.0.1,rea.lan rea environment-serve manifest.json --evidence capture.json --host 0.0.0.0
|
||
```
|
||
|
||
`ENVIRONMENT_NAME` renames
|
||
the served environment; set but blank, it is refused as `configuration_invalid`
|
||
like a blank `MCP_HOST`. The admitted client URL is written to stderr as one JSON log line
|
||
(`"msg":"MCP transport listening"`). See
|
||
[agent environments](agent-environments.md) for what the state and replay
|
||
return and do not establish.
|
||
|
||
## Build an environment bundle
|
||
|
||
`rea environment-build <capture> [dir] [--name <name>] [--template-rule <rule> ...]
|
||
[--format-rules-only] [--package-tarball <path>] [--capture-timeout-ms <ms>] [--dry-run]` builds the bundle
|
||
`environment-export` writes from one recorded capture: a `.har` file, which it
|
||
inspects as `inspect-web-network-capture` does, or capture Evidence JSON from
|
||
`inspect-web-network-capture` (HAR) or `capture-browser-scenario`. It projects
|
||
the environment manifest from the in-memory Evidence with the opt-in rules
|
||
`after-uid-literal`, `opaque-token`, `sibling-variance` and `prefix-variance`
|
||
unless `--template-rule` or `--format-rules-only` selects others, prepares the
|
||
state, and writes the bundle; every file is byte-identical to the three-step
|
||
path's when that path lists the same rules in the views' order and exports
|
||
under the build's name. A loopback or IP-literal capture is named after its file unless `--name`
|
||
is given. It prints one JSON report: the capture and manifest identities, the
|
||
state, a `coverage` summary of which tools answer from the state, which replay
|
||
and why each write is withheld with a hint, the files, warnings and the next
|
||
command. `--dry-run` writes nothing and may omit the directory.
|
||
|
||
```sh
|
||
rea environment-build grafana.har ./grafana --json
|
||
```
|
||
|
||
See [agent environments](agent-environments.md#build-a-bundle-in-one-step)
|
||
for the report, the hints and what the bundle ships.
|
||
|
||
## Export an environment bundle
|
||
|
||
`rea environment-export <manifest.json> --evidence <capture.json> <dir>
|
||
[--package-tarball <path>] [--name <override>]` writes the same manifest and
|
||
capture pair as a bundle folder the agentenv framework runs with
|
||
`agent-env run <dir>`. It reads each file once, authenticates and cross-checks
|
||
the pair with the same checks as `environment-serve`, checks the bundle name,
|
||
then prepares the state under that name as the bundle's server will at startup
|
||
to predict what the bundle serves (replay when the state is unavailable),
|
||
copies the bytes it authenticated, refuses to write into a directory that
|
||
exists and is not empty, and never overwrites a file.
|
||
|
||
```sh
|
||
rea environment-export manifest.json --evidence capture.json ./grafana-example --json
|
||
```
|
||
|
||
The folder holds a `README.md` with the bundle's provenance, an env folder
|
||
`envs/<name>/` with a `Dockerfile` that installs this exact `rea-agents`
|
||
version and serves the copied `manifest.json` and `evidence.json` (written
|
||
with mode `0600` on POSIX; Windows retains inherited ACLs) with
|
||
`environment-serve` (from the state by default), a Python verifier under
|
||
`artifacts/check_<name>/` that checks the served tool count and one served
|
||
answer, and the smoke task `tasks/smoke.json`. `--package-tarball` copies a packed `rea-agents`
|
||
tarball into the env folder and installs it instead of the registry package,
|
||
which is how the hosted lane proves a checkout rather than a release. `--name`
|
||
replaces the manifest's kebab-case name as the env folder and env id; a device
|
||
name Windows reserves (`con`, `nul`, `com1` and the like) is refused. The
|
||
command prints one JSON document listing the written files with their sizes,
|
||
the pinned package source, the served `tool_count`, and `state` in the shape
|
||
of the serve command's stopped document. See
|
||
[agent environments](agent-environments.md#export-a-bundle) for the folder
|
||
layout and what the smoke task checks.
|
||
|
||
## Compare an environment with its application
|
||
|
||
`rea environment-compare <manifest.json> --evidence <capture.json> --probe <probe.json> [--probe <probe.json> ...]`
|
||
compares the environment the pair serves with probe captures of the application it came from. Each probe is capture
|
||
Evidence of one call sequence recorded against the application after a reset; the environment's state is reset before
|
||
each probe, every request to the application's origin is answered as the served environment answers it, and the
|
||
answer is compared with the application's after ids are mapped and clocks and generated values are normalised. The command reads files only and
|
||
makes no network call. It prints the report and exits `0`, or prints a structured `invalid_request` error and exits `1`
|
||
for the same pair refusals as `environment-serve`, a probe that is not capture Evidence, or a browser probe captured
|
||
without response bodies, or without the request bodies its writes need. See
|
||
[agent environments](agent-environments.md#measure-fidelity-against-the-application) for the report.
|
||
|
||
```sh
|
||
rea environment-compare manifest.json --evidence capture.json --probe probe.json --json
|
||
```
|
||
|
||
## Report version and runtime
|
||
|
||
`rea --version` prints the package version. Add an output format to get the
|
||
version with the runtime and platform context that a support request needs:
|
||
|
||
Version reports do not initialize analysis providers. Invalid global settings
|
||
or output options still report their usual errors.
|
||
|
||
```bash
|
||
rea --version --json
|
||
```
|
||
|
||
```json
|
||
{
|
||
"name": "rea-agents",
|
||
"version": "6.7.0",
|
||
"runtime": { "id": "node", "version": "24.22.1" },
|
||
"platform": "linux",
|
||
"architecture": "x64",
|
||
"supported_runtime": {
|
||
"range": "^22.23.4 || ^24.22.1 || >=26.12.1",
|
||
"satisfied": true
|
||
}
|
||
}
|
||
```
|
||
|
||
| Field | Meaning |
|
||
| ------------------------------------ | -------------------------------------------------------------------------------------------------------------- |
|
||
| `name`, `version` | Published package name and version; `version` matches plain `--version`. |
|
||
| `runtime.id` | `node` or `bun`. |
|
||
| `runtime.version` | Version of that runtime. |
|
||
| `runtime.node_compatibility_version` | Bun only: the Node.js version it reports compatibility with. |
|
||
| `platform`, `architecture` | Node.js `process.platform` and `process.arch`, such as `linux` and `x64`. |
|
||
| `supported_runtime.range` | Supported Node.js range. |
|
||
| `supported_runtime.satisfied` | Whether this runtime is supported, judged as `rea doctor` judges it; Bun by its Node.js compatibility version. |
|
||
|
||
The field names are stable. The report contains no paths, environment values,
|
||
user or host names, and it starts no provider and reads no target. `--format`
|
||
selects any CLI output format, such as `--format yaml`. JSON follows the usual
|
||
rule: indented on a terminal, compact when piped or redirected. `--help` still
|
||
takes precedence. With other output controls, `--version` keeps its existing
|
||
behavior: valid controls such as `--filter-output` or `--full-output` print the
|
||
plain version, and invalid combinations report the usual output-flag error,
|
||
such as `UNSUPPORTED_OUTPUT_COMBINATION` for `--json` with `--token-limit`. Use
|
||
`rea doctor` for provider and host readiness.
|
||
|
||
## Output and exit status
|
||
|
||
Commands reject surplus positional arguments before execution, including commands
|
||
that accept no positionals. Use `rea <command> --help` for the declared argument
|
||
shape; a final variadic argument continues to accept multiple values.
|
||
|
||
`rea mcp doctor --json` checks production MCP startup and reports child logs in
|
||
`diagnostics`. It drains stderr while retaining at most its first 64 KiB of raw
|
||
bytes as text, omitting a final UTF-8 code point if it crosses that byte limit.
|
||
`stderr_bytes` counts all observed bytes, including omitted output;
|
||
`stderr_truncated` indicates that output exceeded the retention limit. The same
|
||
diagnostics appear inside the report when `--full-output` is selected. Log
|
||
truncation alone does not change the diagnostic health or exit status.
|
||
|
||
The default terminal format is TOON. Use `--json` when saving results for a
|
||
JSON consumer. Valued output flags (`--format`, `--filter-output`,
|
||
`--token-limit`, `--token-offset`) accept `--flag value` or `--flag=value`; a
|
||
missing value fails with `MISSING_OUTPUT_VALUE` before the command runs.
|
||
Malformed format values and token windows fail with `INVALID_OUTPUT_VALUE` in the
|
||
selected structured format. Token limits and offsets must be nonnegative safe
|
||
integers; zero selects an empty window. These checks run before command execution. `--json` is indented on a terminal and compact when piped or
|
||
redirected, which keeps large results smaller for agents and scripts; pipe
|
||
through `jq .` for indented files. A formatting refusal exits nonzero even when
|
||
the underlying operation succeeded.
|
||
|
||
`--format md` escapes backslashes, pipes, and inline Markdown syntax in table
|
||
headers and cells so source text is displayed literally, and renders cell line
|
||
breaks as `<br>`. Underscores between letters or digits, as in `source_index`,
|
||
cannot form emphasis and stay unescaped. These are presentation changes; retained
|
||
Evidence and JSON output keep the original values. Nested objects and arrays
|
||
use JSON inside their owning Markdown cells, retaining the field names and
|
||
values instead of replacing them with an object label. Table widths are measured
|
||
iteratively, so an admitted row page is not limited by the JavaScript function
|
||
argument count.
|
||
|
||
Markdown refuses selected NUL characters and unpaired UTF-16 surrogates because
|
||
renderers or UTF-8 output would substitute them. TOON refuses selected unpaired
|
||
surrogates for the same UTF-8 reason. Use JSON or YAML to retain those values.
|
||
An authored replacement character and valid supplementary characters are not
|
||
treated as malformed.
|
||
|
||
`--format csv` and `--format tsv` export one selected row collection. Select an
|
||
exact array path with `--filter-output` and provide comma-separated direct
|
||
scalar columns with `--columns`; column order is the output column order:
|
||
|
||
```bash
|
||
rea inspect-analysis-view request.json --format csv \
|
||
--filter-output normalized_result.rows --columns name,address
|
||
```
|
||
|
||
The first line is a REA metadata record,
|
||
`# rea-delimited-v1 <JSON>`. It carries the selected path, view request, limitations and unknowns,
|
||
coverage, source coverage and completeness, command, and duration. A metadata
|
||
value of `null` means that the selected row collection did not provide that
|
||
fact; it does not mean complete coverage. The following line is the header.
|
||
CSV records use CRLF line endings; TSV records use LF. Cells containing the
|
||
delimiter, quotes, or line breaks are quoted and embedded quotes are doubled.
|
||
Values are emitted as UTF-8 without Unicode normalization. Missing and `null`
|
||
cells are empty; booleans and finite numbers use their ordinary text forms.
|
||
String cells and headers that begin with spreadsheet formula syntax are refused
|
||
with a structured error; unpaired surrogates in selected cells or headers are
|
||
also refused. JSON retains those values unchanged.
|
||
|
||
Delimited output does not flatten nested Evidence or nested field values.
|
||
It returns a structured error and a nonzero status if the filter does not
|
||
select exactly one row array, a row is not an object, or a selected cell is
|
||
nested or formula-leading. JSON remains the complete canonical output; use it
|
||
to retain fields outside the selected columns or preserve exact formula-leading
|
||
text. Token counting and token windows are not
|
||
available for CSV/TSV because slicing would corrupt records or metadata.
|
||
|
||
`--filter-output` selects fields from successful command data. Classified failures
|
||
retain their complete typed diagnostic projection even when the selector matches
|
||
a field such as `code` or `message`, or names a missing field. `--full-output`
|
||
controls whether the outer `{ok, error, meta}` envelope is included.
|
||
Selected own JSON members, including `__proto__`, remain ordinary output data.
|
||
|
||
Sibling array slices that select different fields are combined by original row
|
||
index and emitted in source order, regardless of selector order. Selecting a
|
||
whole parent row keeps that row intact and takes precedence over narrower fields
|
||
selected from the same row. For example, `rows[0,2].name,rows[1,3].version`
|
||
combines both fields for overlapping rows, while `rows[0,2],rows[1,3].version`
|
||
retains rows 0 and 1 whole.
|
||
|
||
For `cutter-command`, `--json-output` requests command-specific JSON from
|
||
Cutter when available. This is separate from `--format json`, which selects the
|
||
CLI result representation; the global `--json` flag selects that same CLI
|
||
representation.
|
||
|
||
Cutter no longer accepts command-local `--json=true` or `--json=false`.
|
||
Replace these with `--json-output=true` or `--json-output=false`, respectively.
|
||
Combine `--json-output` with bare `--json` when both Cutter command output and
|
||
the CLI result should use JSON; either argument order is accepted.
|
||
|
||
| Status | Meaning |
|
||
| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||
| `0` | The operation completed. Its result may include partial evidence, warnings or unresolved questions. |
|
||
| `1` | The operation could not complete. Structured output identifies invalid input, permissions, cancellation, timeouts or another failure when available. |
|
||
| `128 + N` | Signal-derived status. An unhandled signal may have ended the process, with the shell or runtime preserving the conventional status; a command may instead have handled the signal, waited for its operation and cleanup to settle, and assigned the same status (`130` for SIGINT, `143` for SIGTERM). In the handled case, it may also have printed a structured `cancelled` result. |
|
||
|
||
`rea mcp --http` is long-running and prints no structured result. It exits
|
||
`130` on SIGINT or `143` on SIGTERM after closing the listener, the session and
|
||
its providers, and `1` when that cleanup fails.
|
||
|
||
`rea environment-serve` is also long-running: it serves until SIGINT or
|
||
SIGTERM, closes the listener, then prints one structured
|
||
`{"status":"stopped", …}` document naming the URL, environment and tool count
|
||
and exits `130` or `143` through the shared cancellation handler below.
|
||
Refusals before binding (unreadable files, non-Evidence JSON, capture Evidence
|
||
in a format other than HAR, a manifest or state whose parent is not the
|
||
supplied evidence, an unbindable port) print a structured
|
||
error and exit `1`, as does a listener that fails after binding.
|
||
If shutdown fails, the command reports `cleanup_incomplete` with the retained
|
||
server resources. It preserves a primary listener diagnostic, or a
|
||
`shutdown_observation` naming the requested shutdown and its URL, environment,
|
||
tool count and Evidence identifiers. The successful `stopped` document is
|
||
returned only after cleanup is confirmed. The same in-process server owner
|
||
retains failed stages for retry; the CLI exposes no separate retry command.
|
||
`rea environment-build` is one-shot too: it prints its report and exits `0`,
|
||
or prints the refusing stage's structured error and exits `1`, writing nothing
|
||
when the refusal comes before the bundle is written. SIGINT cancels it with a
|
||
`cancelled` error; a cancellation before the bundle stage writes nothing.
|
||
`rea environment-export` is one-shot: it prints the written files and exits
|
||
`0`, or prints a structured `invalid_request` error and exits `1` for the same
|
||
pair refusals, a non-empty or file-occupied directory, a missing package
|
||
tarball or a name that is not a kebab-case label or is a Windows device name,
|
||
writing nothing in each case. A file occupying the destination is reported as
|
||
requiring a directory; identity-change diagnostics are reserved for admitted
|
||
paths that fail later identity checks. Host permission failures identify whether
|
||
input reading or bundle writing was denied and retain the reported path.
|
||
A write that fails after the first file (a permission, disk-space or
|
||
pathname-length failure on a later path) propagates as the host's failure,
|
||
naming its cause and, when it failed on one bundle file or the package
|
||
tarball, that file. A failure while syncing the finished folder names only its
|
||
cause. Either way the partial folder stays in place for inspection; remove it
|
||
before retrying, since the next attempt refuses the non-empty directory.
|
||
|
||
A `cancelled` result does not by itself determine the exit status; it depends on
|
||
the command's signal handling. Commands routed through the shared
|
||
`withCommandCancellation` handler in the CLI set status `130` for SIGINT or
|
||
`143` for SIGTERM after their operation settles. Current source registrations
|
||
use that handler for process capture and comparison (`capture-process`,
|
||
`compare-process-captures`), JavaScript application analysis (`analyze` when it
|
||
selects the JavaScript lane, and `analyze-javascript-application`), selected
|
||
browser, web and Electron commands (including `analyze-web-bundle`,
|
||
`observe-web-execution`, `capture-browser-scenario` and
|
||
`capture-electron-scenario`), and the platform and provider command families
|
||
for Android/ADB, Apktool/JEB/Flutter, firmware, PCAP and signal analysis,
|
||
WASM/EVM, Go/Rust and binary diagnostics, Frida instrumentation, the Cutter
|
||
command, objdump, Rizin and GDB, and the agent environment server
|
||
(`environment-serve`). The handler also wraps `doctor`,
|
||
`inspect-sqlite-database`, `recover-javascript-sources`,
|
||
`inspect-il2cpp-metadata`, and `inspect-desktop-application`. In these paths, a returned structured
|
||
`cancelled` result can be printed before the wrapper assigns the
|
||
signal-derived status. This describes the current source routing; it is not a
|
||
claim that every command or provider runtime has been independently exercised.
|
||
|
||
Commands with `--evidence_output` select their delivered view, then publish the
|
||
complete Evidence bundle atomically. Cancellation before publication leaves no
|
||
new file. A publication that has committed can win a later cancellation; the
|
||
`cancelled` result then keeps the written path and byte count under
|
||
`details.diagnostics.evidence_bundle`. If staging cleanup stays incomplete
|
||
after commit, the `cleanup_incomplete` error reports the same file as
|
||
`details.published_path` and `details.bytes`.
|
||
|
||
When the reader of piped output closes early, as `rea ... | head` does, the
|
||
CLI stops writing, prints no error and exits `141` (`128 + 13`, the status of a
|
||
SIGPIPE-terminated writer). The operation itself may have completed; only its
|
||
output was declined.
|
||
|
||
`inspect-artifact` also returns a structured cancelled result. Its logging
|
||
wrapper sits outside the cancellation handler and replaces the signal-derived
|
||
status with the analysis failure status, so it exits `1`. Setup has its own contract: cancellation exits `0`. Treat the
|
||
structured result and process status as separate signals when scripting
|
||
cancellation.
|
||
|
||
`setup --dry-run` returns `planned` and exits `0`; a cancelled setup also exits
|
||
`0`. Setup returns `1` for `needs_confirmation` or `needs_human`.
|
||
`doctor` returns `1` when required checks in its selected readiness scope fail;
|
||
unavailable optional providers remain informational for unrelated tasks.
|
||
|
||
Enable `pipefail` in a supporting shell so a downstream formatter preserves
|
||
REA's failure status:
|
||
|
||
```bash
|
||
set -o pipefail
|
||
rea inspect-artifact ./app.asar --json | jq . > inspection.json
|
||
```
|
||
|
||
`--token-count` reports a tokenx estimate for the selected emitted document,
|
||
including a requested full-output envelope and metadata. JSON uses the same
|
||
terminal indentation or compact piped representation as normal output; JSONL
|
||
uses its emitted line representation. The final newline is included (tokenx
|
||
assigns whitespace no tokens). This is an estimate, not a model tokenizer count.
|
||
Counting retains the existing bounded formatted-string allocation after capacity
|
||
admission; omit the flag to stream large JSON documents without materializing
|
||
the complete formatted text. Exhausted token windows display an empty range at
|
||
the total token count and have no next offset.
|
||
|
||
### `inspect-devicetree`
|
||
|
||
Inspect an explicit local FDT v16/v17 blob without execution or an external tool.
|
||
Returns header, reservations, node/property tree, derived standard values and
|
||
selected phandle references. See [devicetree inspection](devicetree.md) for
|
||
limits and retained-view pagination.
|
||
|
||
```bash
|
||
rea inspect-devicetree /artifacts/board.dtb --json
|
||
rea inspect-devicetree /artifacts/board.dtb --detail summary --evidence_output /artifacts/board-evidence.json --json
|
||
```
|
||
|
||
### PE TLS and load configuration
|
||
|
||
```sh
|
||
rea inspect-pe-load-config /absolute/path/to/application.exe --json
|
||
```
|
||
|
||
Reports TLS callbacks and size-covered load-config fields without execution.
|
||
Use `--max-callbacks` and `--max-file-bytes` to set read budgets. See
|
||
[PE TLS and load configuration](pe-load-config.md).
|
||
|
||
Bun standalone executables use the existing `inspect-artifact` and
|
||
`extract-artifact` commands to inventory and materialize embedded source,
|
||
sourcemaps and bytecode without running the executable. See
|
||
[Bun standalone executables](bun-standalone.md) for layout coverage and limits.
|