Compare commits

..
541 changed files with 2990 additions and 37145 deletions

No files matched your search

@@ -2,7 +2,7 @@
name: reverse-engineer-anything
description: Reverse engineer native, managed, Electron/JavaScript, packaged, firmware, and browser targets with REA, and build agent environments (AgentEnv bundles) of web applications from a live instance or a capture, and of Linux desktop apps with a task from a request. Use shipped-artifact or requested runtime evidence to explain features, compare versions, decompile code, or guide a reconstruction. Skip REA for ordinary source-repository architecture analysis.
metadata:
version: "61"
version: "62"
---
# REA
@@ -110,7 +110,7 @@ explicit path or endpoint and do not need it.
absolute output directory. These tools use caller-supplied Binwalk/Unblob on
Linux; see the [firmware guide](https://github.com/morluto/rea/blob/main/docs/guides/firmware/firmware-analysis.md).
- ESP32-family app or bootloader image (`0xE9` magic): `inspect_artifact`
without a provider; see the ESP section of `references/native-and-artifacts.md`.
without a provider; read [ESP images](references/firmware-and-raw-images.md#esp-images).
- .NET NativeAOT PE/ELF: native Ghidra analysis. Native code can yield pseudocode;
consult the NativeAOT workflow in `references/native-and-artifacts.md` for
optional metadata recovery and its supported host/layout boundary.
@@ -276,14 +276,6 @@ explicit path or endpoint and do not need it.
proof the image never imported APIs through another table. Bound snapshots
and loaded IAT contents remain unobserved. See the
[PE import guide](https://github.com/morluto/rea/blob/main/docs/pe-imports.md).
- Windows Shell Link shortcuts: `inspect_windows_shortcut` with the explicit
local `.lnk` file. This built-in target-free inspection requires no Windows
SDK or COM. Preserve header flags and FILETIMEs, IDList items, LinkInfo
volume and network names, StringData arguments and icon location, ExtraData
signatures, and Tracker droid facts labelled derived. Unknown IDList items
and ExtraData signatures stay raw with SHA-256. The target path is not
resolved or executed. See the
[Windows shortcut guide](https://github.com/morluto/rea/blob/main/docs/guides/native/windows-shortcut.md).
- PE TLS callbacks and load configuration: `inspect_pe_load_config` with the
explicit local PE32/PE32+ file. This built-in target-free inspection requires
no Windows SDK. Preserve callback VAs as strings with their RVA, file offset
@@ -539,8 +531,14 @@ as complete while required questions remain open.
## Read only the relevant guide
- Native binaries, managed assemblies, archives, and extraction:
- Native binaries and managed assemblies:
[references/native-and-artifacts.md](references/native-and-artifacts.md)
- Firmware, headerless images, ESP images, and address-bearing containers:
[references/firmware-and-raw-images.md](references/firmware-and-raw-images.md)
- Archive inventory, extraction, Godot PCK, UnityFS, wxapkg, and Bun:
[references/packages-and-archives.md](references/packages-and-archives.md)
- Offline format headers, metadata, signatures, and database snapshots:
[references/format-inspectors.md](references/format-inspectors.md)
- ASARs, extracted JavaScript, feature tracing, and version comparison:
[references/javascript-applications.md](references/javascript-applications.md)
- Android APK declarations, classes, methods, and static references:
@@ -72,13 +72,6 @@ the first source that has the app:
`/usr/share/applications` without `.desktop`, such as
`org.inkscape.Inkscape`. Add `window_class` when you know it; Inkscape's
is `Inkscape`. AbiWord's entry is `abiword` and its class `Abiword`.
- To find the entry, read the package's file list: its page on
packages.ubuntu.com, or `apt-get download PKG && dpkg -c PKG_*.deb` where
apt runs. The entry may ship in a companion `-data` or `-common` package,
as Planner's in `planner-data`: read that package's list too. The entry's `StartupWMClass` may not be the window's real
class, so do not take `window_class` from it: MuseScore 3's says
`mscore3`, its window's is `MuseScore3`. The export uses the class the
capture saw.
## Drive the app
@@ -104,25 +97,16 @@ the first source that has the app:
environment's name.
- Prefer shortcuts and typing to long pointer paths: `key` takes `keys`, an
array holding one chord's keys, such as
`{"action": "key", "keys": ["Control", "s"]}`, and `type` turns `\n` into
`{"action": "key", "keys": ["ctrl", "s"]}`, and `type` turns `\n` into
Enter.
- Before `type`, check in a screenshot that a text cursor or a focused
field is there. In apps with single-key shortcuts, such as Inkscape, text
typed without one runs as commands.
- Save As differs by app. Check the dialog's current folder first: most
open in the home, so click Documents in the sidebar, but Scribus opens in
`~/Documents` and its sidebar has no Documents entry. Type only the file
name. A dialog taller than the 1280×800 screen hides its buttons:
maximize it from its title bar, or confirm it with `key` Return, as when
HomeBank's Save button is off-screen.
- Answer the app's save questions, such as keeping the format, then check
the saved path in the title or status bar, or the dialog's folder when
the app shows only the name, and that it shows no unsaved mark, before
you mark a checkpoint or check. Where the title never shows one, as in
HomeBank, a greyed Save or Revert, or the file reopened from recent
files, confirms the save.
- Qt spin boxes change their value on scroll: move the pointer off them
before you scroll.
- Save As opens in the home: click Documents in its sidebar and type only
the file name. Answer the app's save questions, such as keeping the
format, then check the saved path in the title or status bar, or the
dialog's folder when the app shows only the name, and that it shows no
unsaved mark, before you mark a checkpoint or check.
- What the app saved under the home before `seed`, such as a document in
`~/Documents` or its settings, is part of the seed. A file you move to the
Trash is left out, and the export lists it in `limitations`. Trash a stray
@@ -142,30 +126,12 @@ the first source that has the app:
1. `start_build_computer` with
`{"source": {"kind": "request", "desktop_entry": ..., "install": ...}}`.
The first start of an app builds its image, and starts at the same time
on this host wait for each other; either can take minutes. A failed
start returns the build's error: fix the request, or stop when the
The first start of an app builds its image and can take minutes. A
failed start returns the build's error: fix the request, or stop when the
install cannot work, and close any `computer_id` the error names.
2. `start_build_capture`. It returns once the app's window is open. A
`package_lists_error` may stop the capture finding the package's
sources; the export says when that matters. When the app exits before
its window opens, the error gives its exit code and the end of its
stderr, which may name what it lacks, such as a Java runtime: try
another install source, or stop as for an install this flow cannot do.
- REA counts a file as the app's document only when the app's window
title shows its file name or `~/` path. When the title shows something
else, such as MuseScore's score title, pass the path you will save the
document at in `state_paths`, relative to the home:
`{"computer_id": ..., "state_paths": ["Documents/MuseScore3/Scores/Morning_Song.mscz"]}`.
It need not exist yet. Add the app's settings folder when its name
differs from the app's, such as `.config/MuseScore`, so the seed keeps
the dismissed first-run dialogs. Do not pass the document as a launch
argument to make REA see it: `state_paths` is the way.
- An app whose document is a folder of pages, such as a Zim notebook
(`Notebooks/Notes`, a `.txt` file per page), never shows a file in its
title: pass the folder in `state_paths`. Every page in it is then a
document, so the bundle opens none at launch and the app starts where
it keeps its place; check the page the task changes.
sources; the export says when that matters.
3. Make the seed through the app and save it from the app under
`~/Documents`: build the document in the app, or write its source where
the app edits its format as text, then save it again from the app. Make
@@ -175,57 +141,14 @@ the first source that has the app:
otherwise starts on its own screen. For a starting file of the requester's,
read it here and rebuild it in the app with synthetic data in place of
personal data; REA cannot copy a file into the computer.
- An app that keeps its own data and saves after every change, such as
Osmo's contacts in `~/.local/share/osmo/contacts_records.xml`, has no
document: that state is the seed. Build it in the app and mark `seed`
once the app has written. To see whether it has, mark a throwaway
checkpoint, such as `probe-1`, and read its `changed_files`: up to ten
files that changed since the checkpoint before, those under
`state_paths` first, then those the app held open, caches last, with
`more_changed_files` counting the rest.
- The app's own compressed format works when it is gzip, such as
Gnumeric's `.gnumeric`, Dia's `.dia` or Inkscape's `.svgz`: REA reads
the document inside, and checks point into it as into the same
document uncompressed. A format REA reads as binary, such as one
compressed with xz, has no view: save in a format the app offers that
REA reads, then reopen the file once and check that a plain Ctrl+S
keeps that format.
4. Only once the seed is saved, `mark_build_checkpoint` with name `seed`.
5. Make the change the task asks for, as a user would, and save it.
6. Only once the change is saved, `mark_build_checkpoint` with name `end`.
7. `stop_build_capture`. Read `draft.files` (each file that changed, with
its net change and its `values`), `draft.task.checks` (suggested
checks), `draft.guards`, `draft.notes` and `limitations`. When `draft`
is null, read `draft_refusal` first: a missing `seed` or `end`
checkpoint means capturing again on a new build computer, while a failed
draft command names its own cause to fix. When `draft.files` is empty,
`draft.notes` says why:
- `no_change`: nothing was saved between `seed` and `end`. Capture again
on a new build computer, and save before marking `end`.
- `unowned_changes` with `no_document`: the app saved your document
where REA did not count it as the app's. Its `files` list what changed,
with their roles; the document is among them. Close the computer and
capture again on a new one with that path in `state_paths`; the export
refuses a check on a file outside the owned paths.
- `uncheckable_changes`: the app saved the change in dconf, as gedit does
its preferences, and no check reads dconf. Saving again does not help:
choose a change the app saves in a file, and capture again on a new
build computer.
- `unreadable_changes`: a dconf database changed but REA could not read
it; `error` says why. This is not evidence the app saved there: report
the error rather than changing the task.
A note with a `file` says why that file's suggestions are thin:
- `by_position`: only position names what changed; its `elements` lack
a naming attribute. Check an ancestor's text, or give the element a
name in the app, such as an id, and capture again.
- `view_state_only`: every change is view state, a renumbered id or a
time the app stamps, and its `pointers` are the checks kept. The edit
was likely not saved: capture again and save it before `end`.
- `long_text`: the element's text is too long to check whole; check a
phrase in it with `text_contains`.
- `cut_view` or `unreadable_view`: REA's view holds only part of the
file, or none a check can read, such as a binary format (step 3).
its net change), `draft.task.checks` (suggested checks) and
`limitations`. When `draft` is null, read `draft_refusal` first: a
missing `seed` or `end` checkpoint means capturing again on a new build
computer, while a failed draft command names its own cause to fix.
## Write the task
@@ -235,37 +158,18 @@ Write `{"name", "prompt", "checks"}` from the draft and what you did.
- `prompt`: what the requester wants, in their words and the app's terms,
naming the document as a user would and ending with saving it: "Change the
poster's headline in Documents/poster.svg to Summer Bake Sale and save
it." Leave saving out for an app that saves on its own, after every change
or a few seconds after the last one, such as Osmo or Gnote: grading waits
until the app's files have held for 3 s, so the solving agent need not
wait for the save. A save more than 3 s after the agent stops is still
missed; name such an app in the report. Never say how the file stores it,
and never give the checks.
it." Never say how the file stores it, and never give the checks.
- Checks are file-based: content checks that prove the edit, never only that
a file exists or changed. Start from the suggestions; keep those that say
what the user did and drop what follows from how the app saves.
- Compare exact values, so a typo or a wrong number fails.
- Read each file's `values` before writing checks: every value the change
added, changed and removed, with `before` and `after` (a scalar, or an
element as compact XML, cut at 600 characters) at the pointer a check
names it by, so copy the pointer and the `after` value into a check.
Only XML files, archives, JSON and key files have values: a text file's
or SQLite database's `change` already lists its lines and rows.
`refers_to` says what a number stands for, such as `category="44"`
naming `<cat key="44" name="Groceries"/>`; use it for the prompt's words.
Skip entries whose `note` is `by_position` (no check can use that
pointer) or noise (`view_state`, `renumbered`, `volatile`,
`timestamp`, `upkeep`). `values_cut` means a limit left some out.
- Select elements by what they hold, never by position: SVG text by id,
`/root/children/[name=g][@inkscape:label=Layer 1]/children/[name=text][@id=headline]/children/[name=tspan]/children/0`,
or an ODF paragraph by style,
`/entries/[name=content.xml]/view/root/children/[name=office:body]/children/[name=office:text]/children/[name=text:p][@text:style-name=Title]/children/0`.
A selector must match exactly one element, so give the target a unique id
or style in the seed. The draft's pointers already do this: a name-like
attribute such as Scribus's `ANNAME` before a coordinate, and two
attributes together where one is not enough, as a spreadsheet cell's
`[@Col=3][@Row=2]`. Only an element's text is read by position, as
`children/0`.
or style in the seed. The draft may suggest positional pointers; rewrite
them. Only an element's text is read by position, as `children/0`.
- When an app may split an element's text into runs, check it whole:
`text_equals` (or `text_contains` for a value inside a sentence) on the
element compares all its text, however it is split. Add
@@ -274,43 +178,15 @@ Write `{"name", "prompt", "checks"}` from the draft and what you did.
`{"kind": "text_contains", "file": "Documents/memo.abw", "pointer": "/root/children/[name=section]/children/[name=p][@xid=6]", "value": "at 3:30 PM"}`.
Point a text check at the paragraph or another element the edit keeps,
never at a run such as AbiWord's `[name=c]`: a reopened document splits
the text into several runs. The draft lists only what changed; an
unchanged element is in `draft.guards` when it is a like sibling of a
changed one, and otherwise use the app's usual numbering, such as another
paragraph's `xid`, and let the export confirm it.
- In an ODS spreadsheet no attribute names a row or a cell. Name the sheet
in the seed, and check its whole text: `text_equals` on
`/entries/[name=content.xml]/view/root/children/[name=office:body]/children/[name=office:spreadsheet]/children/[name=table:table][@table:name=Q1 Sales]`,
or a stretch of it with `text_contains`. Cell texts join with nothing
between, so a row reads `February23100`.
- For a line added to free text, such as an item on a note's list, the
draft compares the whole text with `text_equals`. This rejects longer or
negated items, but can reject a correct edit with different ordering or
spacing. Report that limitation when the task permits such variations.
- Some apps keep text in an attribute, such as Scribus's `<ITEXT CH=...>`.
`text_equals` cannot join it, so check the attribute with `value_equals`:
`/root/children/[name=DOCUMENT]/children/[name=PAGEOBJECT][@ANNAME=Headline]/children/[name=StoryText]/children/[name=ITEXT]/attributes/CH`.
A partial edit the app restyles splits the text into several runs and
fails that check; list it in the report's limitations.
- When the draft shows no values or no checks, a scratch export can expose
`envs/<env>/seed/` only after the task has at least one valid check: it must
pass on the captured end state and fail on the seed. Before the first
successful export, use `act_on_build_computer` to open the saved file in an
available GUI text editor or viewer and inspect its actual representation
without changing it. Use the visible saved content and the task's edit to
write a check; an export refusal can also name the end state's value, such
as `is "44", not "1"`. If the file cannot be inspected or expressed by a
supported check, report the limitation instead of guessing a check.
the text into several runs. The draft lists only what changed; to name an
unchanged element, such as another paragraph's `xid`, use the app's usual
numbering and let the export confirm it, or read it in the exported
bundle's `envs/<env>/seed/`.
- Where easy, add one check that the rest is untouched, such as another
text's value the edit must leave alone. `draft.guards` offers them at
their seed values, which the end holds too: a field on the nearest like
siblings, one the user sets, such as Planner's `work`, before a default
they all share; the text an addition kept; a text file's lines beside its
new one. Copy one or two into `checks`; they pass on the seed, which is
fine, since the export needs only one check to fail there.
- The draft suggests a decimal the app writes as text, such as `146.83`,
as a number with `tolerance: 0.001`; keep the tolerance for any number
you check that way.
text's value the edit must leave alone. It is the one unchanged value
worth checking; it passes on the seed too, which is fine, since the export
needs only one check to fail there.
- Give `value_equals` a `tolerance` for a number the app writes as text.
- When the draft shows an XML document's changes as `lines`, REA read it
as text, for example because its DOCTYPE declares entities. Selectors are
impossible there, and `line_count` with `contains` only approximates a
@@ -339,17 +215,6 @@ is no longer empty. Keep
`seed_files`, `scrubbed_files`, `unscrubbed_files` and `limitations` for the
report.
The limitations name the folders the app wrote that the bundle neither owns
nor seeds, such as `~/.config/MuseScore`, so the app starts without them:
expect its first-run wizard, a tour or a "restore session?" question at every
boot. If the witness shows one, capture again on a new build computer with
those folders in `state_paths`, and dismiss the dialogs before `seed`. Lock
files the app holds while it runs, such as LibreOffice's `.lock` and
`.~lock.<name>#`, are left out of the seed and listed after "Left out of the
seed", so you need not close the document or delete them before `seed`. The
limitations also name seeded WebKit or Chromium storage and caches; when the
witness's boot is clean, list them in the report.
Keep the build computer open until the witness holds: a task fix exports
again from it.
@@ -366,19 +231,12 @@ checks. Give every `check_build_task` the task exactly as exported.
seed holds exactly one and the app takes a file. When the export's
`limitations` say it opens no document, open it from the prompt's path,
as a solving agent would. Take a `wait` screenshot first, since the
desktop may not have drawn yet. When the start fails because the
computer never got ready, and its error names lock files in the bundle's
seed, delete them from the bundle and start again.
desktop may not have drawn yet.
2. `check_build_task` with `stage: "seed"` and `directory`, the absolute
path of a new or empty folder for the record. A check must fail.
Recording starts. Later stages take no `directory`.
3. Do the task from the prompt with `act_on_build_computer` and save.
`check_build_task` with `stage: "after_edit"`: every check must pass.
Each check waits until the app's files have held for 3 s, as the
bundle's verifier does, so an app that saves on its own needs no wait of
yours. A result with `settled: false` means the files were still
changing at the 15 s bound, and `read_cut: true` that a slow read was
still running there.
4. Make a near miss on top of it: a plausible wrong edit, such as a typo or
a wrong number, and save. `check_build_task` with `stage: "near_miss"`:
a check must fail.
@@ -428,9 +286,7 @@ Report:
`witness.json` and the recording;
- the seed's contents: `seed_files` and what each holds;
- `scrubbed_files` and `unscrubbed_files`; an unscrubbed file still names
the host or user, so it needs review before sharing. `recompressed_files`
are gzip seeds whose headers were cleared, their content unchanged: give
their count;
the host or user, so it needs review before sharing;
- limitations: the capture's, the export's and anything you could not do;
- solver runs, if any: the model, the runs and the passes;
- the build time and the questions you asked.
@@ -308,20 +308,9 @@ Run `rea describe-desktop-application-task CAPTURE --format json`. Read:
and last version, with its `role`, the `baseline` and `final` versions and
the net `change`. Text changes list each added and removed line once, and
`line_counts` gives lines found at both ends whose count changed.
- `values` in each of those files: what the change added, changed and
removed, `before` and `after`, at the pointer a check names it by, with
`refers_to` naming the element a key such as `category="44"` stands for.
Skip entries noted `by_position` or as noise. Only XML files, archives,
JSON and key files have values: a text file's or database's change
already lists its lines and rows.
- `task`: a draft whose `checks` are suggestions from the net change of the
documents the user worked on, or of the app's own state when no document
changed.
- `guards`: optional checks that the rest kept its seed values, such as a
like sibling's field or a text file's neighbouring lines; add one or two
to show the rest is untouched.
- `notes`: why `files` is empty, or why a file's suggestions are thin, such
as `by_position` when no attribute names the changed element.
### Write the prompt
@@ -357,9 +346,7 @@ document saved as a zip archive, such as FreeCAD's `.FCStd`, reads as its
entries: point into an entry's view, and name array members by selector
rather than position, as
`/entries/[name=Document.xml]/view/root/children/[name=Object][@name=Hole]`,
so the check survives an app version that writes them in another order. A
gzip-compressed document, such as Gnumeric's `.gnumeric`, reads as the
document inside: point into it as into the same document uncompressed. Give
so the check survives an app version that writes them in another order. Give
`value_equals` a `tolerance` to compare a number the app writes as text, such
as `"5.0000000000000000"`, with a number. Check what the user wrote in a
document with `text_equals` on its paragraph element, not `value_equals` on a
@@ -0,0 +1,76 @@
# Firmware and raw images
## Firmware regions and extraction
Use `inspect_firmware_regions` when advertised. Use `extract_firmware` when
extraction is requested, with a caller-selected new absolute output directory.
These tools use caller-supplied Binwalk/Unblob on Linux; see the
[firmware guide](https://github.com/morluto/rea/blob/main/docs/guides/firmware/firmware-analysis.md).
## Headerless raw images and firmware containers
For other headerless images (embedded firmware without ELF/Mach-O/PE/MZ
headers), open with `format: "raw-binary"`, `architecture` (`x86`, `x86_64`,
`arm`, `arm64`, `6502`, or `m68k`), and `entry_point` (`0x` hex). File offset 0 is mapped at
that address. Hopper and IDA refuse this interpretation; Ghidra uses
`BinaryLoader` with a generic language: big-endian for `m68k`, little-endian
otherwise. Big-endian ARM, Thumb-only, and Cortex-M variants are unsupported.
`6502` selects NMOS 6502; the entry and complete file mapping must fit within
`0x0000..0xffff`. 65C02, 65816, cartridge containers, bank switching, and mapper
semantics are unsupported. `m68k` requires an even entry and decodes the 68040
superset of the 68000 family; Amiga/Atari containers and vector-table entry
discovery are unsupported. Windows Ghidra P0 admits raw images only for `6502`. See [raw binary guide](https://github.com/morluto/rea/blob/main/docs/providers/raw-binary.md).
Intel HEX, S-record, UF2 and DfuSe files are containers, not raw images:
`inspect_artifact` lists address regions (UF2 per familyID, DfuSe per element
with its alternate setting), and `extract_artifact` writes each region. Open an
extracted region as `raw-binary` with its start address as `entry_point`. UF2
numbering gaps leave coverage unknown; a DfuSe suffix CRC mismatch follows
`integrity_policy`. See [UF2 and DfuSe](https://github.com/morluto/rea/blob/main/docs/uf2-dfuse.md).
## ESP images
For an ESP32-family app or bootloader image, `open_binary` then
`inspect_artifact` with `detail: "summary"`. `root_format_metadata` holds the
observed `header` and `descriptor` (its `kind` is the app or bootloader role)
and the derived `chip`, `checksum`, `appended_sha256` and
`undescribed_trailing` ranges. Segment rows (`segment-NN.bin`) carry
`load_addr`, `classification` (`mapped`, `loaded`, `not-loaded` or `unknown`,
per ESP-IDF `should_map`/`should_load`; RAM ranges are not checked) and
`source_location`; select them with `inspect_analysis_view` and
`logical_paths`. Signature blocks are `unverified`; `public_key_sha256`
matches eFuse key digests.
Unknown `source_coverage` means truncation or undescribed trailing bytes, such
as the flash segments of a `--ram-only-header` image; do not infer absence
from it. Erased flash is described. Integrity mismatches are facts, not
refusals. ESP8266 images are refused, and flash-encrypted images are not
recognized. Segments are not disassembled, and RISC-V or Xtensa segments
cannot be opened as raw-binary. See
[ESP images](https://github.com/morluto/rea/blob/main/docs/esp-image.md).
## ESP flash images
A whole-flash dump opens as `esp-flash-image`. `root_format_metadata` holds
`partition_table` (its `method`: `default`, `caller` or a derived
`scan`), `bootloader` and `apps` ESP image facts, the `otadata` records,
`partitions_outside_dump` and `boot_selection`. Every table has a
`boot_selection`: its `basis` names the bootloader's branch (`no-otadata`
without otadata, `none` when the bootloader rejects the table, `unknown`
when a record is missing), and it lists its `assumptions`;
`running_partition` is always `unknown`. Each image names its entry
`path`. With `detail: "summary"` the root kind is `esp-flash-image-summary`
and each image is a compact `summary` (chip, entry point, segment count,
classified load ranges, signature block count, descriptor identity,
integrity); complete detail keeps every segment. Partition rows
(`partitions/<label>.bin`) carry the declared `type`, `subtype`, names,
`flash_address`, `size` and flags; `encrypted` is a storage flag, not
proof of ciphertext. `written_offset`/`written_end` bound the bytes that
are not erased: an app slot whose facts say `erased` can still hold an older
image, and a limitation says so. For a dump read from another flash address, call
`open_binary` with `format: "esp-flash-image"` and `flash_offset`; an
`esp-image` whose `flash_dump` is set names that offset in a next probe; a
relocated table can be named with `partition_table_offset`. No table found
means unknown, often flash encryption. Extraction writes `nvs_keys` as
ordinary data. See
[ESP flash images](https://github.com/morluto/rea/blob/main/docs/esp-flash-image.md).
@@ -0,0 +1,310 @@
# Offline format and metadata inspection
Read the section matching the supplied artifact; other sections are optional.
- [Go build metadata](#go-build-metadata)
- [Model files](#model-files)
- [IL2CPP metadata](#il2cpp-metadata)
- [Rust dependency metadata](#rust-dependency-metadata)
- [PE Authenticode certificate tables](#pe-authenticode-certificate-tables)
- [PE import and delay-load tables](#pe-import-and-delay-load-tables)
- [PE TLS callbacks and load configuration](#pe-tls-callbacks-and-load-configuration)
- [Binary hardening](#binary-hardening)
- [ELF note tables](#elf-note-tables)
- [Apple IPS crash reports](#apple-ips-crash-reports)
- [Linux kernel modules](#linux-kernel-modules)
- [CPython pyc files](#cpython-pyc-files)
- [Erlang BEAM files](#erlang-beam-files)
- [SQLite snapshots](#sqlite-snapshots)
- [Recorded Linux crashes](#recorded-linux-crashes)
- [Offline ELF layout](#offline-elf-layout)
- [Offline WASM](#offline-wasm)
- [Offline APK signatures](#offline-apk-signatures)
- [Offline PE resource inventory](#offline-pe-resource-inventory)
- [Offline PE and ELF export tables](#offline-pe-and-elf-export-tables)
## Go build metadata
Use `inspect_go_binary` for an explicit local ELF, PE or Mach-O (universal
files need `architecture`) when the
question concerns the embedded compiler version, module/dependency versions,
local replacements, build settings, or static Go string literals packed without
NUL terminators. Pass `include_string_literals: true`
(`--include-string-literals`, optional `max_string_literals` /
`--max-string-literals`) to recover bounded inferred word-aligned `(ptr, len)`
UTF-8 string slices from read-only data and page them through `go`
`string_literals` views. No `open_binary`, Go installation or
target execution is required. The CLI equivalent is `inspect-go-binary PATH`.
Preserve artifact SHA-256, original file ranges, framed module bytes and every
unparsed module-text line. A missing record is an unknown, not proof of a
non-Go binary; observed metadata does not authenticate its claims, and static
`(ptr, len)` string recovery excludes compiler-inlined stack/immediate strings
without proving runtime reachability. Function,
source and type recovery remain a separate native-analysis question.
## Model files
Use `inspect_model_artifact` (`inspect-model-artifact PATH`) for local GGUF v2/v3
and safetensors declarations without a model runtime. Header-only mode does not
read weights or establish full-file identity. `hash: "full"` explicitly streams
all bytes. Retained `model-artifact` views page `metadata`, `tensors` and derived
`types`, with exact batch filters and selected fields. Arrays retain typed counts
and eight-element previews; omitted values, unknown types and runtime behavior
stay explicit. See [model files](https://github.com/morluto/rea/blob/main/docs/model-artifacts.md).
## IL2CPP metadata
Use `inspect_il2cpp_metadata` for an explicit local Unity `global-metadata.dat`
(versions 24, 27, 29, 31 and 39) when the question concerns assembly images,
names, member owners, string literals, namespaces or index ranges. No `open_binary`, Unity installation or target
execution is required. The CLI equivalent is `inspect-il2cpp-metadata PATH`.
MCP defaults to counts with complete retained Evidence. Query `sections`,
`images`, `types`, `methods`, `fields`, `properties`, `events`, `parameters` or
`string_literals` through `inspect_analysis_view`; use exact names,
batch indices and selected fields. Row indices are scoped by artifact SHA-256
and collection; page offsets count filtered matches. Parameters support batch
`method_indices` to read one method's parameters directly. Historical reports
without member collections retain unknown counts rather than empty inventories. Preserve
artifact SHA-256 and index widths. Unrecognized magic may mean encryption and
is unsupported, not decrypted. Linking names to `GameAssembly` or
`UnityFramework` code addresses remains a separate native-analysis question.
See [IL2CPP metadata](https://github.com/morluto/rea/blob/main/docs/il2cpp-metadata.md).
## Rust dependency metadata
Use `inspect_rust_binary` for an explicit local ELF, PE, or Mach-O (universal
files need `architecture`) when
the question concerns the cargo-auditable crate list embedded in `.dep-v0`
(Mach-O: `__DATA,.dep-v0`). No `open_binary`, Rust installation, or target
execution is required. The CLI equivalent is `inspect-rust-binary PATH`.
Preserve artifact SHA-256, package order, dependency indices, and the section
file range. A missing section is null metadata, not proof the binary has no
dependencies. An omitted package kind stays null. A rustc commit hash is
inferred from a read-only `/rustc/<40-hex>/` path and does not by itself
identify the compiler. See [Rust binaries](https://github.com/morluto/rea/blob/main/docs/rust-binaries.md).
## PE Authenticode certificate tables
Use `inspect_pe_certificates` for an explicit local PE32/PE32+ image when the
question concerns the Authenticode certificate table, WIN_CERTIFICATE types, or
embedded PKCS#7 signer and X.509 identity fields. No `open_binary`, Windows SDK,
or target execution is required. The CLI equivalent is
`inspect-pe-certificates PATH`. Preserve artifact SHA-256, table file ranges,
payload hashes, original validity-time spellings, and
`subject_key_identifier_hex` when the signer uses that form. A missing
`IMAGE_DIRECTORY_ENTRY_SECURITY` directory is observed absence of that table,
not proof the image is unsigned in every catalog or overlay sense. Signatures,
chains, timestamps, and Authenticode digest coverage remain unobserved. See
[PE Authenticode certificate tables](https://github.com/morluto/rea/blob/main/docs/pe-certificates.md).
## PE import and delay-load tables
Use `inspect_pe_imports` for an explicit local PE32/PE32+ image when the
question concerns imported DLL and function names, ordinals, delay-load
descriptors, or the pefile import hash. No `open_binary`, Windows SDK, or
target execution is required. The CLI equivalent is `inspect-pe-imports PATH`.
Preserve artifact SHA-256, directory file ranges, original DLL spellings, and
ILT/IAT RVAs. A missing `IMAGE_DIRECTORY_ENTRY_IMPORT` directory is observed
absence of that table, not proof the image never imported APIs through another
table or overlay. Bound snapshots, loaded IAT contents, and export-forward
resolution remain unobserved. See
[PE import and delay-load tables](https://github.com/morluto/rea/blob/main/docs/pe-imports.md).
## PE TLS callbacks and load configuration
Use `inspect_pe_load_config` for an explicit local PE32/PE32+ image when the
question concerns code that runs before the entry point through TLS callbacks,
or the security cookie, SafeSEH, CFG, EH-continuation and XFG declarations in
the load configuration. No `open_binary`, Windows SDK, or target execution is
required. The CLI equivalent is `inspect-pe-load-config PATH`. Preserve artifact
SHA-256, directory and callback-array file ranges, and callback VAs as
hexadecimal strings. Fields outside the declared `Size` are `null`. Guard-flag
mitigations carry `evidence_kind: "inferred"`; enforcement remains unobserved.
See
[PE TLS callbacks and load configuration](https://github.com/morluto/rea/blob/main/docs/pe-load-config.md).
## Binary hardening
Use `inspect_binary_hardening` for an explicit local ELF, PE, or Mach-O file
when the question concerns PIE or ASLR, NX, RELRO, stack-protector or FORTIFY
signs, control-flow flags, or stripped state. No `open_binary`, pwntools, or
target execution is required. The CLI equivalent is
`inspect-binary-hardening PATH`. Universal Mach-O files return one image per
slice. A missing import is `unknown`, not proof the protection was omitted.
Authenticode and Mach-O code-signature rows report presence, not validity.
`hardened_runtime` stays unknown and names `inspect_signature`. See
[binary hardening](https://github.com/morluto/rea/blob/main/docs/binary-hardening.md).
## ELF note tables
Use `inspect_elf_notes` for an explicit local ELF32/ELF64 image when the
question concerns PT_NOTE/SHT_NOTE owners, GNU build-id, or GNU ABI-tag
fields. No `open_binary` or target execution is required. The CLI equivalent
is `inspect-elf-notes PATH`. Preserve artifact SHA-256, table file ranges,
descriptor hashes, and original owner spellings. A missing table is observed
absence of those declarations, not proof the image never carried notes in
another container. See
[ELF note tables](https://github.com/morluto/rea/blob/main/docs/elf-notes.md).
## Apple IPS crash reports
Use `inspect_apple_ips` for an explicit local Apple `.ips` file when the
question concerns crash metadata, exception and termination facts, threads,
usedImages, lossless registers, or documented crash classes. No `open_binary`,
Xcode, atos, or target execution is required. The CLI equivalent is
`inspect-apple-ips PATH`. Preserve artifact SHA-256, `bug_type` as a string,
uppercase image UUIDs, and integer literals that exceed IEEE-754 range. Only
`bug_type` `"309"` yields a crash body; other DiagnosticReports types return
no crash result. Opened Mach-O and dSYM UUID joins remain unobserved. See
[Apple IPS crash reports](https://github.com/morluto/rea/blob/main/docs/apple-ips.md).
## Linux kernel modules
Use `inspect_kernel_module` for an explicit local ELF32/ELF64 relocatable
object when the question concerns `.modinfo` license, vermagic, alias,
depends, or parm strings, the `__versions` symbol CRCs, or an appended module
signature trailer. No `open_binary`, Linux host, or `modinfo` program is
required. The CLI equivalent is `inspect-kernel-module PATH`. Preserve
artifact SHA-256, key order, duplicate keys, CRC hex, and section file ranges.
A missing section is observed absence when section names are known, not proof
the module never carried that metadata. `.gnu.linkonce.this_module` is reported
as present or absent; `struct module` stays unknown because its layout changes
with the kernel version. `__ksymtab` records are not decoded. The PKCS#7 range
and its SHA-256 are observed; certificate, signer, and digest coverage remain
unvalidated. An absent trailer is not proof the module is unsigned in another
container. gzip, xz, and zstd whole-file containers are unsupported and name
`external_decompression` as the next probe: use an external codec utility to
decompress into a separate file, then call `inspect_kernel_module` on that file. See
[Linux kernel modules](https://github.com/morluto/rea/blob/main/docs/kernel-modules.md).
## CPython pyc files
Use `inspect_python_pyc` for an explicit local CPython 3.7–3.13 `.pyc` when the
question concerns the header magic, PEP 552 source hash or timestamp, nested
code-object names, or bytecode identity. No `open_binary`, Python installation,
or target execution is required. The CLI equivalent is `inspect-python-pyc PATH`.
Preserve artifact SHA-256, the 16-byte header fields, nested code names, and
bytecode SHA-256. Bytecode is not decompiled. A decoded header does not prove
the matching `.py` is present. See [CPython pyc inspection](https://github.com/morluto/rea/blob/main/docs/python-pyc.md).
## Erlang BEAM files
Use `inspect_erlang_beam` for an explicit local `FOR1`/`BEAM` file when the
question concerns IFF chunks, atom names, exports, imports, lambdas, or the
Code header. No `open_binary`, Erlang installation, or bytecode execution is
required. The CLI equivalent is `inspect-erlang-beam PATH`. Preserve artifact
SHA-256, chunk file ranges, 1-based atom indices, and original atom spellings.
Missing optional tables stay null. For a few facts, use `detail: "summary"` with
a `beam` view selecting a collection, exact names/indices and fields; read later
pages from `parent_evidence_id` or the complete Evidence bundle. Same-name
exports retain every arity and original atom index. CLI summary delivery requires
`--evidence_output`. Null table counts remain distinct from decoded empty tables. See
[Erlang BEAM files](https://github.com/morluto/rea/blob/main/docs/erlang-beam.md).
## SQLite snapshots
Use `inspect_sqlite_database` with an explicit local database snapshot for its
schema and optional rows from one exact ordinary table. It requires Node.js
24.x >=24.15 or 26+ with the native `DatabaseSync.limits` API. It also consumes the
existing sibling `-wal`; retain both original digests when citing its result.
Default output is a compact inventory; a selected table expands only its own
schema and samples 20 rows. Select `columns` to avoid irrelevant or large cells,
and request `schema_scope: "database"` only for complete schema details.
Choose `row_limit` when a finite sample is useful and keep truncation and
unspecified row order explicit. Virtual tables, shadow tables and views are
schema facts, not row-read targets. REA inspects private copies and leaves
the source files untouched; obtain a coherent quiescent database/WAL snapshot
before inspection. See [SQLite snapshots](https://github.com/morluto/rea/blob/main/docs/guides/data/sqlite-databases.md).
## Recorded Linux crashes
Use `inspect_recorded_crash` with an explicit Linux x86-64 ELF core path to
inspect historical thread registers, signals, raw notes and original file
ranges. It is independent of the active disassembler target. Source-bound
register values and note bytes are observations; missing notes, signal-thread
association and current executable/library identity remain unknown. PIDs are
historical metadata and never authorize live attach or control.
The optional `include_debugger_context` facet adds mapping candidates through
caller-supplied GDB/pwndbg in an owned core-only session. Display names do not
establish current file identity; zero reported flags leave permissions unknown.
Requested unavailable context fails with setup guidance. See
[recorded crashes](https://github.com/morluto/rea/blob/main/docs/guides/native/recorded-crashes.md)
for exact upstream profiles, bounds and verification coverage.
## Offline ELF layout
Use `inspect_binary_layout` with an explicit local ELF path for file-backed
sections, symbols, relocations, linkage names, and static mitigation inferences
without opening a disassembler target. When that complete record is already
retained, call `inspect_analysis_view` for a summary, mitigations or linkage
facet, one section or symbol, or a stable page. Do not re-run the decoder to
read a single object.
## Offline WASM
Use `inspect_wasm_artifact` with caller-supplied WABT for selected local bytes.
For counts, validation and WAT identity, select `detail: "summary"` on the first
MCP call. The complete original Evidence stays retained; use
`normalized_result.parent_evidence_id` for exact exports or text pages. Omitted
or complete detail keeps the existing complete result.
For a completed result, use `inspect_analysis_view` with its reported retained
reference for a summary or `view: {kind: "wasm", facet: "exports", offset: 0, limit: 32}`.
Facets also include sections, imports, candidates, glue, WAT, headers, details
and tool profile. Text offsets count UTF-16 code units; follow `next_offset`.
The complete source remains retained after an oversized MCP reply. No target
execution or network acquisition is implied, and WAT is decoded representation.
For exact core-module import/export differences, use `compare_wasm_interfaces`
with two complete inspection Evidence records (or current-session retained
references) and explicit scope. Preserve unknown types/duplicate names; static
interface equality is not runtime compatibility. CLI accepts complete inline
Evidence in `rea compare-wasm-interfaces input.json --json`, or exact references
with `--evidence-bundle /absolute/path/evidence.json`. Use the complete parent
ID when the inspection returned a summary.
## Offline APK signatures
Use `inspect_apk_signature` for a selected local APK/ZIP/AAB. Default MCP summaries
retain complete Evidence. Select `view.collection: signers/digests/lineage`,
with scheme or ID batches, fields and paging; use `inspect_analysis_view` kind
`apk-signature` for retained reads after deletion. IDs retain artifact/scheme
scope and signer links; `fields: []` keeps identity links. Empty pages preserve
partial decoding reasons and unknown authenticity. CLI compact delivery needs
`--evidence_output` to preserve the complete parent.
It reads the APK
Signing Block (v2/v3/v3.1) and v1 `META-INF` signing without executing anything,
and reports each signer's certificate facts (for v1, the certificate the PKCS#7
block's SignerInfo names), the declared v3 rotation lineage,
the declared source-stamp certificate and, for SHA-256/SHA-512 digests, whether
the declared content digest matches the current bytes. That match is not a
signature verification: anyone who changes the bytes can recompute the digest.
`posture` summarises v1-only signing (which cannot tell a v1-only APK from one
whose v2/v3 block was removed), observed signer agreement across schemes,
whether the v1 signer equals the block-scheme signer (`false` is notable),
whether every differing signer appears in a declared (unverified) rotation
lineage, and the strongest scheme.
It never claims authenticity: no signature or lineage is cryptographically
verified.
## Offline PE resource inventory
Use `inspect_pe_resources` with an absolute `path`, or
`rea inspect-pe-resources ./selected.exe --json`, for portable PE32/PE32+
resource inspection without opening a native target. Preserve returned Evidence:
type/name/language IDs and UTF-16 names, original directory/data/payload ranges,
code pages, payload hashes and static icon-group references. Same-language icon
resolution is explicit; missing references and size mismatch remain visible.
Other payloads are opaque, and this operation does not decode images or simulate
Windows resource loading. Consult the budgets and supported tree profile in
[offline binary diagnostics](https://github.com/morluto/rea/blob/main/docs/guides/native/binary-diagnostics.md).
## Offline PE and ELF export tables
Use `inspect_native_exports` with an absolute `path`, or
`rea inspect-native-exports ./libexample.so --json`, to read a PE export
directory or ELF dynamic symbol table on any host without opening a native
target. Ordinal-only, forwarded and hidden-version exports stay distinct;
partial or unsupported coverage means the inventory is incomplete, not that
exports are absent. Use `inspect_macho` for Mach-O. See
[offline binary diagnostics](https://github.com/morluto/rea/blob/main/docs/guides/native/binary-diagnostics.md).
@@ -1,4 +1,21 @@
# Native, managed, and packaged artifacts
# Native and managed analysis
Read the section matching the supplied artifact; other sections are optional.
- [Other artifact guides](#other-artifact-guides)
- [Apple app resources](#apple-app-resources)
- [Desktop installation directories](#desktop-installation-directories)
- [Native targets](#native-targets)
- [Continue editable Ghidra analysis across processes](#continue-editable-ghidra-analysis-across-processes)
- [Reuse caller-selected Ghidra analysis](#reuse-caller-selected-ghidra-analysis)
- [Ghidra startup deadline recovery](#ghidra-startup-deadline-recovery)
- [Bridge request ceiling](#bridge-request-ceiling)
- [Selected native facts and call graphs](#selected-native-facts-and-call-graphs)
- [Static memory and original file bytes](#static-memory-and-original-file-bytes)
- [DOS MZ with Ghidra](#dos-mz-with-ghidra)
- [Managed PE/CLI](#managed-pecli)
- [DOS COM loader interpretation](#dos-com-loader-interpretation)
- [Function annotations in Ghidra](#function-annotations-in-ghidra)
For release comparisons on one MCP connection, retain `list_procedures` Evidence
from each target and reuse its exact IDs after target switches or `close_binary`.
@@ -62,276 +79,6 @@ runtime presence. For compact CLI output, use
`inspect-desktop-application PATH --detail summary --evidence_output BUNDLE`.
See [desktop installations](https://github.com/morluto/rea/blob/main/docs/desktop-applications.md).
## Go build metadata
Use `inspect_go_binary` for an explicit local ELF, PE or Mach-O (universal
files need `architecture`) when the
question concerns the embedded compiler version, module/dependency versions,
local replacements, build settings, or static Go string literals packed without
NUL terminators. Pass `include_string_literals: true`
(`--include-string-literals`, optional `max_string_literals` /
`--max-string-literals`) to recover bounded inferred word-aligned `(ptr, len)`
UTF-8 string slices from read-only data and page them through `go`
`string_literals` views. No `open_binary`, Go installation or
target execution is required. The CLI equivalent is `inspect-go-binary PATH`.
Preserve artifact SHA-256, original file ranges, framed module bytes and every
unparsed module-text line. A missing record is an unknown, not proof of a
non-Go binary; observed metadata does not authenticate its claims, and static
`(ptr, len)` string recovery excludes compiler-inlined stack/immediate strings
without proving runtime reachability. Function,
source and type recovery remain a separate native-analysis question.
## Model files
Use `inspect_model_artifact` (`inspect-model-artifact PATH`) for local GGUF v2/v3
and safetensors declarations without a model runtime. Header-only mode does not
read weights or establish full-file identity. `hash: "full"` explicitly streams
all bytes. Retained `model-artifact` views page `metadata`, `tensors` and derived
`types`, with exact batch filters and selected fields. Arrays retain typed counts
and eight-element previews; omitted values, unknown types and runtime behavior
stay explicit. See [model files](https://github.com/morluto/rea/blob/main/docs/model-artifacts.md).
## IL2CPP metadata
Use `inspect_il2cpp_metadata` for an explicit local Unity `global-metadata.dat`
(versions 24, 27, 29, 31 and 39) when the question concerns assembly images,
names, member owners, string literals, namespaces or index ranges. No `open_binary`, Unity installation or target
execution is required. The CLI equivalent is `inspect-il2cpp-metadata PATH`.
MCP defaults to counts with complete retained Evidence. Query `sections`,
`images`, `types`, `methods`, `fields`, `properties`, `events`, `parameters` or
`string_literals` through `inspect_analysis_view`; use exact names,
batch indices and selected fields. Row indices are scoped by artifact SHA-256
and collection; page offsets count filtered matches. Parameters support batch
`method_indices` to read one method's parameters directly. Historical reports
without member collections retain unknown counts rather than empty inventories. Preserve
artifact SHA-256 and index widths. Unrecognized magic may mean encryption and
is unsupported, not decrypted. Linking names to `GameAssembly` or
`UnityFramework` code addresses remains a separate native-analysis question.
See [IL2CPP metadata](https://github.com/morluto/rea/blob/main/docs/il2cpp-metadata.md).
## Rust dependency metadata
Use `inspect_rust_binary` for an explicit local ELF, PE, or Mach-O (universal
files need `architecture`) when
the question concerns the cargo-auditable crate list embedded in `.dep-v0`
(Mach-O: `__DATA,.dep-v0`). No `open_binary`, Rust installation, or target
execution is required. The CLI equivalent is `inspect-rust-binary PATH`.
Preserve artifact SHA-256, package order, dependency indices, and the section
file range. A missing section is null metadata, not proof the binary has no
dependencies. An omitted package kind stays null. A rustc commit hash is
inferred from a read-only `/rustc/<40-hex>/` path and does not by itself
identify the compiler. See [Rust binaries](https://github.com/morluto/rea/blob/main/docs/rust-binaries.md).
## PE Authenticode certificate tables
Use `inspect_pe_certificates` for an explicit local PE32/PE32+ image when the
question concerns the Authenticode certificate table, WIN_CERTIFICATE types, or
embedded PKCS#7 signer and X.509 identity fields. No `open_binary`, Windows SDK,
or target execution is required. The CLI equivalent is
`inspect-pe-certificates PATH`. Preserve artifact SHA-256, table file ranges,
payload hashes, original validity-time spellings, and
`subject_key_identifier_hex` when the signer uses that form. A missing
`IMAGE_DIRECTORY_ENTRY_SECURITY` directory is observed absence of that table,
not proof the image is unsigned in every catalog or overlay sense. Signatures,
chains, timestamps, and Authenticode digest coverage remain unobserved. See
[PE Authenticode certificate tables](https://github.com/morluto/rea/blob/main/docs/pe-certificates.md).
## PE import and delay-load tables
Use `inspect_pe_imports` for an explicit local PE32/PE32+ image when the
question concerns imported DLL and function names, ordinals, delay-load
descriptors, or the pefile import hash. No `open_binary`, Windows SDK, or
target execution is required. The CLI equivalent is `inspect-pe-imports PATH`.
Preserve artifact SHA-256, directory file ranges, original DLL spellings, and
ILT/IAT RVAs. A missing `IMAGE_DIRECTORY_ENTRY_IMPORT` directory is observed
absence of that table, not proof the image never imported APIs through another
table or overlay. Bound snapshots, loaded IAT contents, and export-forward
resolution remain unobserved. See
[PE import and delay-load tables](https://github.com/morluto/rea/blob/main/docs/pe-imports.md).
## Windows Shell Link shortcuts
Use `inspect_windows_shortcut` for an explicit local `.lnk` file when the
question concerns the recorded target path, arguments, working directory, icon
location, volume serial, network share names, ExtraData blocks, or Tracker
droid identifiers. No `open_binary`, Windows SDK, COM, or target execution is
required. The CLI equivalent is `inspect-windows-shortcut PATH`. Preserve
artifact SHA-256, FILETIME raw decimals, IDList type bytes, and ExtraData
signatures. Unknown IDList items and ExtraData signatures remain raw with
SHA-256; they are not guessed. Version-1 UUID MAC and timestamp values are
labelled derived. A missing optional section is observed absence of that
structure. See
[Windows Shell Link shortcuts](https://github.com/morluto/rea/blob/main/docs/guides/native/windows-shortcut.md).
## PE TLS callbacks and load configuration
Use `inspect_pe_load_config` for an explicit local PE32/PE32+ image when the
question concerns code that runs before the entry point through TLS callbacks,
or the security cookie, SafeSEH, CFG, EH-continuation and XFG declarations in
the load configuration. No `open_binary`, Windows SDK, or target execution is
required. The CLI equivalent is `inspect-pe-load-config PATH`. Preserve artifact
SHA-256, directory and callback-array file ranges, and callback VAs as
hexadecimal strings. Fields outside the declared `Size` are `null`. Guard-flag
mitigations carry `evidence_kind: "inferred"`; enforcement remains unobserved.
See
[PE TLS callbacks and load configuration](https://github.com/morluto/rea/blob/main/docs/pe-load-config.md).
## Binary hardening
Use `inspect_binary_hardening` for an explicit local ELF, PE, or Mach-O file
when the question concerns PIE or ASLR, NX, RELRO, stack-protector or FORTIFY
signs, control-flow flags, or stripped state. No `open_binary`, pwntools, or
target execution is required. The CLI equivalent is
`inspect-binary-hardening PATH`. Universal Mach-O files return one image per
slice. A missing import is `unknown`, not proof the protection was omitted.
Authenticode and Mach-O code-signature rows report presence, not validity.
`hardened_runtime` stays unknown and names `inspect_signature`. See
[binary hardening](https://github.com/morluto/rea/blob/main/docs/binary-hardening.md).
## ELF note tables
Use `inspect_elf_notes` for an explicit local ELF32/ELF64 image when the
question concerns PT_NOTE/SHT_NOTE owners, GNU build-id, or GNU ABI-tag
fields. No `open_binary` or target execution is required. The CLI equivalent
is `inspect-elf-notes PATH`. Preserve artifact SHA-256, table file ranges,
descriptor hashes, and original owner spellings. A missing table is observed
absence of those declarations, not proof the image never carried notes in
another container. See
[ELF note tables](https://github.com/morluto/rea/blob/main/docs/elf-notes.md).
## Apple IPS crash reports
Use `inspect_apple_ips` for an explicit local Apple `.ips` file when the
question concerns crash metadata, exception and termination facts, threads,
usedImages, lossless registers, or documented crash classes. No `open_binary`,
Xcode, atos, or target execution is required. The CLI equivalent is
`inspect-apple-ips PATH`. Preserve artifact SHA-256, `bug_type` as a string,
uppercase image UUIDs, and integer literals that exceed IEEE-754 range. Only
`bug_type` `"309"` yields a crash body; other DiagnosticReports types return
no crash result. Opened Mach-O and dSYM UUID joins remain unobserved. See
[Apple IPS crash reports](https://github.com/morluto/rea/blob/main/docs/apple-ips.md).
## Linux kernel modules
Use `inspect_kernel_module` for an explicit local ELF32/ELF64 relocatable
object when the question concerns `.modinfo` license, vermagic, alias,
depends, or parm strings, the `__versions` symbol CRCs, or an appended module
signature trailer. No `open_binary`, Linux host, or `modinfo` program is
required. The CLI equivalent is `inspect-kernel-module PATH`. Preserve
artifact SHA-256, key order, duplicate keys, CRC hex, and section file ranges.
A missing section is observed absence when section names are known, not proof
the module never carried that metadata. `.gnu.linkonce.this_module` is reported
as present or absent; `struct module` stays unknown because its layout changes
with the kernel version. `__ksymtab` records are not decoded. The PKCS#7 range
and its SHA-256 are observed; certificate, signer, and digest coverage remain
unvalidated. An absent trailer is not proof the module is unsigned in another
container. gzip, xz, and zstd whole-file containers are unsupported and name
`external_decompression` as the next probe: use an external codec utility to
decompress into a separate file, then call `inspect_kernel_module` on that file. See
[Linux kernel modules](https://github.com/morluto/rea/blob/main/docs/kernel-modules.md).
## CPython pyc files
Use `inspect_python_pyc` for an explicit local CPython 3.7–3.13 `.pyc` when the
question concerns the header magic, PEP 552 source hash or timestamp, nested
code-object names, or bytecode identity. No `open_binary`, Python installation,
or target execution is required. The CLI equivalent is `inspect-python-pyc PATH`.
Preserve artifact SHA-256, the 16-byte header fields, nested code names, and
bytecode SHA-256. Bytecode is not decompiled. A decoded header does not prove
the matching `.py` is present. See [CPython pyc inspection](https://github.com/morluto/rea/blob/main/docs/python-pyc.md).
## Erlang BEAM files
Use `inspect_erlang_beam` for an explicit local `FOR1`/`BEAM` file when the
question concerns IFF chunks, atom names, exports, imports, lambdas, or the
Code header. No `open_binary`, Erlang installation, or bytecode execution is
required. The CLI equivalent is `inspect-erlang-beam PATH`. Preserve artifact
SHA-256, chunk file ranges, 1-based atom indices, and original atom spellings.
Missing optional tables stay null. For a few facts, use `detail: "summary"` with
a `beam` view selecting a collection, exact names/indices and fields; read later
pages from `parent_evidence_id` or the complete Evidence bundle. Same-name
exports retain every arity and original atom index. CLI summary delivery requires
`--evidence_output`. Null table counts remain distinct from decoded empty tables. See
[Erlang BEAM files](https://github.com/morluto/rea/blob/main/docs/erlang-beam.md).
## SQLite snapshots
Use `inspect_sqlite_database` with an explicit local database snapshot for its
schema and optional rows from one exact ordinary table. It requires Node.js
24.x >=24.15 or 26+ with the native `DatabaseSync.limits` API. It also consumes the
existing sibling `-wal`; retain both original digests when citing its result.
Default output is a compact inventory; a selected table expands only its own
schema and samples 20 rows. Select `columns` to avoid irrelevant or large cells,
and request `schema_scope: "database"` only for complete schema details.
Choose `row_limit` when a finite sample is useful and keep truncation and
unspecified row order explicit. Virtual tables, shadow tables and views are
schema facts, not row-read targets. REA inspects private copies and leaves
the source files untouched; obtain a coherent quiescent database/WAL snapshot
before inspection. See [SQLite snapshots](https://github.com/morluto/rea/blob/main/docs/guides/data/sqlite-databases.md).
## Recorded Linux crashes
Use `inspect_recorded_crash` with an explicit Linux x86-64 ELF core path to
inspect historical thread registers, signals, raw notes and original file
ranges. It is independent of the active disassembler target. Source-bound
register values and note bytes are observations; missing notes, signal-thread
association and current executable/library identity remain unknown. PIDs are
historical metadata and never authorize live attach or control.
The optional `include_debugger_context` facet adds mapping candidates through
caller-supplied GDB/pwndbg in an owned core-only session. Display names do not
establish current file identity; zero reported flags leave permissions unknown.
Requested unavailable context fails with setup guidance. See
[recorded crashes](https://github.com/morluto/rea/blob/main/docs/guides/native/recorded-crashes.md)
for exact upstream profiles, bounds and verification coverage.
## Offline ELF layout
Use `inspect_binary_layout` with an explicit local ELF path for file-backed
sections, symbols, relocations, linkage names, and static mitigation inferences
without opening a disassembler target. When that complete record is already
retained, call `inspect_analysis_view` for a summary, mitigations or linkage
facet, one section or symbol, or a stable page. Do not re-run the decoder to
read a single object.
## Offline WASM
Use `inspect_wasm_artifact` with caller-supplied WABT for selected local bytes.
For counts, validation and WAT identity, select `detail: "summary"` on the first
MCP call. The complete original Evidence stays retained; use
`normalized_result.parent_evidence_id` for exact exports or text pages. Omitted
or complete detail keeps the existing complete result.
For a completed result, use `inspect_analysis_view` with its reported retained
reference for a summary or `view: {kind: "wasm", facet: "exports", offset: 0, limit: 32}`.
Facets also include sections, imports, candidates, glue, WAT, headers, details
and tool profile. Text offsets count UTF-16 code units; follow `next_offset`.
The complete source remains retained after an oversized MCP reply. No target
execution or network acquisition is implied, and WAT is decoded representation.
## Offline APK signatures
Use `inspect_apk_signature` for a selected local APK/ZIP/AAB. Default MCP summaries
retain complete Evidence. Select `view.collection: signers/digests/lineage`,
with scheme or ID batches, fields and paging; use `inspect_analysis_view` kind
`apk-signature` for retained reads after deletion. IDs retain artifact/scheme
scope and signer links; `fields: []` keeps identity links. Empty pages preserve
partial decoding reasons and unknown authenticity. CLI compact delivery needs
`--evidence_output` to preserve the complete parent.
It reads the APK
Signing Block (v2/v3/v3.1) and v1 `META-INF` signing without executing anything,
and reports each signer's certificate facts (for v1, the certificate the PKCS#7
block's SignerInfo names), the declared v3 rotation lineage,
the declared source-stamp certificate and, for SHA-256/SHA-512 digests, whether
the declared content digest matches the current bytes. That match is not a
signature verification: anyone who changes the bytes can recompute the digest.
`posture` summarises v1-only signing (which cannot tell a v1-only APK from one
whose v2/v3 block was removed), observed signer agreement across schemes,
whether the v1 signer equals the block-scheme signer (`false` is notable),
whether every differing signer appears in a declared (unverified) rotation
lineage, and the strongest scheme.
It never claims authenticity: no signature or lineage is cryptographically
verified.
## Native targets
After `open_binary`, use focused search, procedure, or function tools directly.
@@ -627,81 +374,17 @@ and distinguish derived metadata from Ghidra database observations. Recovered
types and native pseudocode are not original C# source. Windows Ghidra P0 has
no database mutation authority.
## Packages and extraction
For headerless firmware and address-bearing containers, read
[raw images](firmware-and-raw-images.md#headerless-raw-images-and-firmware-containers).
Use `inspect_artifact` for application bundles, archives, ZIP/APK/IPA/MSIX/AppX,
ASAR, or DMG inputs when the artifact graph and findings help answer the
question. It returns the complete artifact graph inline. Cite graph manifest
IDs when using them.
For an IPA or a macOS `.app`, ZIP, or DMG, pass the inventory Evidence to
`project_apple_application_graph`. It reports application roots and nested
bundles by path convention: app extensions, XPC services, frameworks, login
items, system and driver extensions, and plug-ins. It also lists privileged
helpers, launchd plists, symlinks, and each bundle's `info_plist_path` and
executable candidates. Roles are path conventions, not parsed plists; read the
listed plists with `inspect_plist`.
After `open_binary` on a `.app` or Mach-O, use `trace_dylib_resolution` to see
which file each `@rpath`, `@loader_path` and `@executable_path` load reaches for
every executable in the bundle. It also lists missing or weak loads and earlier
`@rpath` candidates that are absent. Narrow large bundles with `roots` or
`architecture`. System paths stay undetermined because the dyld shared cache
provides them.
Use `extract_artifact` when materialized files from a supported format are needed.
It extracts the active target and accepts optional `integrity_policy`; see the [MCP contracts](https://github.com/morluto/rea/blob/main/docs/reference/mcp-contracts.md).
It materializes all regular files into a fresh temporary directory chosen by
REA. Symlinks and encrypted entries are inventory facts, not extractable files.
The result reports the materialized directory; callers do not choose the
destination. No separate permission grant is needed.
For classic iNES cartridges, `open_binary` recognizes the byte signature without
an executable architecture. `inspect_artifact` retains header facts and each
PRG/CHR file unit's offset, length and SHA-256; its nested inventory's
`ines.reconstruction` verifies the actual ordered region bytes against the
whole-file digest. Extraction includes the original header, optional trainer
and uninterpreted trailing bytes. These file units are not mapper/CPU bank
maps. NES 2.0, UNIF, dirty legacy headers and truncated payloads are explicitly
refused. No emulator or 6502 analysis is performed. See the
[iNES guide](https://github.com/morluto/rea/blob/main/docs/ines-containers.md).
Native DMG traversal automatically uses a read-only mount on supported macOS
hosts, with an owned temporary mount directory and cleanup. It needs no approval
flag. Unsupported hosts or mount failures remain explicit limitations; do not
claim child inventory when only root identity is available. DMG extraction is
unavailable through `extract_artifact`. To access DMG files on macOS, create a
read-only mount with your own tools, select files from that mount for analysis,
and detach the mount you created when finished. REA's inspection mount is
temporary, not a retained export of the image's contents.
## DOS COM loader interpretation
For headerless DOS COM, explicitly open with `format: "dos-com"` (CLI
`--target-format dos-com`). Inspect the load image before trusting function
analysis: the entry is `0x10100`, file offset zero, with imposed real-mode segment
context. PSP/stack/device state remains unmodeled. See [DOS guide](https://github.com/morluto/rea/blob/main/docs/providers/ghidra-dos.md).
Use `inspect_byte_entropy` to triage high- and low-entropy ranges before extraction.
For other headerless images (embedded firmware without ELF/Mach-O/PE/MZ
headers), open with `format: "raw-binary"`, `architecture` (`x86`, `x86_64`,
`arm`, `arm64`, `6502`, or `m68k`), and `entry_point` (`0x` hex). File offset 0 is mapped at
that address. Hopper and IDA refuse this interpretation; Ghidra uses
`BinaryLoader` with a generic language: big-endian for `m68k`, little-endian
otherwise. Big-endian ARM, Thumb-only, and Cortex-M variants are unsupported.
`6502` selects NMOS 6502; the entry and complete file mapping must fit within
`0x0000..0xffff`. 65C02, 65816, cartridge containers, bank switching, and mapper
semantics are unsupported. `m68k` requires an even entry and decodes the 68040
superset of the 68000 family; Amiga/Atari containers and vector-table entry
discovery are unsupported. Windows Ghidra P0 admits raw images only for `6502`. See [raw binary guide](https://github.com/morluto/rea/blob/main/docs/providers/raw-binary.md).
Intel HEX, S-record, UF2 and DfuSe files are containers, not raw images:
`inspect_artifact` lists address regions (UF2 per familyID, DfuSe per element
with its alternate setting), and `extract_artifact` writes each region. Open an
extracted region as `raw-binary` with its start address as `entry_point`. UF2
numbering gaps leave coverage unknown; a DfuSe suffix CRC mismatch follows
`integrity_policy`. See [UF2 and DfuSe](https://github.com/morluto/rea/blob/main/docs/uf2-dfuse.md).
### Function annotations in Ghidra
## Function annotations in Ghidra
Fresh-import sessions admit annotations; existing-project reuse is read-only.
Use `annotate_native_function` for one function name and/or entry comments, with at least one explicit change. Review its annotation readback and refreshed dossier inline. Changes are atomic; empty comments clear them, omitted fields preserve them. Later MCP calls observe edits. Normal close saves them when owned Program checkpoint mode is enabled; ephemeral mode discards them. CLI `annotate-native-function` follows the same setting when closing after its updated result. Select the [checkpoint workflow](#continue-editable-ghidra-analysis-across-processes) before edits that must survive a later process. Original executable bytes are unchanged; edits invalidate immutable snapshots. Windows P0 does not admit database mutations. For naming only, `set_address_name` / `set_addresses_names` also work with Ghidra: they name function entries or data addresses (primary labels) and return compact per-address success booleans instead of a dossier. Ghidra rejects batch addresses that request the same name or a name another selected address holds; `raw_result.failures` gives each failure's reason.
@@ -721,139 +404,33 @@ architecture, provider-owned mapping and selected-image commitments. Equal
numeric addresses alone cannot establish correspondence; inspect retained
source Evidence and coverage when the match remains ambiguous.
## Offline PE resource inventory
## Other artifact guides
Use `inspect_pe_resources` with an absolute `path`, or
`rea inspect-pe-resources ./selected.exe --json`, for portable PE32/PE32+
resource inspection without opening a native target. Preserve returned Evidence:
type/name/language IDs and UTF-16 names, original directory/data/payload ranges,
code pages, payload hashes and static icon-group references. Same-language icon
resolution is explicit; missing references and size mismatch remain visible.
Other payloads are opaque, and this operation does not decode images or simulate
Windows resource loading. Consult the budgets and supported tree profile in
[offline binary diagnostics](https://github.com/morluto/rea/blob/main/docs/guides/native/binary-diagnostics.md).
Read the owning page for offline formats, containers or firmware. These explicit
anchors preserve older links without creating duplicate searchable sections.
## Offline PE and ELF export tables
Use `inspect_native_exports` with an absolute `path`, or
`rea inspect-native-exports ./libexample.so --json`, to read a PE export
directory or ELF dynamic symbol table on any host without opening a native
target. Ordinal-only, forwarded and hidden-version exports stay distinct;
partial or unsupported coverage means the inventory is incomplete, not that
exports are absent. Use `inspect_macho` for Mach-O. See
[offline binary diagnostics](https://github.com/morluto/rea/blob/main/docs/guides/native/binary-diagnostics.md).
For exact core-module import/export differences, use `compare_wasm_interfaces`
with two complete inspection Evidence records (or current-session retained
references) and explicit scope. Preserve unknown types/duplicate names; static
interface equality is not runtime compatibility. CLI accepts complete inline
Evidence in `rea compare-wasm-interfaces input.json --json`, or exact references
with `--evidence-bundle /absolute/path/evidence.json`. Use the complete parent
ID when the inspection returned a summary.
## Godot PCK
For a standalone PCK, `open_binary` then `inspect_artifact` with
`detail: "summary"` retains the complete static inventory. Use the returned
`parent_evidence_id` with `inspect_analysis_view` inventory occurrences filtered
by exact `logical_paths` or `occurrence_ids`, and join `artifact_id` to nodes
filtered by `artifact_ids`. Select only needed fields; identity links remain.
Prefer `inspect-artifact --detail summary --evidence_output PATH` for CLI tasks;
the same CLI discovers embedded packs via their EOF trailer without opening a
native analysis provider. Normal native opening remains unchanged.
Distinguish declared MD5 from observed SHA-256 and graph/page completeness from
`source_coverage`. Encrypted directories and sparse bundles have unknown entry
coverage; encrypted members have unknown content identity. An empty filtered
page cannot prove absence in unknown coverage. Retained views are historical,
not fresh filesystem checks. No decryption, Godot resource decoding, GDScript
decompilation or target execution is provided. See
[Godot PCK](https://github.com/morluto/rea/blob/main/docs/godot-pck.md).
UnityFS AssetBundle containers (`.bundle`, `.ab`, YooAsset packages and other
byte-identified files) list `CAB-*` SerializedFiles and `.resS` members with
header and node facts; none/LZ4/LZ4HC member blocks stream on demand. Bundles
declaring asset-bundle encryption keep header facts with unknown directory
coverage; LZMA member bytes retain named occurrences with unavailable hashes.
Virtual member offsets are not physical carrier locations. Format 6–8 readers
cache one decoded block and support retained filtered/paged views. No
decryption or SerializedFile object decoding is provided. See
[UnityFS](https://github.com/morluto/rea/blob/main/docs/unityfs.md).
WeChat mini-program packages (`.wxapkg` files with `0xBE`/`0xED` marks) list
members with declared names and absolute carrier offsets; member bytes are
stored verbatim. `V1MMWX` PC-client packages report `encrypted` directory
status with unknown coverage and are refused on extraction; no decryption is
attempted. See
[wxapkg](https://github.com/morluto/rea/blob/main/docs/wxapkg.md).
U-Boot legacy uImages use the same inspection/extraction workflows. Prefer a
retained summary and selected inventory views for header facts or component
IDs. Header addresses/CPU/OS are declarations; payloads remain as stored.
Multi-file components retain their aligned physical ranges; the full payload
is also materialized. Unknown name text retains its original 32 bytes. See
[U-Boot uImage](https://github.com/morluto/rea/blob/main/docs/uimage.md).
## ESP images
For an ESP32-family app or bootloader image, `open_binary` then
`inspect_artifact` with `detail: "summary"`. `root_format_metadata` holds the
observed `header` and `descriptor` (its `kind` is the app or bootloader role)
and the derived `chip`, `checksum`, `appended_sha256` and
`undescribed_trailing` ranges. Segment rows (`segment-NN.bin`) carry
`load_addr`, `classification` (`mapped`, `loaded`, `not-loaded` or `unknown`,
per ESP-IDF `should_map`/`should_load`; RAM ranges are not checked) and
`source_location`; select them with `inspect_analysis_view` and
`logical_paths`. Signature blocks are `unverified`; `public_key_sha256`
matches eFuse key digests.
Unknown `source_coverage` means truncation or undescribed trailing bytes, such
as the flash segments of a `--ram-only-header` image; do not infer absence
from it. Erased flash is described. Integrity mismatches are facts, not
refusals. ESP8266 images are refused, and flash-encrypted images are not
recognized. Segments are not disassembled, and RISC-V or Xtensa segments
cannot be opened as raw-binary. See
[ESP images](https://github.com/morluto/rea/blob/main/docs/esp-image.md).
## ESP flash images
A whole-flash dump opens as `esp-flash-image`. `root_format_metadata` holds
`partition_table` (its `method`: `default`, `caller` or a derived
`scan`), `bootloader` and `apps` ESP image facts, the `otadata` records,
`partitions_outside_dump` and `boot_selection`. Every table has a
`boot_selection`: its `basis` names the bootloader's branch (`no-otadata`
without otadata, `none` when the bootloader rejects the table, `unknown`
when a record is missing), and it lists its `assumptions`;
`running_partition` is always `unknown`. Each image names its entry
`path`. With `detail: "summary"` the root kind is `esp-flash-image-summary`
and each image is a compact `summary` (chip, entry point, segment count,
classified load ranges, signature block count, descriptor identity,
integrity); complete detail keeps every segment. Partition rows
(`partitions/<label>.bin`) carry the declared `type`, `subtype`, names,
`flash_address`, `size` and flags; `encrypted` is a storage flag, not
proof of ciphertext. `written_offset`/`written_end` bound the bytes that
are not erased: an app slot whose facts say `erased` can still hold an older
image, and a limitation says so. For a dump read from another flash address, call
`open_binary` with `format: "esp-flash-image"` and `flash_offset`; an
`esp-image` whose `flash_dump` is set names that offset in a next probe; a
relocated table can be named with `partition_table_offset`. No table found
means unknown, often flash encryption. Extraction writes `nvs_keys` as
ordinary data. See
[ESP flash images](https://github.com/morluto/rea/blob/main/docs/esp-flash-image.md).
## Bun standalone executables
A `bun build --compile` executable stores its module graph in Mach-O
`__BUN,__bun`, a PE or ELF `.bun` section, or an appended trailer.
`open_binary` then `inspect_artifact` reports root format `bun-standalone` with
the carrier, structural layout, module count, entry point, raw flags and
compile exec-argv; each module keeps its `/$bunfs/...` source name, loader,
encoding, sizes and SHA-256, with sourcemap, bytecode and module-info regions as
separate children. Use summary root metadata `entry_point_logical_path` as the
exact inventory `logical_paths` selector, then read the selected content node by
its artifact ID; the reader owns virtual-name normalization and indexed fallback. `extract-artifact` writes stored contents through the safe
output tree; route extracted JavaScript to `analyze_javascript_application`.
Layout labels describe record shapes, not a detected compiler version; unknown
or ambiguous layouts are unsupported. Bytecode is retained, not decoded, and the
executable is never run. See
[Bun standalone executables](https://github.com/morluto/rea/blob/main/docs/bun-standalone.md).
- <a id="go-build-metadata"></a>[Go build metadata](format-inspectors.md#go-build-metadata)
- <a id="model-files"></a>[Model files](format-inspectors.md#model-files)
- <a id="il2cpp-metadata"></a>[IL2CPP metadata](format-inspectors.md#il2cpp-metadata)
- <a id="rust-dependency-metadata"></a>[Rust dependency metadata](format-inspectors.md#rust-dependency-metadata)
- <a id="pe-authenticode-certificate-tables"></a>[PE Authenticode certificate tables](format-inspectors.md#pe-authenticode-certificate-tables)
- <a id="pe-import-and-delay-load-tables"></a>[PE import and delay-load tables](format-inspectors.md#pe-import-and-delay-load-tables)
- <a id="pe-tls-callbacks-and-load-configuration"></a>[PE TLS callbacks and load configuration](format-inspectors.md#pe-tls-callbacks-and-load-configuration)
- <a id="binary-hardening"></a>[Binary hardening](format-inspectors.md#binary-hardening)
- <a id="elf-note-tables"></a>[ELF note tables](format-inspectors.md#elf-note-tables)
- <a id="apple-ips-crash-reports"></a>[Apple IPS crash reports](format-inspectors.md#apple-ips-crash-reports)
- <a id="linux-kernel-modules"></a>[Linux kernel modules](format-inspectors.md#linux-kernel-modules)
- <a id="cpython-pyc-files"></a>[CPython pyc files](format-inspectors.md#cpython-pyc-files)
- <a id="erlang-beam-files"></a>[Erlang BEAM files](format-inspectors.md#erlang-beam-files)
- <a id="sqlite-snapshots"></a>[SQLite snapshots](format-inspectors.md#sqlite-snapshots)
- <a id="recorded-linux-crashes"></a>[Recorded Linux crashes](format-inspectors.md#recorded-linux-crashes)
- <a id="offline-elf-layout"></a>[Offline ELF layout](format-inspectors.md#offline-elf-layout)
- <a id="offline-wasm"></a>[Offline WASM](format-inspectors.md#offline-wasm)
- <a id="offline-apk-signatures"></a>[Offline APK signatures](format-inspectors.md#offline-apk-signatures)
- <a id="packages-and-extraction"></a>[Packages and extraction](packages-and-archives.md#packages-and-extraction)
- <a id="offline-pe-resource-inventory"></a>[Offline PE resource inventory](format-inspectors.md#offline-pe-resource-inventory)
- <a id="offline-pe-and-elf-export-tables"></a>[Offline PE and ELF export tables](format-inspectors.md#offline-pe-and-elf-export-tables)
- <a id="godot-pck"></a>[Godot PCK](packages-and-archives.md#godot-pck)
- <a id="esp-images"></a>[ESP images](firmware-and-raw-images.md#esp-images)
- <a id="esp-flash-images"></a>[ESP flash images](firmware-and-raw-images.md#esp-flash-images)
- <a id="bun-standalone-executables"></a>[Bun standalone executables](packages-and-archives.md#bun-standalone-executables)
@@ -0,0 +1,117 @@
# Packages and archives
Read the section matching the supplied artifact; other sections are optional.
- [Packages and extraction](#packages-and-extraction)
- [Godot PCK](#godot-pck)
- [Bun standalone executables](#bun-standalone-executables)
## Packages and extraction
Use `inspect_artifact` for application bundles, archives, ZIP/APK/IPA/MSIX/AppX,
ASAR, or DMG inputs when the artifact graph and findings help answer the
question. It returns the complete artifact graph inline. Cite graph manifest
IDs when using them.
For an IPA or a macOS `.app`, ZIP, or DMG, pass the inventory Evidence to
`project_apple_application_graph`. It reports application roots and nested
bundles by path convention: app extensions, XPC services, frameworks, login
items, system and driver extensions, and plug-ins. It also lists privileged
helpers, launchd plists, symlinks, and each bundle's `info_plist_path` and
executable candidates. Roles are path conventions, not parsed plists; read the
listed plists with `inspect_plist`.
After `open_binary` on a `.app` or Mach-O, use `trace_dylib_resolution` to see
which file each `@rpath`, `@loader_path` and `@executable_path` load reaches for
every executable in the bundle. It also lists missing or weak loads and earlier
`@rpath` candidates that are absent. Narrow large bundles with `roots` or
`architecture`. System paths stay undetermined because the dyld shared cache
provides them.
Use `extract_artifact` when materialized files from a supported format are needed.
It extracts the active target and accepts optional `integrity_policy`; see the [MCP contracts](https://github.com/morluto/rea/blob/main/docs/reference/mcp-contracts.md).
It materializes all regular files into a fresh temporary directory chosen by
REA. Symlinks and encrypted entries are inventory facts, not extractable files.
The result reports the materialized directory; callers do not choose the
destination. No separate permission grant is needed.
For classic iNES cartridges, `open_binary` recognizes the byte signature without
an executable architecture. `inspect_artifact` retains header facts and each
PRG/CHR file unit's offset, length and SHA-256; its nested inventory's
`ines.reconstruction` verifies the actual ordered region bytes against the
whole-file digest. Extraction includes the original header, optional trainer
and uninterpreted trailing bytes. These file units are not mapper/CPU bank
maps. NES 2.0, UNIF, dirty legacy headers and truncated payloads are explicitly
refused. No emulator or 6502 analysis is performed. See the
[iNES guide](https://github.com/morluto/rea/blob/main/docs/ines-containers.md).
Native DMG traversal automatically uses a read-only mount on supported macOS
hosts, with an owned temporary mount directory and cleanup. It needs no approval
flag. Unsupported hosts or mount failures remain explicit limitations; do not
claim child inventory when only root identity is available. DMG extraction is
unavailable through `extract_artifact`. To access DMG files on macOS, create a
read-only mount with your own tools, select files from that mount for analysis,
and detach the mount you created when finished. REA's inspection mount is
temporary, not a retained export of the image's contents.
Use `inspect_byte_entropy` to triage high- and low-entropy ranges before extraction.
## Godot PCK
For a standalone PCK, `open_binary` then `inspect_artifact` with
`detail: "summary"` retains the complete static inventory. Use the returned
`parent_evidence_id` with `inspect_analysis_view` inventory occurrences filtered
by exact `logical_paths` or `occurrence_ids`, and join `artifact_id` to nodes
filtered by `artifact_ids`. Select only needed fields; identity links remain.
Prefer `inspect-artifact --detail summary --evidence_output PATH` for CLI tasks;
the same CLI discovers embedded packs via their EOF trailer without opening a
native analysis provider. Normal native opening remains unchanged.
Distinguish declared MD5 from observed SHA-256 and graph/page completeness from
`source_coverage`. Encrypted directories and sparse bundles have unknown entry
coverage; encrypted members have unknown content identity. An empty filtered
page cannot prove absence in unknown coverage. Retained views are historical,
not fresh filesystem checks. No decryption, Godot resource decoding, GDScript
decompilation or target execution is provided. See
[Godot PCK](https://github.com/morluto/rea/blob/main/docs/godot-pck.md).
UnityFS AssetBundle containers (`.bundle`, `.ab`, YooAsset packages and other
byte-identified files) list `CAB-*` SerializedFiles and `.resS` members with
header and node facts; none/LZ4/LZ4HC member blocks stream on demand. Bundles
declaring asset-bundle encryption keep header facts with unknown directory
coverage; LZMA member bytes retain named occurrences with unavailable hashes.
Virtual member offsets are not physical carrier locations. Format 6–8 readers
cache one decoded block and support retained filtered/paged views. No
decryption or SerializedFile object decoding is provided. See
[UnityFS](https://github.com/morluto/rea/blob/main/docs/unityfs.md).
WeChat mini-program packages (`.wxapkg` files with `0xBE`/`0xED` marks) list
members with declared names and absolute carrier offsets; member bytes are
stored verbatim. `V1MMWX` PC-client packages report `encrypted` directory
status with unknown coverage and are refused on extraction; no decryption is
attempted. See
[wxapkg](https://github.com/morluto/rea/blob/main/docs/wxapkg.md).
U-Boot legacy uImages use the same inspection/extraction workflows. Prefer a
retained summary and selected inventory views for header facts or component
IDs. Header addresses/CPU/OS are declarations; payloads remain as stored.
Multi-file components retain their aligned physical ranges; the full payload
is also materialized. Unknown name text retains its original 32 bytes. See
[U-Boot uImage](https://github.com/morluto/rea/blob/main/docs/uimage.md).
## Bun standalone executables
A `bun build --compile` executable stores its module graph in Mach-O
`__BUN,__bun`, a PE or ELF `.bun` section, or an appended trailer.
`open_binary` then `inspect_artifact` reports root format `bun-standalone` with
the carrier, structural layout, module count, entry point, raw flags and
compile exec-argv; each module keeps its `/$bunfs/...` source name, loader,
encoding, sizes and SHA-256, with sourcemap, bytecode and module-info regions as
separate children. Use summary root metadata `entry_point_logical_path` as the
exact inventory `logical_paths` selector, then read the selected content node by
its artifact ID; the reader owns virtual-name normalization and indexed fallback. `extract-artifact` writes stored contents through the safe
output tree; route extracted JavaScript to `analyze_javascript_application`.
Layout labels describe record shapes, not a detected compiler version; unknown
or ambiguous layouts are unsupported. Bytecode is retained, not decoded, and the
executable is never run. See
[Bun standalone executables](https://github.com/morluto/rea/blob/main/docs/bun-standalone.md).
@@ -41,12 +41,6 @@ behavior unknown. Record truncation, unmatched breakpoints, changed target
identity and cleanup uncertainty as limitations, rather than treating no retained
event as proof that the behavior never occurs.
For same-session MCP comparisons, pass each capture inline or reuse it as
`{"kind":"retained-evidence","evidence_id":"ev_..."}`. Default comparison
keeps capture-local timing as provenance without treating scheduler or observer
latency as a behavior change; use an exact `trace_spec` event when timing is part
of the analyst question.
Detailed contracts and prerequisites: [Frida](https://github.com/morluto/rea/blob/main/docs/guides/runtime/frida-instrumentation.md),
[GDB/Rizin/Cutter](https://github.com/morluto/rea/blob/main/docs/reverse-engineering-tools.md),
[process capture](https://github.com/morluto/rea/blob/main/docs/guides/runtime/process-capture.md),
-1
View File
@@ -924,7 +924,6 @@ jobs:
tests/process-global/filesystemSnapshotFallbackCancellation.test.ts
tests/boundary/filesystem/typescriptCompiler.test.ts
tests/boundary/filesystem/releaseProposal.test.ts
tests/acceptance/byteEntropyWindowsIdentity.test.ts
tests/boundary/filesystem/binaryTarget.test.ts
src/contracts/sessionPathInputs.test.ts
tests/boundary/filesystem/directAnalysisSnapshotReplay.test.ts
@@ -95,31 +95,3 @@ jobs:
decode cortex ARM:LE:32:Cortex mrs/4/msp bx/2/lr
decode v8m ARM:LE:32:v8-m mrs/4/msplim bx/2/lr
decode xtensa Xtensa:LE:32:default entry/3/a1 retw.n/2
# A call through a writable slot keeps Ghidra's recorded target as an
# observed callee while callee coverage stays partial; a direct call is
# the complete control. The source-owned x86-64 bytes are never executed.
- name: Keep computed-call targets non-exhaustive in callee coverage
shell: bash
run: |
java_home_variable="JAVA_HOME_${GHIDRA_JAVA_MAJOR}_X64"
export JAVA_HOME="${!java_home_variable}"
probe_root="${RUNNER_TEMP}/rea-computed-call"
mkdir -p "${probe_root}/project"
{
printf '\270\001\000\000\000\303\314\314' # 0x1000: mov eax, 1; ret
printf '\270\002\000\000\000\303\314\314' # 0x1008: mov eax, 2; ret
printf '\377\025\052\000\000\000\303\314' # 0x1010: call [rip + 0x2a]; ret
printf '\377\320\303\314\314\314\314\314' # 0x1018: call rax; ret
printf '\350\333\377\377\377\303\314\314' # 0x1020: call 0x1000; ret
printf '\314%.0s' $(seq 24) # 0x1028: int3 padding
printf '\000\020\000\000\000\000\000\000' # 0x1040: slot = 0x1000
} > "${probe_root}/computed-call.bin"
test "$(wc -c < "${probe_root}/computed-call.bin")" -eq 72
timeout 180s "${GHIDRA_INSTALL_DIR}/support/analyzeHeadless" "${probe_root}/project" ComputedCall \
-import "${probe_root}/computed-call.bin" -loader BinaryLoader -loader-baseAddr 0x1000 \
-processor x86:LE:64:default -noanalysis -readOnly -deleteProject \
-scriptPath "${GITHUB_WORKSPACE}/tests/conformance/ghidra;${GITHUB_WORKSPACE}/bridge/ghidra" \
-postScript ReaComputedCallCoverageProbe.java "${GITHUB_WORKSPACE}/bridge/ghidra" \
> "${probe_root}/output.log" 2>&1 || { cat "${probe_root}/output.log"; exit 1; }
cat "${probe_root}/output.log"
grep -F 'REA_COMPUTED_CALL_COVERAGE_OK slot=observed-complete-discovery-unknown register=partial direct=complete callers=discovery-unknown' "${probe_root}/output.log"
-1
View File
@@ -204,7 +204,6 @@ Additional tools and host support depend on the target:
| Rust binaries | Embedded cargo-auditable crate names, versions, sources and dependency edges | Built-in static ELF/PE/thin Mach-O inspection; [Rust binaries](docs/rust-binaries.md) |
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](docs/pe-certificates.md) |
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](docs/pe-imports.md) |
| Windows shortcuts | Shell Link target, arguments, working dir, ExtraData and Tracker droid facts | Built-in static LNK inspection; [Windows shortcuts](docs/guides/native/windows-shortcut.md) |
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](docs/elf-notes.md) |
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](docs/binary-hardening.md) |
| Apple IPS crashes | .ips metadata, threads, images, registers and documented crash classes | Built-in static JSON inspection; [Apple IPS](docs/apple-ips.md) |
+4 -49
View File
@@ -1,9 +1,7 @@
"""Run REA inside an agentenv build computer for REA on the host: capture the app, draft its task and export it.
REA sends this file's source through the computer's ``computer-exec/v1`` route as
``python3 -I -c <source> <command> <JSON arguments>``. JSON that would exceed the bytes Linux
passes as one string travels as several arguments, which this file joins before parsing. Each command prints
one JSON line and exits. ``start``
``python3 -I -c <source> <command> <JSON arguments>``; each command prints one JSON line and exits. ``start``
leaves the capture running on its own: its stdin is a FIFO it holds open for reading and writing, so it never
sees the end of its input, and ``mark`` and ``stop`` write their requests there and wait for the capture's own
acknowledgement on its progress log. A small shell around the capture writes its exit status to a file, the
@@ -93,13 +91,6 @@ def _exit_code(capture: str):
return None
def _ended(capture: str, code: int) -> dict:
"""A capture that has exited: its status, the end of its progress log and its output's size, which REA reads
whole for the Evidence or failure it ended with, whichever step was waiting on it."""
return {"exited": code, "progress": _tail(os.path.join(capture, "progress.log")),
"output_bytes": os.path.getsize(os.path.join(capture, "capture.out"))}
def _event_line(path: str, offset: int, kinds: set, name):
"""The first progress line after ``offset`` whose event is of one of ``kinds``, for ``name`` when given."""
try:
@@ -128,49 +119,17 @@ def _wait(capture: str, offset: int, kinds: set, name, timeout: float) -> dict:
return {"line": line}
code = _exit_code(capture)
if code is not None:
return _ended(capture, code)
return {"exited": code, "progress": _tail(log), "output": _tail(os.path.join(capture, "capture.out"))}
if time.monotonic() > deadline:
return {"timed_out": True, "progress": _tail(log)}
time.sleep(POLL_S)
def _within(path: str, folder: str) -> bool:
return path == folder or path.startswith(folder + os.sep)
def _state_paths(values: list, work: str):
"""Each state path as an absolute path strictly under the home and outside REA's work folder, both as named
and with its links resolved, or the refusal of the first that is not. The capture resolves the links again
and retains what they lead to, so a link out of the home is refused here. Each passes on as named, so the
capture keeps what its spelling means, such as a hidden settings folder."""
home = os.path.normpath(os.path.expanduser("~"))
real_home, real_work = os.path.realpath(home), os.path.realpath(work)
spellings = {home, real_home}
paths = []
for value in values:
relative = value[2:] if value.startswith("~/") else value
path = os.path.normpath(os.path.join(home, relative))
if ".." in relative.split("/") or not any(path.startswith(spelling + os.sep) for spelling in spellings):
return None, f"The state path {value} is not under the computer's home, {home}."
# Every link on the way is followed, a dangling one too; a path that does not exist yet resolves
# through its nearest existing folder.
resolved = os.path.realpath(path)
if not resolved.startswith(real_home + os.sep):
return None, f"The state path {value} leads through a link to {resolved}, which is not under the computer's home, {home}."
if _within(path, work) or _within(resolved, real_work):
return None, f"The state path {value} is in REA's work folder {work}, which every capture leaves out."
paths.append(path)
return paths, None
def start(arguments: dict) -> int:
work, capture = os.path.abspath(arguments["work"]), os.path.abspath(arguments["capture"])
rea = shutil.which("rea")
if rea is None:
return _say({"error": "REA is not installed in this computer; start the computer from a request, which installs it."})
state_paths, refusal = _state_paths(arguments.get("state_paths", []), work)
if refusal is not None:
return _say({"refused": refusal})
if os.path.exists(capture):
return _say({"error": f"the capture folder {capture} is already used"})
apt = _apt(work, arguments.get("apt_timeout_s", 600))
@@ -184,8 +143,6 @@ def start(arguments: dict) -> int:
"--output", os.path.join(capture, "capture"), "--exclude", work, "--json"]
for value in arguments.get("arguments", []):
argv += ["--argument", value]
for path in state_paths:
argv += ["--state-path", path]
if arguments.get("package_file"):
argv += ["--package-file", arguments["package_file"]]
# Reading and writing, so the capture never reads the end of its input between requests.
@@ -218,13 +175,11 @@ def _request(capture: str, line: str) -> bool:
def mark(arguments: dict) -> int:
"""Request a checkpoint and answer with the capture's acknowledgement line as it wrote it: the checkpoint's
counts and up to ten of the files that changed, home-relative, with a count of the rest."""
capture, name = os.path.abspath(arguments["capture"]), arguments["name"]
code = _exit_code(capture)
log = os.path.join(capture, "progress.log")
if code is not None:
return _say(_ended(capture, code))
return _say({"exited": code, "progress": _tail(log)})
# A checkpoint already taken, whose answer was lost, is answered from the capture's own log.
taken = _event_line(log, 0, {"checkpoint"}, name)
if taken is not None:
@@ -315,4 +270,4 @@ def listing(arguments: dict) -> int:
COMMANDS = {"start": start, "mark": mark, "stop": stop, "run": run, "list": listing}
if __name__ == "__main__":
sys.exit(COMMANDS[sys.argv[1]](json.loads("".join(sys.argv[2:]))))
sys.exit(COMMANDS[sys.argv[1]](json.loads(sys.argv[2])))
+4 -42
View File
@@ -3753,20 +3753,6 @@ public final class ReaGhidraBridge extends HeadlessScript {
private JsonObject callRelationshipCoverage(
CallBoundaryObservation calls, Integer missingCallers) {
JsonObject coverage = completeResolvedCallCoverage();
JsonObject discovery = new JsonObject();
if (missingCallers != null) {
JsonObject callers = new JsonObject();
callers.addProperty("status", "unknown");
callers.addProperty("reason", "Recorded incoming references do not establish additional computed callers elsewhere in the program.");
discovery.add("callers", callers);
}
if (calls != null && !calls.computedCallSites().isEmpty()) {
JsonObject callees = new JsonObject();
callees.addProperty("status", "unknown");
callees.addProperty("reason", computedCallTargetLimitation(calls.computedCallSites()));
discovery.add("callees", callees);
}
if (!discovery.isEmpty()) coverage.add("discovery", discovery);
Integer omitted = calls == null ? null : calls.unrepresentedCalleeCalls();
JsonArray directions = new JsonArray();
if (missingCallers != null && missingCallers > 0) directions.add("callers");
@@ -3785,10 +3771,10 @@ public final class ReaGhidraBridge extends HeadlessScript {
}
JsonArray limitations = new JsonArray();
if (callerLimitation != null) limitations.add(callerLimitation);
if (omitted != null && omitted > 0) directions.add("callees");
if (omitted != null && omitted > 0) {
limitations.add("Ghidra's callee inventory omits targets of " + omitted +
" call reference(s) or decoded call(s) without destinations.");
directions.add("callees");
}
if (!limitations.isEmpty()) {
coverage.addProperty("status", "partial");
@@ -3838,24 +3824,6 @@ public final class ReaGhidraBridge extends HeadlessScript {
return coverage;
}
/** Name a few sites inline; reference reads and call-target queries recover the rest. */
private static final int NAMED_COMPUTED_CALL_SITES = 4;
/**
* A computed call's ReferenceManager targets are static candidates: Ghidra
* records no proof that other values cannot reach the call, so the callee
* set stays incomplete while the recorded candidates remain observed.
*/
private static String computedCallTargetLimitation(List<String> sites) {
if (sites.isEmpty()) return null;
int named = Math.min(sites.size(), NAMED_COMPUTED_CALL_SITES);
String listed = String.join(", ", sites.subList(0, named)) +
(sites.size() > named ? " and " + (sites.size() - named) + " more" : "");
return "Ghidra records call reference targets at " + sites.size() +
" computed call site(s) (" + listed + "); these static references do not establish" +
" every possible target, so additional callees remain unknown.";
}
private boolean unrepresentedCaller(Reference reference, Set<Function> callers) {
if (!reference.getReferenceType().isCall()) return false;
Function caller = functionAtOrContaining(reference.getFromAddress());
@@ -3890,11 +3858,9 @@ public final class ReaGhidraBridge extends HeadlessScript {
monitor.checkCancelled();
calls.observe(reference);
}
List<String> computedCallSites = new ArrayList<>();
for (Instruction instruction : instructions) {
boundary.observe(instruction);
FlowType flow = instruction.getFlowType();
if (!flow.isCall()) continue;
if (!instruction.getFlowType().isCall()) continue;
boolean destination = false;
for (Reference reference : instruction.getReferencesFrom()) {
if (!reference.getReferenceType().isCall()) continue;
@@ -3906,13 +3872,9 @@ public final class ReaGhidraBridge extends HeadlessScript {
calls.unresolved(instruction.getAddress(),
"Instruction decoder identifies a call but ReferenceManager supplies no call destination");
}
// The same decoded flow classifies resolve_native_call_targets'
// mechanism; its recorded targets are candidates, not a closed set.
else if (flow.isComputed()) computedCallSites.add(canonicalAddress(instruction.getAddress()));
}
return new CallBoundaryObservation(boundary.values(),
returnedCallees == null ? null : calls.unrepresented, calls.unresolvedCalls(),
List.copyOf(computedCallSites));
returnedCallees == null ? null : calls.unrepresented, calls.unresolvedCalls());
}
private final class FunctionBoundaryLimitations {
@@ -5358,7 +5320,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
}
}
private record FunctionAnalysisObservation(JsonObject dossier, List<String> limitations, JsonObject relationshipCoverage, boolean decompilerFailed) {}
private record CallBoundaryObservation(List<String> limitations, Integer unrepresentedCalleeCalls, JsonArray unresolvedCalls, List<String> computedCallSites) {}
private record CallBoundaryObservation(List<String> limitations, Integer unrepresentedCalleeCalls, JsonArray unresolvedCalls) {}
private record SwiftStringLiteralDecode(JsonArray literals, JsonArray omissions) {}
private record SwiftLiteralLayout(
String representation,
+1 -2
View File
@@ -1399,8 +1399,7 @@ def _serve_connection(connection):
break
except (ConnectionError, OSError):
# A lost client cannot confirm document closure or revoke this run.
# Keep the listener available for the owner to confirm cleanup.
should_stop = False
pass
finally:
try:
file.close()
-13
View File
@@ -24,16 +24,3 @@ func captureAccessibilityValue(_ source: Any?) -> AccessibilityValue {
}
return AccessibilityValue(value: NSNull(), gap: "AXValue type \(type(of: source)) is not a supported JSON scalar")
}
/** Keep an unavailable action list distinct from a successful empty list. */
func captureActionNames(status: Int32, success: Int32, names: [String]?) -> (names: [String], error: Int32?) {
guard status == success else { return ([], status) }
return (names ?? [], nil)
}
/** AX absence is distinct from a failed attribute read. */
func captureAttributeValue<Value>(status: Int32, success: Int32, absent: [Int32], value: Value?) -> (value: Value?, error: Int32?) {
if status == success { return (value, nil) }
if absent.contains(status) { return (nil, nil) }
return (nil, status)
}
+20 -45
View File
@@ -18,10 +18,9 @@ struct Request: Decodable {
struct Action: Decodable { var kind: String; var path: [Int]?; var direction: String?; var text: String? }
struct BoundaryFailure: Error { var code: String; var message: String }
func fail(_ code: String, _ message: String) throws -> Never { throw BoundaryFailure(code: code, message: message) }
func attribute(_ element: AXUIElement, _ key: String) -> (value: CFTypeRef?, error: Int32?) {
func attribute(_ element: AXUIElement, _ key: String) -> CFTypeRef? {
var value: CFTypeRef?
let status = AXUIElementCopyAttributeValue(element, key as CFString, &value)
return captureAttributeValue(status: status.rawValue, success: AXError.success.rawValue, absent: [AXError.noValue.rawValue, AXError.attributeUnsupported.rawValue], value: value)
return AXUIElementCopyAttributeValue(element, key as CFString, &value) == .success ? value : nil
}
func childCount(_ element: AXUIElement) -> ChildCount {
var count = 0
@@ -35,15 +34,13 @@ func children(_ element: AXUIElement, count: Int) -> ChildBatch<AXUIElement> {
return value as? [AXUIElement]
}
}
func windowBounds(_ element: AXUIElement) -> (bounds: CGRect?, error: Int32?) {
let position = attribute(element, kAXPositionAttribute)
let size = attribute(element, kAXSizeAttribute)
if let error = position.error ?? size.error { return (nil, error) }
guard let p = position.value, let s = size.value,
CFGetTypeID(p) == AXValueGetTypeID(), CFGetTypeID(s) == AXValueGetTypeID() else { return (nil, nil) }
var point = CGPoint.zero; var dimensions = CGSize.zero
guard AXValueGetValue(p as! AXValue, .cgPoint, &point), AXValueGetValue(s as! AXValue, .cgSize, &dimensions) else { return (nil, nil) }
return (CGRect(origin: point, size: dimensions), nil)
func text(_ element: AXUIElement, _ key: String) -> Any { (attribute(element, key) as? String) ?? NSNull() as Any }
func windowBounds(_ element: AXUIElement) -> CGRect? {
guard let p = attribute(element, kAXPositionAttribute), let s = attribute(element, kAXSizeAttribute),
CFGetTypeID(p) == AXValueGetTypeID(), CFGetTypeID(s) == AXValueGetTypeID() else { return nil }
var point = CGPoint.zero; var size = CGSize.zero
guard AXValueGetValue(p as! AXValue, .cgPoint, &point), AXValueGetValue(s as! AXValue, .cgSize, &size) else { return nil }
return CGRect(origin: point, size: size)
}
func observe(_ request: Request) async throws -> [String: Any] {
guard let app = NSRunningApplication(processIdentifier: request.pid) else {
@@ -87,22 +84,11 @@ func observe(_ request: Request) async throws -> [String: Any] {
guard AXIsProcessTrusted() else { try fail("accessibility-denied", "Grant Accessibility to the REA host in System Settings > Privacy & Security, then retry") }
let application = AXUIElementCreateApplication(request.pid)
AXUIElementSetMessagingTimeout(application, 2)
let windowsAttribute = attribute(application, kAXWindowsAttribute)
if let error = windowsAttribute.error {
try fail("accessibility-unavailable", "Cannot read accessibility windows: AXError \(error)")
guard let axWindows = attribute(application, kAXWindowsAttribute) as? [AXUIElement] else {
try fail("accessibility-unavailable", "Selected application does not expose accessibility windows")
}
guard let axWindows = windowsAttribute.value as? [AXUIElement] else {
try fail("accessibility-unavailable", "Accessibility windows are unavailable for the selected application")
}
let candidates = axWindows.map { (element: $0, observation: windowBounds($0)) }
if let error = candidates.compactMap({ $0.observation.error }).first {
try fail("accessibility-unavailable", "Cannot read accessibility window geometry: AXError \(error)")
}
guard candidates.allSatisfy({ $0.observation.bounds != nil }) else {
try fail("accessibility-unavailable", "Accessibility window geometry is unavailable; the selected window cannot be identified")
}
let matches = candidates.filter { $0.observation.bounds == bounds }.map { $0.element }
guard matches.count == 1 else { try fail("ambiguous-window", "Accessibility window geometry does not identify exactly one selected window; selected CG bounds \(bounds), AX candidates \(candidates.map { String(describing: $0.observation.bounds) })") }
let matches = axWindows.filter { windowBounds($0) == bounds }
guard matches.count == 1 else { try fail("ambiguous-window", "Accessibility window geometry does not identify exactly one selected window; selected CG bounds \(bounds), AX candidates \(axWindows.map { String(describing: windowBounds($0)) })") }
selected = matches[0]
}
if let action = request.action, let root = selected {
@@ -113,16 +99,13 @@ func observe(_ request: Request) async throws -> [String: Any] {
guard let available = count.value else {
try fail("element-count-unavailable", "Cannot validate selected accessibility path because child count failed with AXError \(count.error ?? -1)")
}
guard index >= 0 && index < available else { try fail("element-missing", "Selected accessibility path no longer exists") }
let childStatus = AXUIElementCopyAttributeValues(element, kAXChildrenAttribute as CFString, index, 1, &selectedChild)
guard childStatus == .success else { try fail("accessibility-unavailable", "Cannot read selected accessibility child: AXError \(childStatus.rawValue)") }
guard let item = (selectedChild as? [AXUIElement])?.first else { try fail("accessibility-unavailable", "Selected accessibility child was not returned") }
guard index >= 0 && index < available,
AXUIElementCopyAttributeValues(element, kAXChildrenAttribute as CFString, index, 1, &selectedChild) == .success,
let item = (selectedChild as? [AXUIElement])?.first else { try fail("element-missing", "Selected accessibility path no longer exists") }
element = item
}
var owner: pid_t = 0
let ownerStatus = AXUIElementGetPid(element, &owner)
guard ownerStatus == .success else { try fail("accessibility-unavailable", "Cannot identify accessibility element owner: AXError \(ownerStatus.rawValue)") }
guard owner == request.pid else { try fail("element-owner-mismatch", "Selected element belongs to another process") }
guard AXUIElementGetPid(element, &owner) == .success && owner == request.pid else { try fail("element-owner-mismatch", "Selected element belongs to another process") }
let outcome: AXError
switch action.kind {
case "click": outcome = AXUIElementPerformAction(element, kAXPressAction as CFString)
@@ -139,18 +122,10 @@ func observe(_ request: Request) async throws -> [String: Any] {
if nodes.count >= request.max_nodes { truncated = true; break }
let childCountResult = childCount(element)
var actions: CFArray?
let actionStatus = AXUIElementCopyActionNames(element, &actions)
let observedActions = captureActionNames(status: actionStatus.rawValue, success: AXError.success.rawValue, names: actions as? [String])
if let error = observedActions.error { gaps.append("AX actions unavailable at path \(path): AXError \(error)"); truncated = true }
let role = attribute(element, kAXRoleAttribute)
let title = attribute(element, kAXTitleAttribute)
let value = attribute(element, kAXValueAttribute)
for (name, error) in [("role", role.error), ("title", title.error), ("value", value.error)] {
if let error { gaps.append("AX \(name) unavailable at path \(path): AXError \(error)"); truncated = true }
}
let observedValue = captureAccessibilityValue(value.value)
AXUIElementCopyActionNames(element, &actions)
let observedValue = captureAccessibilityValue(attribute(element, kAXValueAttribute))
if let gap = observedValue.gap { gaps.append("\(gap) at path \(path)"); truncated = true }
nodes.append(["path": path, "role": (role.value as? String) ?? NSNull() as Any, "title": (title.value as? String) ?? NSNull() as Any, "value": observedValue.value, "actions": observedActions.names, "children_count": childCountResult.value.map { $0 as Any } ?? NSNull()])
nodes.append(["path": path, "role": text(element, kAXRoleAttribute), "title": text(element, kAXTitleAttribute), "value": observedValue.value, "actions": actions as? [String] ?? [], "children_count": childCountResult.value.map { $0 as Any } ?? NSNull()])
guard let totalChildren = childCountResult.value else {
truncated = true
gaps.append("AX child count unavailable at path \(path): AXError \(childCountResult.error ?? -1)")
+5 -7
View File
@@ -78,9 +78,14 @@ public final class NativeAotExtension {
new FrozenObjectAnnotator(program, manager).analyze(located.directory(), scan.getPointerLocations(), monitor, log);
}
monitor.checkCancelled();
report.addProperty("method_tables", manager.getMethodTableCount());
JsonObject coverage = auditFrozenObjects(program, scan.getPointerLocations(), monitor);
report.add("coverage", coverage);
if (coverage.get("frozen_objects_annotated").getAsInt() != coverage.get("frozen_object_candidates").getAsInt())
diagnostics.add("Some frozen-object candidates lack committed instance annotations; inspect coverage counts and Ghidra diagnostics.");
report.addProperty("status", diagnostics.isEmpty() ? "complete" : "partial");
report.add("reason", JsonNull.INSTANCE);
report.add("derived_memory", derivedRange.deepCopy());
JsonArray tables = new JsonArray();
for (var mt : manager.getMethodTables()) {
monitor.checkCancelled();
@@ -89,13 +94,6 @@ public final class NativeAotExtension {
identity.addProperty("type", mt.getMTType().getPathName());
tables.add(identity);
}
// Publish recovery facts only after every fallible recovery and audit step.
// A failed transaction is rolled back, so it cannot report committed metadata.
report.addProperty("method_tables", manager.getMethodTableCount());
report.add("coverage", coverage);
report.addProperty("status", diagnostics.isEmpty() ? "complete" : "partial");
report.add("reason", JsonNull.INSTANCE);
report.add("derived_memory", derivedRange.deepCopy());
report.add("types", tables);
commit = true;
return finish(log);
-10
View File
@@ -48,11 +48,6 @@ export const documentationSections = [
source: "guides/native/recorded-crashes.md",
route: "/recorded-crashes",
},
{
text: "Windows Shell Link shortcuts",
source: "guides/native/windows-shortcut.md",
route: "/windows-shortcut",
},
],
},
{
@@ -93,11 +88,6 @@ export const documentationSections = [
source: "guides/javascript/electron-observation.md",
route: "/electron-observation",
},
{
text: "V8 code cache inspection",
source: "guides/javascript/v8-code-cache.md",
route: "/v8-code-cache",
},
],
},
{
-2
View File
@@ -17,7 +17,6 @@ product pages. The VitePress documentation source is this `docs/` tree.
- [Go binary build metadata](guides/native/go-binaries.md)
- [Offline binary diagnostics](guides/native/binary-diagnostics.md)
- [Recorded crash evidence](guides/native/recorded-crashes.md)
- [Windows Shell Link shortcuts](guides/native/windows-shortcut.md)
## JavaScript and Electron
@@ -28,7 +27,6 @@ product pages. The VitePress documentation source is this `docs/` tree.
- [Passive Node and Electron runtime observation](guides/javascript/javascript-runtime-observation.md)
- [JavaScript static/runtime reconciliation](guides/javascript/javascript-runtime-reconciliation.md)
- [Electron file-page observation](guides/javascript/electron-observation.md)
- [V8 code cache inspection](guides/javascript/v8-code-cache.md)
## Browser and captured websites
+4 -67
View File
@@ -85,49 +85,9 @@ canonical hexadecimal form), `EXC_GUARD` subtypes,
pointer-authentication suffixes, and dyld missing-library strings. A report
that matches none of those rules is `unclassified`.
## Joining frames to the crashed Mach-O or dSYM
Pass `join_path` (`--join-path` in the CLI) to select one local Mach-O
executable or dSYM. UUID bytes in the report and the artifact's `LC_UUID` load
commands are compared in hexadecimal, so an uppercase `dwarfdump --uuid` value
and a lowercase report `uuid` match.
```bash
rea inspect-apple-ips /absolute/path/to/report.ips \
--join-path /absolute/path/to/App.app/Contents/MacOS/App --json
```
Shared-cache image sources `S` and `C` remain unjoined: UUID agreement does not
establish that a standalone image has the cached layout.
For a FAT container every slice is examined. An image joins only when exactly
one slice has that UUID **and** an architecture that agrees with the report's
`arch`; `arm64` and `arm64e` stay distinct, and an all-zero UUID never joins. A
UUID match with a different architecture, more than one compatible slice, or a
slice without a readable `__TEXT` segment is reported as a rejected image with
its reason and stays unjoined rather than being guessed.
An unknown UUID in another slice prevents a unique join unless its architecture
rules out a match. Missing or unrecognized architecture and unsupported CPU
subtypes remain unknown; display-name fallbacks do not establish agreement.
The parsed slice facts remain available even when no match can be derived.
`join.status` is `selected` with the artifact path, SHA-256, byte length, every
slice's offset, arch, CPU type and subtype, UUID, and `__TEXT` vmaddr, plus
`image_matches`, per-frame results, and join-specific limitations. A joined
frame reports `static_address`, the slice `__TEXT` vmaddr plus the frame's
recorded `imageOffset`, in decimal and hexadecimal. An unjoined frame reports
a reason instead; an unknown image UUID, an absent `imageIndex` row, and a
frame without `imageOffset` all stay unjoined rather than treated as aligned.
An artifact that is not a Mach-O image or FAT container reports
`join.status: "unreadable"` with a typed reason and leaves every report fact
intact. Symbol names printed inside the report stay recorded claims. Function
or symbol lookup at a joined static address, and matching dSYM symbol tables,
are not performed by this step.
Symbol names printed inside the report are recorded claims and are not
verified. Linux ELF cores use the separate
[recorded crash](guides/native/recorded-crashes.md) workflow.
Opened Mach-O and dSYM UUID joins remain unobserved. Symbol names printed
inside the report are recorded claims and are not verified. Linux ELF cores
use the separate [recorded crash](guides/native/recorded-crashes.md) workflow.
## Limits and failures
@@ -160,27 +120,4 @@ success or truncated integer facts.
rea inspect-apple-ips /absolute/path/to/report.ips --max-integer-text-bytes 16777216 --json
```
`max_join_bytes` (`--max-join-bytes`) independently bounds the join artifact
read, defaulting to 64 MiB. This workflow does not execute a target, launch
atos, or search for dSYMs.
## Join admission and failures
The join uses the shared Mach-O container, slice, command-table and segment
readers. Slice extents and `sizeofcmds` bound every identity observation; duplicate
UUIDs or malformed commands cannot select the first plausible value. Unreadable
join results retain the successfully read artifact's path, SHA-256 and byte count.
The integer-text allowance spans the report and join, including repeated integer
facts and derived static addresses. Reservation precedes derived address
expansion. If a selected join read, interpretation or allowance fails, the error
retains the completed IPS report. Join read limits and recovery use
`max_join_bytes`, even when the report and join paths are identical. Cancellation
retains the caller's reason independently of those completed facts.
When a failure follows a completed join-file read, `partial_observation` retains
both the decoded report and a `join_artifact` receipt with the independently
observed path, SHA-256 and byte count. Frame rejection reasons reference
`join.limitations`; source-sized architecture diagnostics are retained once per
image rather than copied into every frame. Universal-image admission includes
the shared reader's supported hidden ARM64 row and rejects overlapping slices.
This workflow does not execute a target, launch atos, or search for dSYMs.
+12 -33
View File
@@ -47,10 +47,6 @@ computer would not have that entry. An `--executable` runs from
the current directory, as it would from a shell, and a relative path to it is
resolved there.
Desktop-entry string values retain literal trailing spaces. For example,
`Path=/tmp/workspace ` and `Path=/tmp/workspace\s` name the same directory
ending in a space. Use the escaped spelling to make such a suffix visible.
Before writing anything the command refuses to start if a window of the
application is already open, or if the window list cannot be read. A launch
beside a running instance can hand off to it, and that instance's unsaved
@@ -94,20 +90,12 @@ Progress lines on stderr are JSON, `{"rea_progress": {"phase":
"desktop_capture", "message": ...}}`. Those a driver waits on also carry an
`event`: `{"kind": "window_open"}` once the window opens; `{"kind":
"checkpoint", "name", "trigger", "at_ms", "entries", "changes",
"retained_files", "retained_bytes", "changed_files", "more_changed_files"}`
once a checkpoint has been taken, the baseline, `stop` and `exit` included,
with the paths walked, the paths changed since the checkpoint before, the
files retained, and up to ten of the files (not folders) that changed,
home-relative: those under a `--state-path` or absolute `--argument` first,
then those the application held open, then those in its state directories,
then the rest, and last caches (a folder whose name ends in `cache`, such as
`~/.cache`) and WebKit or Chromium storage, each by path, with a count of the
rest. An application can rewrite dozens of cache files on a save, and KDE's
holds its service cache open, so the saved document would otherwise fall past
the ten;
and `{"kind": "checkpoint_refused", "name", "reason"}` for a requested name it
refused. A driver that sends `checkpoint <name>` waits for the event naming it
rather than for a fixed time.
"retained_files", "retained_bytes"}` once a checkpoint has been taken, the
baseline, `stop` and `exit` included, with the paths walked, the paths
changed since the checkpoint before and the files retained; and `{"kind":
"checkpoint_refused", "name", "reason"}` for a requested name it refused. A
driver that sends `checkpoint <name>` waits for the event naming it rather
than for a fixed time.
When the session ends the capture sends SIGTERM to the launched process, its
process group and every process that carries the run token, waits up to five
@@ -318,22 +306,16 @@ the whole name is compared, so `~/.config/GIMPshop` does not. Dotfiles in the
home named after the application (`.<name>rc`, or
`.<name>` followed by `-`, `.` or `_`, for a name of three or more characters)
that exist at the start join them, since an application may only read such a
file during the capture. So does KDE's settings file in the config folder,
`<name>rc` such as Tellico's `~/.config/tellicorc`, for each such name of three
or more characters, whether or not it exists yet. A `flatpak run` launch is named after its application
file during the capture. A `flatpak run` launch is named after its application
id rather than the `flatpak` launcher, and adds `~/.var/app/<id>`.
**Session.** When it started, how long it ran and why it ended; the launched
process's exit status; every process seen in the application's tree; every
client window with its class pair, titles, owner pid and type, marked when it
belongs to the application. Titles and class names decode xprop's quoted
delimiters, newline and tab escapes, and three-digit octal byte escapes;
unsupported escape spellings leave that string unknown rather than inventing
text. The session also records when the first application window appeared; the
belongs to the application; when the first application window appeared; the
most common class pair of the application's windows, for matching its window
later; files the application's processes held open under the walked roots; the last
8 KiB of the launched process's stderr, where a failing app's last words are,
with `stderr_truncated` when earlier output was dropped; and whether cleanup was verified. Each
later; files the application's processes held open under the walked roots; the first
8 KiB of the launched process's stderr; and whether cleanup was verified. Each
process records whether the capture started it: the launched process, a
process carrying the capture's run token, or a descendant of one, even when
its environment cannot be read, as for a zombie or a sandboxed renderer.
@@ -359,9 +341,7 @@ directory, which are named in the limitations instead; a selected path that is
not a directory brings the `-wal` and `-journal` paths beside it, so a database
selected on its own is captured with its logs), the state directories
as walked (their physical spelling, which differs from the inventory's when one
is a symlink), the selected paths (each `--state-path` and content-root
`--argument` as named, absolute, beside the physical path walked, so a
symlinked settings folder keeps what its name means), the paths every walk left out (the output folder and
is a symlink), the paths every walk left out (the output folder and
`--exclude` values), and the checkpoints in order: `baseline` before
launch, each requested checkpoint, `stop` or `time-limit`, and `exit` after the
application has gone. Each checkpoint records how many paths its walk saw,
@@ -390,8 +370,7 @@ unreadable SQLite sidecar from an absent one. Captures made before this field
was added must be made again before state inspection, task drafting or export.
Directories the application held files open in, but whose starting content was
not retained, are listed so a second capture can pass them with
`--state-path`, or `state_paths` on a build computer's `start_build_capture`,
and a limitation names them, spelled from `~/` under the home.
`--state-path`.
**Limitations.** What the capture could not establish, such as a truncated
walk or a missing window.
+11 -108
View File
@@ -33,10 +33,8 @@ The command returns Evidence with operation `export_desktop_application` and
predicate type `rea.desktop-application-export`, listing the environment name,
the seed and end `checkpoints`, owned paths, seed files, the seeds it
rewrote to remove the capture host's name and user (`scrubbed_files`), the
gzip seeds it compressed again only to clear their headers
(`recompressed_files`), the bundle files that still hold them
(`unscrubbed_files`), and written files, the last four by their path in the
bundle. A failed export, cancellation
bundle files that still hold them (`unscrubbed_files`), and written files,
the last three by their path in the bundle. A failed export, cancellation
included, removes the bundle it had not yet published, so no partial copy of the
user's data remains. If the bundle was published but its output handles could
not be closed, the command reports a cleanup error that carries the export
@@ -156,11 +154,9 @@ The class declares:
Files the capture recorded under a symlinked state directory's physical
spelling are mapped back to the innermost such directory's home spelling,
unless that spelling is outside the captured home, as when the home itself is
a symbolic link. When a selected link names an application state directory,
the bundle seeds the application's state path and restores the selected
alias at boot. Selections nested under that alias are seeded under the same
state path. An existing incompatible alias stops setup without replacing it. When two files map to one home path, the export uses the one present
before the capture, else the first by path, and a limitation names the other.
a symbolic link. When two files map to one home path, the export uses the one
present before the capture, else the first by path, and a limitation names
the other.
- Data hooks, because declaring any hook switches off agentenv's default data
plane: the class seeds the home once per boot and keeps it as the initial
state with `keep_initial`, `data/add` and `data/reset` call agentenv's `add`
@@ -173,43 +169,10 @@ retain is not seeded: one in the state view without retained content at that
checkpoint, or one the capture omitted (for example too large or past the
content budget). The result lists them in `unseeded_files` and the README
names them. When the application held files open in folders whose starting
content the capture did not retain, a limitation names those folders, spelled
from `~/` under the home: a file there is seeded only when the capture
retained it, as it does one written during the capture, and the limitation
names the seed files there or says there are none. The desktop Trash,
`~/.local/share/Trash`, is never the application's, so a file the user moved
there is neither owned nor seeded, even when a window title names it; a
limitation lists the files the Trash held at the seed.
Files that changed during the capture outside the owned paths are neither
owned nor seeded, so the app starts without what it saved there: MuseScore
keeps its settings in `~/.config/MuseScore`, which its name rules miss, and
showed its first-run wizard on every boot. A limitation and the README name
those folders, leaving out desktop services' files, each collapsed to its
folder in `~/.config`, `~/.local/share`, `~/.local/state` or `~/.local`, the
hidden folder of the home it lies in, or else its own folder, up to eight and
a count of the rest. Capture again naming them with `--state-path`, or
`state_paths` on a build computer, to own them.
Lock files the app holds while it runs are left out of the seed: a copied one
makes the app find its profile or document in use, and a LibreOffice Calc
bundle whose seed held its profile's `.lock` showed no window at boot. They
are `lockfile`, `lock`, `.lock`, `.parentlock`, `parent.lock` and
`SingletonLock` anywhere, LibreOffice's document lock `.~lock.<name>#`, and
`*.lock` or `*.lck` directly in one of the application's state directories.
Elsewhere such a file may be a project's own, such as `Cargo.lock` or
`yarn.lock`, which pin dependencies, and is seeded, even under a folder whose
name starts with a dot. A file the state view makes a document, or one named
itself by `--state-path` or an absolute `--argument`, is never left out,
whatever its name. A limitation and the README list them, up to
eight and a count, after "Left out of the seed". They stay under the owned
paths, so `data/get` returns the lock the running app holds, and `data/reset`
restores the seed without them. Other seed files the app keeps for its own
running are seeded as captured and named, collapsed to the folder that holds
them: WebKit or Chromium storage (such as `hsts-storage.sqlite`, `storage/`,
`mediakeys/`, `Local Storage/`, `IndexedDB/`, `Cookies`), which may hold the
app's own data, and caches (a folder whose name ends in `cache`); a stale one
can change how the app starts.
content the capture did not retain, a limitation names those folders. The
desktop Trash, `~/.local/share/Trash`, is never the application's, so a file
the user moved there is neither owned nor seeded, even when a window title
names it; a limitation lists the files the Trash held at the seed.
The application's dconf keys at the seed, if any, are written once when the
app first starts, also for an application that owns no files, and the class adds
@@ -219,11 +182,7 @@ existing user database through a `file-db:` profile, then runs `dconf compile`
on that dump and the captured keys, in that name order so the captured keys
win, and replaces `~/.config/dconf/user`. If that fails, the server prints a
one-line warning and the app starts without the captured keys. `data/reset` does
not restore them. No file of the bundle holds them, so `data/get` does not
return them and no task check reads them; a draft whose only change between
its checkpoints is to the application's dconf keys says so with
`uncheckable_changes`, and one whose dconf database changed but could not be
read says so with `unreadable_changes`.
not restore them.
## The seed checkpoint
@@ -293,26 +252,6 @@ export removes them from what the bundle ships:
boot, as it did the captured home before. The replacements read as the
computer's own: its user is `user`, with its home in `/home/user`, and
`localhost` is the name every host has for itself.
- A gzip-compressed seed, such as a Gnumeric workbook, is decompressed, up to
256 MiB, scrubbed as the format the state view read its document as, and
compressed again whether or not its document changed, so no file name,
comment or time its header held ships: with no file name, comment or extra
field, modification time 0 and the operating system unknown, so the same
document gives the same bytes on every host. It is deflated at the fastest
level, or stored when deflating would make it expand more than the 100 times
a view reads, so a seed that read still reads. It is listed in
`scrubbed_files` when its document changed, and otherwise in
`recompressed_files`, which the README counts rather than lists, so the
example and template files an app copies on first run do not bury the
document scrubbed; one already in that form ships as captured. The class copies it at
boot as it is, so the home it holds stays `/home/user`. One that does not
decompress ships as captured, its bytes searched, and one the state view did
not read is only searched. One whose document needs no edit but a member of
which has an extra field, where BGZF (`.vcf.gz`, `.bam`) and dictzip
(`.dict.dz`) files index their blocks, also ships as captured, its bytes
searched, so its blocks stay where its index says; one whose document
changed is still compressed again as one member, losing them, as the
export's limitation says.
- Binary files, SQLite databases among them, and text files over 256 MiB are
copied as captured. Then every file the bundle ships, the class module with
its dconf keys and the README included, is searched by a looser rule than
@@ -379,39 +318,6 @@ declared vendor repository installs the package already; when the URL is not
written as `http://` or `https://` or holds a user name or password; and when
the file is built for an architecture other than `amd64`, `arm64` or `all`.
## Grading once the app has saved
A task's verifier, `artifacts/<task>-check/verify.py`, grades the app's files
only once they have settled, because an app can save on its own timer after the
last change. Gnote saves a note 4 s after the last key, so a read taken as
soon as the agent stops finds the text on screen but not yet in the file. The
verifier reads the app's `data/get` every 0.5 s. It compares each file's bytes
between reads by their SHA-256, as `data/get` returns them, and grades once
every file has held for 3 s. The 3 s count from when a read returned the
current bytes, not when it began, so a slow read that brings a change late
does not count its own run as quiet. 15 s after the first read began, the
bound, the verifier grades the last completed read: no read starts at or
after the bound, and a read still running then is cut. Each result records
whether the files settled in `settled`, and whether the bound cut a read in
`read_cut`.
The window has a cost and a limit:
- An app whose files do not change during the check, such as one that saved
at once, is graded 3 s after the first read returns.
- A save that lands more than 3 s after the first read returns is still
missed. An app that saves 4 s after the last key is caught only when the
agent stops at least a second after that key.
- Files the app rewrites more often than every 3 s, such as a log, never
settle, so every check waits until the bound.
- The bound counts the reads too, so a slow `data/get` ends the check at
15 s. Only the first read, which every grade needs, runs to its own
timeout.
The bound and window are `SETTLE_BOUND_S` and `SETTLE_WINDOW_S` in the
verifier. `check_build_task` waits the same way before it grades each
witness stage; see [witnessing](desktop-build-witness.md#witness).
## Typed views
The views follow the [state view](desktop-application-state.md)'s formats and
@@ -426,10 +332,7 @@ as their entries, read with Python's `zipfile` from the file in place, with
REA's entry, size, compression ratio and archive limits and its wording for an
entry it does not read. The module reads the central directory itself, as REA
does, and shows `zipfile` only the records it lists, reading each entry in
bounded chunks no further than a byte past its declared size. A gzip file is
the view of the document inside it, with `"compression": "gzip"` beside its
format, decompressed with Python's `zlib` within the same entry size and ratio
limits, its members joined as REA joins them. A JSON file
bounded chunks no further than a byte past its declared size. A JSON file
holding a number JavaScript cannot read exactly, such as an integer above 2^53,
or whose retained value nests more than 500 levels deep, gets an `error` instead of a view, as in
REA. Duplicate keys keep their last value before the depth check. Text that is not one JSON
+1 -44
View File
@@ -40,16 +40,7 @@ The role follows from the reasons, in this order: `shared_desktop` for a file
in the desktop Trash, which the user deleted, whatever else names it;
`application_state` for application keys in dconf, or a state directory outside shared desktop
locations; `document` for a file named in a window title or selected by the
caller, even if the application holds it open, except that a selected file
under a hidden folder of the home, such as `~/.config/MuseScore`, a dotfile
there, or a lock an application holds, such as LibreOffice's `.~lock.<name>#`
beside a document, is `application_state`, judged by the path as the caller
named it, so a file reached through `~/.config/MuseScore` stays the application's
when that folder is a link to `~/settings/MuseScore`; the innermost selected
path holding a file decides, so a score selected itself inside the link's
target keeps its own name and is a document (a capture written before it
recorded selected paths is judged by their physical spelling);
`application_state` for another
caller, even if the application holds it open; `application_state` for another
open file outside shared locations; `shared_desktop` for other files in shared
locations; `unrelated` otherwise. A window title names a file only when no
other file name character adjoins the name, so `day.txt` does not match
@@ -84,9 +75,6 @@ whether the file was present, the SHA-256 of its retained bytes, and a view:
| `text` | Other UTF-8 text | Lines, up to 5,000 |
| `binary` | NUL bytes or invalid UTF-8 | None |
A gzip file is read as the document inside it; see
[compressed documents](#compressed-documents).
A JSON value holding a number JavaScript would round, such as an integer above
2^53, is not read rather than shown rounded.
@@ -205,37 +193,6 @@ so an archive of hundreds of thousands of entries costs no more to read
than one of a thousand. An archive whose central directory cannot be read has a
`view_error` of its own.
## Compressed documents
Gnumeric's `.gnumeric`, Dia's `.dia`, Xournal++'s `.xopp`, GnuCash's default
book and Inkscape's `.svgz` are XML compressed with gzip. A file that starts
with gzip's magic bytes, `1f 8b 08`, whatever its name, is read as the
document inside it: its members joined, then given a format and view by the
rules above under the file's own name, so a pointer or check into it is
written exactly as for the same document uncompressed, and the task draft is
the same. The version records the compression beside the format:
```json
{
"format": "xml",
"compression": "gzip",
"view": {
"root": { "name": "gnm:Workbook", "attributes": {}, "children": [] },
"truncated": false
},
"view_error": null
}
```
The document is read only when it is at most 16 MiB and at most 100 times the
file's size, an archive entry's limits, so decompressing stops there; past
them, or when the stream is damaged or cut short, the version has `format`
null and a `view_error`, and a check into it is undecided. A SQLite or dconf
database inside keeps its format and has a `view_error`, an archive inside is
read as an archive, and another gzip stream inside is binary. A gzip file
inside an archive reads as binary. Other compression, such as xz, Zstandard
or bzip2, is not read: such a file is binary.
## Kinds of change
Between consecutive versions a change is `created`, `deleted`, or a
+17 -174
View File
@@ -42,31 +42,9 @@ operation `describe_desktop_application_task` and predicate type
database is compared by its rows, since its hash leaves out the `-wal`
file. Text changes list each line
once and add `line_counts`, the lines found at both ends whose number of
occurrences changed. An XML file's change names its members by the
[selectors](#pointers-and-selectors) checks use, as an archive's already
does, and by position only where nothing names a member or where only the
members' order changed. Views appear only for the files the suggestions
cover, with arrays past 200 items and strings past 2,000 characters cut
occurrences changed. Views appear only for the files the suggestions cover,
with arrays past 200 items and strings past 2,000 characters cut
(`view_cut`).
- `values`, in each of those files: the values its change added, changed and
removed, each with the `pointer` a check names it by, its `kind` (`added`,
`changed` or `removed`), and `before` and `after`: the scalar, an element as
compact XML with the text the XML reader kept, spaces between runs included,
or a JSON value as compact JSON, cut at 600 characters, and null at the end
that lacks it. A file created or deleted lists what its one end holds,
`added` or `removed`: an archive's entries, an XML file's root element, a
JSON file's members or a key file's keys, each whole once. A value only
position reaches has `note: "by_position"`, and its pointer cannot go into a
check; noise an app writes as it saves has `view_state`, `renumbered`,
`volatile`, `timestamp` or `upkeep` (see `task`). `refers_to` names up to
three other elements a value equals the `key` or `id` of, with the
`attribute` holding it, as HomeBank's `category="44"` names
`<cat key="44" name="Groceries"/>`; where several elements share that key,
the one whose tag shares the attribute's stem, as `payee` and `<pay>`. What a check can
name comes first, then what only position reaches, then noise. A file lists
at most 20 values and 8 KB, the draft 24 KB, and `values_cut` says when a
limit left some out. Values cover XML, archives, JSON and key files; a text
or SQLite file's change and suggestions already carry its lines and rows.
- `task`: a draft with the name `change`, a placeholder prompt the export
refuses, and suggested checks derived from the net change of the files with
role `document`, or of the app's own state files when no document changed.
@@ -78,151 +56,21 @@ operation `describe_desktop_application_task` and predicate type
and the JSON, key file and XML views of a zip archive's entries, gain checks
by [selectors](#pointers-and-selectors) and never through an element by
position: in XML, first [`text_equals`](#whole-text) for each element whose
text changed, then elements added or removed whole, then values changed,
added and removed, an entry or a JSON value added whole through its
members, and an empty array or object as it is.
- A selector names a member by its tag, then by its identifiers (`name`,
`id`, `xid`, `xml:id`), then by one other attribute, a name-like key such
as Scribus's `ANNAME` or `inkscape:label`, a worded value or an integer of
up to five digits first, then by two such attributes together, as
Gnumeric's cells by `[@Col=3][@Row=2]`, and last by a coordinate, a long
number or a token, such as `XPOS` or `ItemID`, or an attribute naming
view state, such as which layer is `selected`. A pair counts every
member holding both its values, whichever attributes that member ranks
first. An element with identifiers is named by those alone. The selector
must name the member alone at both ends, with the same values. An
element whose one identifier changed, at its place among its tag's
members, with every other attribute and its children alike, is that
element renamed, as a MyPaint layer renamed from `Untitled` to `Sun`: it
is named by its first other attribute naming it alone at both ends, as
`[@src=data/layer-00.png]`, so the rename reads as its `name` changed,
and the old name gets a check that it names nothing, unless its change
is noise, as an id the app renumbered.
An identifier the app numbers by position names nothing: where the
members of a tag hold it as 1..N (or 0..N-1) in document order at both
ends, members were inserted or removed, and every member another
attribute names alone at both ends kept its number or moved it the way
the count did, one at least, as Planner's task `id` after a task is
inserted, members are paired by their other attributes, so an insertion
reads as one task added. A value two members swap, or an id appended at
the end, as Tellico's next `<entry id="4">`, leaves the ids identity.
- The text check names the paragraph rather than a run inside it: it goes up
from an element sitting in its parent's text, and from one named by its
tag or position alone to the nearest ancestor named by an attribute, as
AbiWord's `p` by its `xid`; where no attribute names one, to the nearest
ancestor within three levels its tag names with no step by position, as
MuseScore's `VBox` around untitled `<Text>` elements; and where only
position reaches the element, to the ancestor above the outermost step by
position, by an attribute of its own that names it alone where one does,
as an ODS sheet's `table:table[@table:name=Q1 Sales]` above its rows. The
text nodes and runs inside it get no other check, but an element holding
no text that is added or removed there, such as an image or a line break,
keeps its own, since the text check never reads it. A decimal that is an
element's only text, such as a spreadsheet cell's, is checked by its
number instead. Free text that gains a line, such as a shopping list,
uses `text_equals` on the whole text: the current XML checks cannot
compare an unstructured line as a whole item. This can reject a correct
edit that puts the item elsewhere or changes spacing. A record added
or removed whole, an element holding only elements, its fields, such as
Tellico's `<entry>` or a record of repeated `<field>` elements, never
makes the list holding records of its kind a check, whose text would hold
the records the edit left alone, except a record removed whole that only
position names, whose absence only the list's text shows. Text made of
runs is no record: an element whose tag names a paragraph or a piece of
text (`p`, Writer's `text:p` and `text:h`, `h1` to `h6`, `li`, `para`,
SVG's `text` and `tspan`, a `span` or an `a`), or whose runs split a
sentence at a space, as `Meet ` and a linked `Monday`. Nor does an
element holding a date the app stamps in parts take a text check.
- An element added to a list, where another element of its tag is at either
end, gets a `value_equals` for each of its attributes, best ranked first,
at most six, leaving out its selector's attributes, identifiers, times,
volatile values and view state, as HomeBank's new `<ope>` gets its
`amount`, `category` and `payee`. An element whose other attributes, two
or more, a like sibling holds every one of, as a new Planner task copies
its neighbour's defaults, and any other element added whole, get a check
that it exists. A record added to a list that a selector names, as
Tellico's `<entry id="4">`, gets a `text_equals` for each field, at most
five, each by its selector among the record's fields, as `[name=title]`,
leaving out a field repeating the record's identifier, a date the app
stamps in parts, and blank text or times. A record only position names,
such as Osmo's `<record>` of fields, also gets a `text_contains` for each
new field of three or more characters, at most five, at the nearest
ancestor a check can name, each absent from that ancestor's text at the
seed.
- A decimal written as text, such as `146.83`, is suggested as a number
with `tolerance: 0.001`, unless the seed's value is that close too.
- Noise leaves the suggestions when the file's change holds anything
else, a change only position reaches included: view state, which a tag
or attribute on the way names (the cursor, a selection, scrolling, zoom,
the viewport, windows, splitters, Inkscape's `namedview`, view and
configuration settings, the printer, a save's history, edit time,
editing duration and cycles, last-saved time, the generator and
document statistics), or the value of a name-like attribute or `key` on
the way, when it is one token of several words, as LibreOffice's
`config:config-item[@config:name=CursorPositionX]` inside
`[@config:name=ooo:view-settings]`; an attribute ending in `ID` or `Id`,
or an `rsid`, that the app renumbered, with everything else in its
element, as Gnumeric empties a formula cell whose shared expression it
renumbers, and an identifier the app numbers by position; volatile
values, UUIDs and epoch times, and durations and counts under a
document's metadata; times the app stamps, also as a date written in
parts whose tag, or one it sits in, names when the app made, changed or
saved something, as Tellico's `<cdate>` of `<year>`, `<month>` and
`<day>` (`cdate`, `mdate`, `created`, `modified`, `saved`, `updated`),
which no check ever reads, while a date a user enters in parts, as a
birthday, is an edit like any other; and `upkeep`, the
`loext:tab-stop-distance` attributes LibreOffice fills in `styles.xml`
while another archive entry holds an edit. However much noise
comes first, it never takes a check's place. When the change holds
nothing else, the checks stay and a note flags them: up to three in a
document, and as many as fit in the app's own state, where a view
setting may be the task.
- Whitespace an app lays out between elements is not checked, and values
that look like a time the app stamps, in ISO 8601, as C's `asctime`
writes it (`Thu Nov 5 09:20:31 2026`) or as a time of day to the
second (`05:16:21.675302540`), are left out; noise never makes its
ancestor's whole text a check.
- `guards`: checks an author may add to show the edit left the rest alone,
kept out of `task.checks`, with ids `guard-1` and on: for each suggested
`value_equals` or `text_equals` on a member of a list, a field on up to
two like siblings, nearest first, at its seed value, which the end holds
too: the check's own field where like siblings hold different values in
it, and otherwise one that varies among them, as Planner's `work` rather
than the `type="normal"` every task shares; never through a sibling only
position or a renumbered id pairs. A text file gets a `lines_present` of the
unchanged list items beside its first new line with the same marker, as
Zim's other `[ ]` action items, or else its nearest lines with words in
them. At most four a file. They pass on the seed, which the export allows,
since one check failing there is enough.
- `notes`: why `files` is empty, or why a file's suggestions are thin. Each
note has a `code` and a `message`. For a draft with no files: `no_change`
when no file under the home changed between the two checkpoints and nor
did the application's dconf keys; `unowned_changes` when files changed but
none under the owned paths, with up to five of them in `files`, each with
its home path and role (desktop services' files last), and how many more
in `more`; `uncheckable_changes` when the application's keys in a dconf
database changed, with the database in `files`: no check reads dconf, and
the export only writes the keys as they were at the seed checkpoint, so
make the task's change one the app saves in a file; `unreadable_changes`
when a dconf database changed but REA could not read it, with the read
failure in `error` (install `dconf-cli` or set `REA_DCONF_COMMAND` when it
is missing), since whose keys changed is then unknown; and `no_document`
when no file has the document role, as when no window title shows the
file's name. A capture again with `--state-path` naming the file, or
`state_paths` on a build computer's `start_build_capture`, makes it the
app's. For a drafted file, named in `file`: `by_position` when
changes reach their element only by position, the tags no attribute names
alone in `elements`, or when only the elements' order changed;
`view_state_only` when every change is noise, the checks kept in
`pointers`; `long_text` when an element's text changed past the 2,000
characters a text check compares, the elements in `pointers`; `cut_view`
when the views hold only part of the file; and `unreadable_view` when no
check can read it, as a document REA reads as binary.
text changed, then an element added whole that it exists, one removed whole
that it is absent, then values changed, added and removed, an entry or a
JSON value added whole through its members, and an empty array or object as
it is. The text check names the paragraph rather than a run inside it: it
goes up from an element sitting in its parent's text, and from one named by
its tag or position alone to the nearest ancestor named by an attribute, as
AbiWord's `p` by its `xid`; the text nodes and runs inside it get no other
check, but an element holding no text that is added or removed there, such
as an image or a line break, keeps its own, since the text check never
reads it. Whitespace an app lays out between elements is not checked, and
values that look like a time the app stamps, in ISO 8601 or as C's
`asctime` writes it (`Thu Nov 5 09:20:31 2026`), are left out.
The suggestions are a starting point. Keep the ones that say what the user
did; drop the ones that only follow from how the app saves its file. Read
`values` for what changed and its pointers, and `refers_to` for what a number
stands for, before writing the prompt and any check of your own. A LibreCAD
did; drop the ones that only follow from how the app saves its file. A LibreCAD
capture that added four holes to a drawing rewrote it from DXF R12 to DXF 2007,
so most of its lines changed; the draft suggests counts of `SECTION`, `ENDSEC`
and `CIRCLE`, and only `CIRCLE` going from 5 to 9 is the user's change.
@@ -281,9 +129,7 @@ The rules both evaluators follow:
`{"groups": {"GROUP": {"KEY": "VALUE"}}}`, so a setting is
`/groups/GROUP/KEY`; XML as `{"root": {"name", "attributes", "children"}}`;
SQLite as `{"tables": {"TABLE": {"columns", "rows"}}}`; a zip archive as
`{"entries": [{"name", "format", "view", …}]}`; a gzip file, such as a
Gnumeric `.gnumeric`, as the document inside it, so its pointers are those
of the same document uncompressed.
`{"entries": [{"name", "format", "view", …}]}`.
- With `tolerance`, a non-negative number, `value` must be a number, and the
value found passes when it is a number, or a string that is a decimal
numeral such as `"5.0000000000000000"`, at most `tolerance` from `value`.
@@ -476,10 +322,7 @@ The bundle then holds, besides the app and `tasks/smoke.json`:
The verifier finds the app among the computer's child environments, calls its
`data/get` under the run's grant of the computer's control token, and reads
the `state` data part. It reads again until the app's files settle, as
[grading once the app has saved](desktop-application-export.md#grading-once-the-app-has-saved)
describes, and each result's `settled` says whether they did, and `read_cut`
whether the bound cut a read still running. It raises when the app cannot be reached, so the run
the `state` data part. It raises when the app cannot be reached, so the run
fails rather than scoring the agent. The README lists the task, its prompt, its
checks and the recipe below.
+3 -9
View File
@@ -45,11 +45,9 @@ From a request, REA renders a bundle in a private temporary folder: a computer
app on the `ubuntu-24.04` base with the `reference` driver that installs the
app, opens it on demand with `gtk-launch <desktop entry>` from the computer
user's home, so its open and save dialogs start there rather than in the
framework's folder for the app, and keeps no seed and no data hooks. The app is not open when the computer starts:
`start_build_capture` opens it under REA's capture. When no capture is under
way, open it from the desktop's Applications menu, where agentenv lists it
under Other by the environment's name, such as `mousepad`; that entry launches
it through the app's server.
framework's folder for the app, and keeps no seed and no data hooks. The app is not open when the computer starts: agentenv lists it
in the desktop's Applications menu under Other by the environment's name, such
as `mousepad`, and that entry launches it through the app's server.
When the app is Electron, set `electron` to true. The install rewrites that
desktop entry's command with the same policy an export uses: `--no-sandbox`
@@ -98,10 +96,6 @@ in the folder and its files stay as they were shared.
REA runs the deploy with `run_bundle(keep=True, emulate=False)` on the local
sandbox provider, so only the host's own architecture is built. The first start
of a bundle builds its image and can take minutes; later starts reuse it.
agentenv makes a start wait while another agentenv run on this host writes its
bundle's ids, so starts at the same time wait for each other, several minutes
when a few run together; the progress line then says it is waiting for another
agent-env run to finish writing this bundle's ids.
While it waits, REA sends an MCP progress notification every five seconds with
agentenv's latest line, so a client that resets its request timeout on
progress keeps waiting. REA then waits until the computer's readiness checks
+18 -106
View File
@@ -10,7 +10,7 @@ grades the witness with the same checks the exported verifier runs.
| Tool | What it does |
| -------------------------- | ------------------------------------------------------------------------------------------- |
| `start_build_capture` | Downloads the package lists as the computer's user and launches the app under REA's capture |
| `mark_build_checkpoint` | Takes a named checkpoint and returns the capture's counts and the files that changed |
| `mark_build_checkpoint` | Takes a named checkpoint and returns the capture's acknowledgement with its counts |
| `stop_build_capture` | Stops the capture and returns its checkpoints and the task draft between `seed` and `end` |
| `export_build_environment` | Exports the bundle with the agent's task inside the computer and copies it to a folder here |
| `check_build_task` | Grades a task against the app's live state; with a `stage`, files the check in the witness |
@@ -41,34 +41,6 @@ See [desktop builds](https://github.com/morluto/rea/blob/main/.agents/skills/rev
from a `.deb` file, the build layer keeps that file at
`/opt/rea/package.deb` and the capture records it with `--package-file`. Do not open the app from
the menu first: the capture refuses an app whose window is already open.
When the capture ends before the window opens, REA reads its output. An app
that exits at launch, such as one missing a runtime, is reported with its
program, exit code, run time and the last lines of its stderr, at most 8
lines and 800 characters of the last 8 KiB the capture keeps, and
`details.diagnostics` keeps the capture's record of the session. A capture
that failed, such as one refusing to start, gives its own message and
remediation, with the whole failure in `details.diagnostics.command_failure`
and, when its cleanup is incomplete, the run tokens and paths it left in
`details.resources`. A checkpoint or stop that finds the capture ended
reads its output the same way.
`state_paths` names files or folders in the computer's home that hold the
app's state, relative to the home, after `~/` or absolute under it, at
most 16; each becomes a `--state-path` of the capture, so it is retained
from the start and its files count as the app's even before they exist.
REA counts a file as a document only when an app window's title shows its
name or `~/` path, so name the file the app will save when its title shows
something else, as MuseScore's shows a score's title: a file outside
hidden folders, other than a lock such as LibreOffice's `.~lock.<name>#`,
becomes a document, which the bundle owns, seeds and opens
at launch, and one under a hidden folder such as `~/.config/MuseScore` the
app's own state, also when that folder is a link elsewhere in the home. A
path with `..`, or naming the home itself, is refused before the computer
is asked; the computer refuses an absolute path outside its home, one in
`~/.rea-build`, and one a link leads out of the home or into
`~/.rea-build`, resolving a path that does not exist yet through its
nearest existing folder, and starts nothing.
3. Make the seed through the app with `act_on_build_computer`, saving it
where the user keeps such files, such as `~/Documents`, then
`mark_build_checkpoint` `seed`.
@@ -76,56 +48,22 @@ See [desktop builds](https://github.com/morluto/rea/blob/main/.agents/skills/rev
`mark_build_checkpoint` `end`.
5. `stop_build_capture`. The app is closed and the capture finishes. The
result lists the checkpoints with their counts, the package and the
sources the capture found, its limitations and the draft: each file under
the paths the export owns that changed between `seed` and `end`, with its
net change and, for XML files, archives, JSON and key files, its `values`,
the values the change added, changed and removed with what each was and
became, at the pointer a check names it by; suggested checks with a
placeholder prompt; and `guards`, optional checks that the rest kept its
seed values. The draft's
[task section](desktop-application-tasks.md#describe-the-change) describes
each. `notes`, each with a `code` and a `message`, say why a draft has no
files: `no_change` when no file under the home changed between them and
nor did the app's dconf keys; `unowned_changes` when files changed but
none under the owned paths, listing up to five in `files` with their
roles, desktop files last, and counting the rest in `more`;
`uncheckable_changes` when the app's keys in dconf were read and changed,
listing the database in `files`, since no check reads dconf (a version
the capture kept no copy of, as it keeps none of the database at the
start, counts as holding none of the app's keys);
`unreadable_changes` when a dconf database changed but REA could not read
it, such as without `dconf-cli`, with the read `error`, so whose keys
changed is unknown; and `no_document` when no file has the document role.
`unowned_changes` and `no_document` point at `state_paths`. They also say,
for a drafted `file`, why its suggestions are thin: `by_position`,
`view_state_only`, `long_text`, `cut_view` or `unreadable_view`.
sources the capture found, its limitations and the draft: each file that
changed between `seed` and `end` with its net change, and suggested
checks with a placeholder prompt.
6. Write the task: its name, the prompt in the user's own words, and checks
that hold after the change and not on the seed, from the suggestions,
`values` and any `guards` worth keeping.
that hold after the change and not on the seed.
7. `export_build_environment` with the task and a new or empty `directory`.
The export runs inside the computer with `--seed-checkpoint seed
--end-checkpoint end`, so it seeds what was saved at `seed`, scrubs the
computer's host name and user from it, and refuses a task whose checks
all pass on the seed. It also passes `--synthetic-seed`, since the agent
made the seed for the task, so the README does not call it the user's real
data. When the seed holds exactly one document and the app's desktop entry
takes files, the bundle's app opens that document at launch. A file moved
to the desktop Trash is not seeded, and the export's limitations name what
the Trash held at `seed`. The limitations and the README also name the
folders where files changed during the capture that the bundle neither
owns nor seeds, such as settings that keep a first-run wizard away, up to
eight collapsed to their top folder, such as `~/.config/MuseScore`, and a
count of the rest; capture again with them in `state_paths` to own them.
Lock files the app holds while it runs, such as LibreOffice's profile
`.lock` and its document lock `.~lock.<name>#`, are left out of the seed,
since a copied one can keep the app from starting, and the limitations
and the README list them after "Left out of the seed"; a project's
`Cargo.lock` or `yarn.lock`, a document or a path in `state_paths` is
seeded. They name seeded
WebKit or Chromium storage and caches too, which the app keeps for its
own running. When the application held files open in a folder whose
starting content the capture did not retain, a limitation names the
folder and the seed files in it. For a
all pass on the seed. A task larger than 96 KiB as REA passes it to the
computer is refused before anything runs. It also passes `--synthetic-seed`,
since the agent made the seed for the task, so the README does not call it
the user's real data. When the seed holds exactly one document and the
app's desktop entry takes files, the bundle's app opens that document at
launch. A file moved to the desktop Trash is not seeded, and the export's
limitations name what the Trash held at `seed`. For a
`.deb` app that no apt source the capture found offers, the export takes
the request's URL with `--package-url`; one apt offers installs from apt. REA lists the bundle's files with their sizes
and SHA-256 in the computer, into a file beside the bundle that it reads
@@ -140,15 +78,7 @@ A checkpoint name is lowercase letters, digits, dot, dash and underscore; the
capture's own `baseline`, `stop`, `time-limit` and `exit` are refused, and so
is a name already taken. The acknowledgement comes from the capture after it
has walked the home and retained what changed: `entries` walked, `changes`
since the checkpoint before, `retained_files` and `retained_bytes`, and up to
ten `changed_files`, home-relative, with `more_changed_files` counting the
rest. Folders are left out; files under `state_paths` come first, then
those the app held open, then those in its state directories, then the rest,
and caches and WebKit or Chromium storage last, each by path, so a save's
document leads the list. When the computer's REA is older than these lists,
as when `REA_BUILD_COMPUTER_REA_ARCHIVE` still names an earlier build, the
checkpoint is still taken and both are `null`: which files changed is
unknown. Pack the current REA into the archive to get them back.
since the checkpoint before, `retained_files` and `retained_bytes`.
Each step can be asked again after its answer was lost:
@@ -172,9 +102,7 @@ computer's control token. REA sends each command through agentenv's
`computer-exec/v1` route with that token, which stays in REA, and reads larger
outputs, such as the draft, the export's Evidence and the bundle's file
list, from files under `~/.rea-build` through `data/get`, since a command's
output is capped at 1 MiB. In a command's Evidence or failure read this way,
as in its own output, REA replaces the token in every string, after checking
the Evidence's digest; the bundle's files arrive as they are.
output is capped at 1 MiB.
Calls on one computer run one at a time, in the order REA receives them:
an action, a capture step, a check or a close waits until the call before
@@ -188,11 +116,7 @@ cancels the call in progress on it and refuses the calls still waiting.
Start a fresh computer from the exported folder with `start_build_computer`
and `{"kind": "bundle", "path": ...}`; agentenv boots the app with the seed,
on the seed's document when the export opens one. When the computer never
gets ready and the bundle's seed holds lock files, the start's error names
them: delete them from the bundle, or export again, which leaves them out.
A bundle records no state directories, so the error names only files by a
profile lock's name and LibreOffice's document locks.
on the seed's document when the export opens one.
Then, with the task exactly as exported:
| `stage` | Before it | Its checks must |
@@ -205,18 +129,7 @@ Then, with the task exactly as exported:
`check_build_task` reads the app's `data/get`, takes the views it returns
beside the files and evaluates each check with REA's evaluator, the one the
export validates tasks with and the exported verifier mirrors, one result per
check. Like the verifier, it grades only once the app's files settle, since an
app such as Gnote saves a few seconds after the last key. It reads `data/get`
every 0.5 s and grades once every file's bytes have held for 3 s, counted
from the read that returned them. 15 s after the first read began, it grades
the last completed read: no read starts then, and one still running is cut.
It sends progress notifications while it waits. The result's `settled` says
whether the files settled, and `read_cut` whether the bound cut a read; each
stage in the record keeps both. Every check therefore costs at least 3 s,
and a save that lands more than 3 s after the first read returns is still
missed; see
[grading once the app has saved](desktop-application-export.md#grading-once-the-app-has-saved).
Without `stage` it only grades. The `seed` stage begins a recording
check. Without `stage` it only grades. The `seed` stage begins a recording
of the display under REA's own request id, one per computer; a seed check
that fails after it, such as on a failed read of the app's state or a lost
reply to the recording's start, can be asked again, and asks for the
@@ -258,14 +171,13 @@ A stage whose checks do not show what it must ends the witness, and so does
- `recording/NNNN.mp4`, the recording's segments;
- `witness.json`, REA Evidence (`rea.desktop-build-witness`) holding the
verdict, the stage that failed if one did, the task's name and SHA-256,
each stage's results with how many logged actions came before it and
whether the app's files had settled (`settled`), the
each stage's results with how many logged actions came before it, the
computer's [action log](desktop-build-computer.md#act) as it keeps it, each
action with its arguments, timing and outcome, with `actions_dropped`
counting the earliest actions it no longer keeps, and the recording's
segments with their sizes and SHA-256. The record's limitations say when
the log dropped actions or cut arguments, which each entry names in
`truncated_arguments`, and which stages graded files that had not settled.
`truncated_arguments`.
The stages' rules read the log's count of actions and the last applied
action of each kind, which it keeps for the whole session, so they hold
+10 -16
View File
@@ -602,12 +602,6 @@ For partial Evidence delivery, verify that same-session readback preserves
identity and provenance without rerunning the provider. Include a no-writer
control that keeps completed Evidence inline and preserves the original failure.
For a seekable file with a known extent, distinguish a provably impossible wire
extent from an independent representation limit. Pair the malformed declaration
with a structurally possible over-limit control. A count-only truncated fixture
does not establish capacity behavior. This does not require decoding an opaque
compressed payload merely to apply its declared expansion limit.
When producer order is unspecified, synchronize fixtures on the observed request
or completion event. A filename, generated key, or fixed delay does not prove
which item completed before cancellation. Preserve real producer ordering and
@@ -708,16 +702,16 @@ the affected owners and applicable scenarios below. Include temporary snapshots
and one-shot commands. Use public CLI/MCP workflows where practical and a
controlled production seam for interleavings that cannot be reproduced reliably.
| Scenario | Assertion and control |
| --------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Target admitted; startup still pending | Status preserves the readiness distinction and observed startup activity; readiness requires the provider's documented proof. |
| First handshake is late, absent, invalid, or recovered during close | Initial and recovery paths retain the same authenticated identity facts. Foreign run/token controls cannot authorize cleanup; authenticated idle/native work survives the unauthenticated listener deadline. |
| Launch returns an error with a valid process handle | Cover already-exited and still-live owned processes. Verify observed exit separately from creation; failed termination or unavailable identity remains unresolved. |
| Cancellation arrives after workload exit, during result reads or temporary-root removal | Before the documented commit cutoff, return typed cancellation with available partial observations and actual cleanup status. After commit, assert the owning publication outcome and receipt. The non-aborted control succeeds; existing primary/cleanup failures keep their precedence. |
| Children have mixed health or cleanup outcomes | Missing peer health does not erase an observed failure or activity. Idle plus unknown stays unknown. Retry a failed child without closing a successful peer again, including after a close-option change. |
| Cleanup fails after success or primary failure | Public output retains the completed observation or primary diagnostic, remaining resources, and owner recovery action. Restore the denied resource and verify the owning cleanup retry. |
| Cleanup owners recover after an error was returned | Retain the original child and aggregate errors and project them after each owner releases. A successful peer stays released; the aggregate remains incomplete until all owners release. Preserve the first cause even when rejection is `undefined`. |
| Target exits without an acknowledgement, or transport disappears while it stays alive | Positive owned exit proof resolves target lifetime; other resources still need cleanup. Lost transport alone does not prove exit. Verify unrelated/attached targets survive and same-connection recovery works when supported. |
| Scenario | Assertion and control |
| --------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Target admitted; startup still pending | Status preserves the readiness distinction and observed startup activity; readiness requires the provider's documented proof. |
| First handshake is late, absent, invalid, or recovered during close | Initial and recovery paths retain the same authenticated identity facts. Foreign run/token controls cannot authorize cleanup; authenticated idle/native work survives the unauthenticated listener deadline. |
| Launch returns an error with a valid process handle | Cover already-exited and still-live owned processes. Verify observed exit separately from creation; failed termination or unavailable identity remains unresolved. |
| Cancellation arrives after workload exit, during result reads or temporary-root removal | Return typed cancellation with available partial observations and actual cleanup status. The non-aborted control succeeds; existing primary/cleanup failures keep their precedence. |
| Children have mixed health or cleanup outcomes | Missing peer health does not erase an observed failure or activity. Idle plus unknown stays unknown. Retry a failed child without closing a successful peer again, including after a close-option change. |
| Cleanup fails after success or primary failure | Public output retains the completed observation or primary diagnostic, remaining resources, and owner recovery action. Restore the denied resource and verify the owning cleanup retry. |
| Cleanup owners recover after an error was returned | Retain the original child and aggregate errors and project them after each owner releases. A successful peer stays released; the aggregate remains incomplete until all owners release. Preserve the first cause even when rejection is `undefined`. |
| Target exits without an acknowledgement, or transport disappears while it stays alive | Positive owned exit proof resolves target lifetime; other resources still need cleanup. Lost transport alone does not prove exit. Verify unrelated/attached targets survive and same-connection recovery works when supported. |
Shared process-supervision controls separate leader exit from output closure.
Cover output during held or failed cleanup, bounded retention and drain timeout,
+35 -22
View File
@@ -60,11 +60,23 @@ or ordinary script path when available. Identify the specific gap and what a
new or extended contract would add; upstream capability alone does not require
a new tool or equal support from every provider.
Use the caller's selected target and authority. Follow
[provider-backed workflow verification](testing.md#provider-backed-workflow-verification)
for independent observations, boundary controls and reporting unavailable
provider coverage. A scripted tool sequence does not establish agent selection;
that claim requires an actual agent evaluation.
Exercise the task through REA as a whole and compare relevant provider behavior
with an independent artifact or format observation where possible. Use the
caller's selected target and authority. Record the provider version, target and
observed coverage, and distinguish an unsupported operation from an empty
result or an untested path.
Include the boundary cases that could change the answer: provider labels versus
source facts, addresses versus file locations, partial results, and a useful
recovery after failure or session close when those apply to the task. Check that
composed results keep each observation's identity, limitations and source
association. Existing real-provider lanes and focused tests establish their
selected scenarios; they do not by themselves establish a different workflow or
provider equivalence. If a provider cannot be exercised, report that limitation
and continue with the available evidence. Measure agent tool selection only with
an actual agent evaluation; a scripted tool sequence does not establish it. Use
[testing guidance](testing.md#provider-backed-workflow-verification) to select
the cases that establish the proposed contract.
## Audit existing tools
@@ -82,10 +94,14 @@ identity, session lifetime and available alternatives. Matching text between
two renderers does not establish that their shared advice is correct or that an
agent selected the workflow.
Exercise each selected workflow before and after the tool call. Use
[workflow verification](testing.md#provider-backed-workflow-verification) to
check returned facts, partial outcomes, recovery and the scope of existing
provider evidence.
Exercise the selected public workflows, including the steps before and after a
tool call. Check task selection, input choices, returned facts and their source,
partial or unsupported outcomes, and applicable session and recovery behavior.
Compare native provider output and an independent artifact observation where
available. Inspect existing tests and real-provider lanes for the scenario they
actually cover; a passing test does not establish a different task or host.
Use [workflow verification](testing.md#provider-backed-workflow-verification)
for counterexamples and lifecycle controls relevant to the audit.
Report working routes as well as findings. Separate a reproduced contract bug,
an intentional limitation, a design opportunity and an unverified hypothesis;
@@ -166,7 +182,7 @@ does not establish another instance.
whether applicable static relationships could have been missed by discovery;
missing discovery is unknown. Preserve paths supported by observed edges,
and qualify negative and shortest-path claims by discovery scope. Missing
relationship coverage remains `unknown`. Computed candidates and unestablished incoming targets qualify directional discovery; they do not by themselves mean that recorded edges were omitted.
relationship coverage remains `unknown`.
- Declare process, filesystem, network, UI, and mutation effects truthfully;
enforce the requested target and lifecycle.
- Add limits only for real format, protocol, authority, or measured resource
@@ -332,15 +348,13 @@ stages that never ran.
and resource release separate. A valid process handle, successful launcher,
or selected target establishes only its own fact. Publish available identity
and exit observations even when launch fails; retain unknown ownership.
- Process exit does not establish output closure. The
[shared supervisor](https://github.com/morluto/rea/blob/main/src/process/ProviderProcess.ts)
owns diagnostic retention and bounded draining through cleanup and retries.
Report incomplete output separately from process cleanup. After verified stop
and bounded draining, release owned event-loop references even if output closure
stays unconfirmed. Keep the output capture owned for retry while process cleanup
is unresolved. Borrowed processes and streams retain their own lifetime and
reference state; select the matching
[supervision controls](testing.md#lifecycle-regressions) when changing this code.
- Process exit does not establish output closure. The shared process supervisor
retains at most 64 KiB of additional diagnostics from owned stop entry,
including pending cleanup and retries, and drains for up to one second after
verified stop. It marks output incomplete when closure cannot be
observed and releases owned event-loop references without changing the proven
process-cleanup outcome. Failed process cleanup retains capture for retry;
borrowed processes and streams retain their own lifetime and reference state.
- Authenticate and capture identity through the same boundary on initial and
recovery handshakes. Bound abandoned unauthenticated listeners where the
protocol permits it; that deadline must not expire authenticated idle sessions
@@ -359,9 +373,8 @@ stages that never ran.
analysis snapshot is part of `open_binary`; cancellation keeps that operation
and the caller's reason through the shared JSON reader.
- For cancellable observations, carry cancellation through result consumption
and awaited cleanup. Check the signal after the last awaited finalization step
unless the operation has passed its documented commit cutoff. Preserve the
owning operation's committed outcome and receipt after that cutoff.
and awaited cleanup. Before returning success, check the signal after the
last awaited finalization step.
Retain collected observations under typed cancellation. Preserve an existing
primary failure and let incomplete cleanup retain its own classification.
Preserve the caller's abort reason as the typed cancellation error's cause,
-7
View File
@@ -44,13 +44,6 @@ file memory regions and paths (`NT_FILE`), fault signal and code (`NT_SIGINFO`),
auxiliary vectors (`NT_AUXV`) across 32-bit and 64-bit images. Other owners or unparsed
notes keep `core` null.
Linux `CORE` decoding requires System V/unspecified (0) or GNU/Linux (3)
OSABI with ABI version zero. An unspecified OSABI alone does not establish
Linux origin. An explicitly different OSABI or ABI version leaves `core` null
and its Linux-specific `type_name` unknown, with a limitation explaining the
profile. Owner/type numbers, descriptor ranges and hashes, GNU-owned notes,
and complete table-examination coverage remain available.
`PT_NOTE` and `SHT_NOTE` are examined independently. The same file range may
appear twice when both tables declare it.
+65 -49
View File
@@ -439,19 +439,6 @@ 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 a Windows Shell Link (`.lnk`) without resolving or executing the
target:
```bash
rea inspect-windows-shortcut /absolute/path/to/shortcut.lnk --json
```
The workflow reports header flags and FILETIMEs, IDList items, LinkInfo volume
and network names, StringData arguments and icon location, ExtraData blocks
including Tracker droid facts labelled derived, and derived observations such
as argument length and a minimized ShowCommand. See
[Windows Shell Link shortcuts](../guides/native/windows-shortcut.md).
Inspect ELF PT_NOTE and SHT_NOTE tables without executing the image:
```bash
@@ -609,11 +596,10 @@ be verified. See [MCP contracts](../reference/mcp-contracts.md) for session life
[Ghidra first-query deadlines](../reference/mcp-contracts.md#ghidra-first-query-deadlines-and-recovery)
for import, client timeouts and recovery.
A one-shot command attempts provider-session and runtime cleanup before reporting
a failure. If cleanup remains incomplete, inspect the reported resources and
follow the error's remediation. [Same-session cleanup retry](../reference/mcp-contracts.md#retained-cleanup-retry),
reuse and `update_analysis_settings` apply to a live MCP connection.
For one-shot commands, `details.limit` reports `scope: "rea-invocation"` and
A one-shot command releases its provider session and REA runtime before it
prints a failure, so it omits the same-session reuse and
`update_analysis_settings` guidance an MCP connection receives. Its
`details.limit` reports `scope: "rea-invocation"` and
`applies_at: "next_invocation"`; set the named `environment` (or the per-call
`parameter`) for a new invocation and rerun. For example, a Ghidra decompile
deadline names `REA_GHIDRA_DECOMPILE_TIMEOUT_SECONDS`.
@@ -642,10 +628,12 @@ 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.
A binary CLI replay reports the current invocation's selected path while leaving
the saved Evidence and snapshot query unchanged. See
[caller-selected paths](../reference/mcp-contracts.md#caller-selected-paths)
for primary and auxiliary path fields and their relation to Evidence identity.
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.
@@ -1151,23 +1139,6 @@ 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.
`rea environment-export` prints the written files and exits `0` on success.
Input-pair refusals, an occupied destination, a missing package tarball, and
invalid labels fail before writing output. Host permission failures identify
whether reading or writing was denied and retain the affected path.
A failure during writing can leave a partial folder. Inspect it before removing
it and retrying; the next attempt refuses a non-empty destination. When one
file caused the failure, the diagnostic names that file. A folder-sync failure
can report only its cause.
If `details.partial_observation.kind` is `environment-bundle-publication`, the
bundle was already published. Its `directory` and `files` record the committed
output, even when a later identity check or cleanup failed. Inspect that bundle
and resolve the reported failure before using or replacing it; the receipt is
not an instruction to delete published output. Cleanup resources remain a
separate obligation.
## Compare an environment with its application
`rea environment-compare <manifest.json> --evidence <capture.json> --probe <probe.json> [--probe <probe.json> ...]`
@@ -1359,17 +1330,62 @@ retains failed stages for retry; the CLI exposes no separate retry command.
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.
A `cancelled` result does not by itself determine the exit status. Commands
that handle SIGINT or SIGTERM assign `130` or `143` after their operation and
cleanup settle; their structured result can be printed before that status is
assigned. Read the process status and typed result separately.
`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.
Commands with `--evidence_output` save the complete Evidence bundle before
delivering the selected view. For committed-file receipts, retained partial
Evidence and missing source records, see
[Evidence publication and source recovery](../reference/mcp-contracts.md#evidence-publication-and-source-recovery).
Other outputs have their own receipt shapes; for example,
[environment export](#export-an-environment-bundle) reports a published 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`), desktop application capture and state
(`capture-desktop-application`, `inspect-desktop-application-state`,
`export-desktop-application`, `describe-desktop-application-task`), 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 a failure retains a completed observation and no bundle committed,
`details.source_closure` states what that observation cites. `publication` is
`not_published`. `records` holds authentic source Evidence this invocation
captured, within 64 KiB. `omitted` lists up to 64 other identities and
`omitted_count` the total: `not_captured` means the invocation holds no such
record, so nothing is shown for it or its ancestors; `not_inlined` means it was
captured but is not in this response. `complete` is true only when every cited
record is in `records` or embedded in the observation. If examining the closure
itself fails, `collection_failure` names the error type and the closure state is
unknown; the original failure is still returned. Nothing is retained after exit,
and running the command again is a new observation.
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
+2 -9
View File
@@ -390,15 +390,8 @@ A scoped report has `scope.mode: "explicit"`. Its `scope_checks` determine
`environment_healthy` summarizes the full audit. `--target` adds a target check,
but the report remains audit-wide unless a scope option is also supplied.
With one selected client, Ghidra, IDA, JADX and Kuna provider checks use the
client's recorded environment over the terminal it inherits. If that registration
cannot be read or a setting a provider needs cannot be resolved, its available
value is `null` and its check is `unknown`; its configured value is also `null`
when the configuration setting itself is unknown. Doctor does not infer
readiness from the terminal. A missing registration still uses terminal settings
for setup. Other checks, including Hopper, use the terminal running doctor. A
client-started server may receive a different environment, and doctor cannot
observe it. When an available
Provider checks read the terminal running doctor. A client-started server may
receive a different environment, and doctor cannot observe it. When an available
provider relies on a setting that a registration's file does not record, or
records with other text, doctor adds a non-blocking
`registration_environment:<client>` check with `classification: "unknown"`. The
+1 -5
View File
@@ -188,7 +188,7 @@ and during shutdown.
property value remains available in `properties`.
- **Device paths are not host paths.** `base_apk_device_path` values live on
the device filesystem. Only pulls and captures produce host-local paths,
with byte size and SHA-256 digest on success. A pulled file's host name is
each paired with byte size and SHA-256 digest. A pulled file's host name is
the device path's final POSIX segment. REA preserves the original device
path and filename; a missing final segment or `.`/`..` is refused before
output creation. Windows hosts follow [Windows filename rules](https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file),
@@ -249,10 +249,6 @@ and during shutdown.
- Pull and capture outputs are additive: the destination must not already
exist. Re-running a completed pull refuses the existing directory instead
of overwriting evidence.
- If a single-file pull fails and REA observes a regular file at the destination,
the error includes its `local_path` with `transfer_status: "unconfirmed"`
and `digest_status: "unknown"`. The file remains in place; the receipt does
not establish that the transfer completed.
- Cancellation during a package pull preserves caller-selected output. The
error includes partial observations identifying completed artifacts and the
interrupted local path; remaining APKs were not attempted.
+3 -14
View File
@@ -90,20 +90,9 @@ column limit. The selected database is readable and is not reported as
malformed; request fewer columns per call instead. `row_limit`
requires `table`, defaults to 20 and accepts positive safe integers. Samples
remain subject to the byte and worker resource limits below, regardless of row count. The result reports
the selected limit, returned count and whether more rows were observed.
Rows are read in a stated unique order reported as `rows.order`: `rowid` for
rowid tables, and `primary_key` for `WITHOUT ROWID` tables, reusing the
primary-key index's own direction and declared collation. `rows.row_offset`
reports the rows skipped before the window and `rows.next_row_offset` the offset
to request for the following page; it is `null` once the window reaches the end.
Pass `row_offset` (CLI `--row-offset`, requires `table`, default 0) to skip into
a table rather than repeating earlier rows. A table that declares all of
`rowid`, `_rowid_` and `oid` as columns and has no primary key reports
`order: "unspecified"` and refuses a non-zero `row_offset`, because the skipped
prefix would be arbitrary. Pages compose only while the reported database and
WAL SHA-256 match; re-inspecting a changed snapshot restarts the sequence.
View, virtual-table and shadow-table row reads are unsupported.
the selected limit, returned count and whether more rows were observed. Row
order is unspecified; a truncated sample does not prove anything about omitted
records. View, virtual-table and shadow-table row reads are unsupported.
SQLite integers are decimal strings so every signed 64-bit value survives JSON
serialization. Real values retain their numbers; `"Infinity"`, `"-Infinity"`
@@ -265,9 +265,6 @@ retained arrays and counters without invoking application iterators or Array
species.
Listener-chain recognition distinguishes owned, unowned and unknown metadata.
Only a directly owned wrapper establishes hook dispatch. An application's data
link to an owned wrapper remains unknown: `.listener` can name that wrapper
without invoking it.
Data links may be inherited; absence is established only after completing the
bounded prototype lookup. Exhausting either walk, encountering a getter or seeing a Proxy leaves
ownership unknown. The hook preserves native registration without adding another
@@ -50,9 +50,6 @@ A path-filtered reference reports the caller-selected path while keeping its
shared node identity. Item selection also accepts any retained path when it
identifies one module; if several module kinds share that path, select the
reported `node_id`. Node-ID selection uses the stable representative path.
Exported symbol bindings reuse the `javascript-module` kind but name one export
of their owning module, so module pages, items, and summary counts include only
module units and an exported module never becomes an extra path candidate.
## Retained module facts
@@ -86,11 +86,6 @@ properties and remains readable without migration.
## Select sources before analysis
Explicit `source_paths` retain the acquisition owner's requested order, including
selected dependency files. First-party priority applies only to unscoped analysis.
Recovered syntax or incomplete semantic examination keeps Electron coverage
partial even when useful windows or IPC facts were retained.
For a question about known entry sources, supply exact artifact-relative file
paths in `source_paths`, or repeat `--source-path` on either CLI route:
@@ -328,12 +323,6 @@ failure does not authorize expanding relationships that parent admission decline
## What is reconstructed
Electron `BrowserView` and `WebContentsView` expose `webContents` and accept
`webPreferences`, including declared preloads. `BaseWindow` has neither surface
of its own. Preload associations retain the parsed constructor's mechanism rather
than labeling every view as a BrowserWindow. These are static associations, not
proof that a renderer or preload executed.
The projector reuses the content-addressed artifact inventory and safe artifact
readers. It retains canonical artifact-relative paths, exact byte counts,
SHA-256 digests, inventory IDs, ASAR container identity, and `.asar.unpacked`
@@ -526,25 +515,6 @@ If fingerprint construction reaches its structural
bound, the fingerprint is unavailable and its status is `truncated`; a bounded
prefix is never presented as a complete stable fingerprint.
## Cached-data compilation boundaries
The [feature specification](../../specs/javascript/v8-cached-data/spec.md) defines
the static admission and uncertainty contract.
A `node:vm` compilation reports whether `cachedData` is declared, absent or
unknown. Explicitly supplied undefined bytes are treated as absent; shadowed
identifiers remain declarations. Source syntax alone does not establish valid cache bytes, V8 acceptance
or execution. The summary counts declared compilations, not executions.
A proven synchronous filesystem read or awaited promise-based read supplies a
cache path. The producer retains its path base: source-directory expressions
can name inventoried artifacts, while relative literal paths depend on the
target working directory and remain unresolved. Other supplied values retain
their bounded source expression. Declared paths, exact-file lookup
status, candidate paths and resolution limitations remain separate; an unresolved
path never becomes a resolved file. Opaque inventoried cache bytes can remain
unavailable to the JavaScript resolver even when their occurrence is known.
## Authority and unknowns
API identity is a derivation: a familiar method or property name does not
-121
View File
@@ -1,121 +0,0 @@
# V8 code cache inspection
`inspect_v8_code_cache` reads one explicitly selected V8 code cache container as static
bytes: a bytenode `.jsc`, the bytes `vm.Script.createCachedData()` writes, or a cache
carried inside a Node single-executable application or an Electron app. It never executes
the file, never starts a V8 runtime, never installs bytenode, and never invokes a
decompiler.
```bash
rea inspect-v8-code-cache /absolute/path/to/module.jsc --json
rea inspect-v8-code-cache /absolute/path/to/module.jsc --minStringBytes 8 --json
```
The MCP tool is `inspect_v8_code_cache` with the same inputs and the same retained
Evidence.
## What is read
A V8 code cache is a producer-written container, not a documented file format. This reader
admits one container shape and reports everything else as an observation, so a build it
has never seen still yields facts instead of a false parse.
The 32-byte header is eight little-endian 32-bit words. Every word is reported in
`header.fields` with its byte offset, its observed value, the role this reader believes it
has, and how confident that role is:
| Offset | Role | Status |
| -------------- | -------------- | ----------- |
| `0x00` | magic | confirmed |
| `0x04` | version hash | inferred |
| `0x08` | source length | confirmed |
| `0x0c` | flag hash | inferred |
| `0x10` | build constant | inferred |
| `0x14` | payload length | confirmed |
| `0x18`, `0x1c` | reserved | unconfirmed |
`confirmed` means the role is demonstrated by the reader's own tests against the producer.
`inferred` means the word's behaviour is consistent with the role but a different producer
could plausibly differ. `unconfirmed` means the word is reported and nothing more is
claimed.
Only three facts gate admission:
1. the header size this reader pins (32 bytes);
2. the V8 code cache magic high half, `0xc0de`. The low half is
`ExternalReferenceTable::kSize` and varies per V8 build, so it is reported as a build
fingerprint and never required to match a fixed value;
3. the declared payload length must equal the bytes that follow the header exactly.
`header.source_length` is the producer's recorded source length in UTF-16 code units.
V8's serializer calls this field the source hash; on the producers measured here its value
equals the source length exactly, including sources whose UTF-8 byte length differs, so the
reader reports it under the name the measurement supports. Treat that name as an
observation, not as a claim about what any other build stores there.
`header.producer` names the producer family from the flag bits in that word. Bit 29
marks `vm.compileFunction` (`compile_function`); bit 30 marks `vm.SourceTextModule`
(`module`). When neither flag is present, the producer remains `unknown`:
`vm.Script`, bytenode output and older unflagged function caches cannot be
separated by those bits alone. The source-length mask is `0x1fffffff`, so both
flags sit outside the reported length. This describes the admitted header
profile rather than a version-independent V8 ABI.
## Failed inspection and provenance
After a stable read completes, format and observation-budget refusals retain a
`file-snapshot-hash` partial observation with the selected path, exact SHA-256 and
byte count. This proves which bytes were read; it does not claim the format was
successfully decoded. A refusal before read completion does not invent an identity.
Descriptor cleanup remains owned for retry if finalization fails.
## Embedded byte patterns
`embedded_strings` lists printable runs found in the payload, each with its absolute byte
offset in the selected file and its observed byte length. `min_string_bytes` (default 4)
sets the shortest reported run; `max_strings` caps how many may be reported, and exceeding
it fails without partial success.
These are **byte-pattern observations inside serialized V8 heap data, not parsed heap
objects**. A reported run is not proof V8 retained it as a string. A literal that is absent
from the payload is not proof it is absent from the program: V8 may encode a string in a
form the run scan does not surface, and a string can be produced at runtime rather than
retained.
A run longer than 4096 bytes is reported with `truncated: true` and a `retained_bytes`
prefix.
## Limitations
Every result states these, and they are part of the contract:
- the payload is serialized V8 heap data, not source; no logic is recovered and no
bytecode is decompiled;
- recovering logic needs a version-pinned V8 decompiler (View8-style) that this tool
neither provides nor depends on;
- header roles beyond magic, source length, and payload length are version-dependent
observations;
- the tool never executes the selected file and never claims recovered source.
## Failure behaviour
An unknown magic is a typed `unsupported_target` failure, because the file is not a V8
code cache. A short header, a payload-length disagreement, or bytes past the declared
payload is a typed `invalid_request` failure. Neither is a partial parse, and no Evidence
is recorded for them.
`max_file_bytes` (default 32 MiB, ceiling 64 MiB) is the caller-selected read budget. The
payload SHA-256 is computed over the declared payload, so two containers with the same
payload length and content but different version hashes have different whole-file digests
and identical payload digests.
## Relationship to other tools
- `inspect_binary_layout` reads the surrounding ELF/PE container of a single-executable
application; it does not decode the embedded code cache.
- A cache embedded in a Node single-executable application or an Electron app must be
**extracted first**: this reader admits a container whose magic is at byte zero, so
passing the surrounding ELF/PE executable here returns `unsupported_target`. Run
`extract_artifact` on the application, then pass the extracted `.jsc` path to this tool.
- `inspect_artifact` inventories the file without decoding V8 structures.
- This tool is in the same family as `inspect_python_pyc`, `inspect_erlang_beam`, and
`inspect_kernel_module`: one explicit local file, bounded parsing, no execution.
+7 -3
View File
@@ -212,9 +212,13 @@ rea inspect-managed-members /absolute/path/Assembly.dll --type Namespace.Type \
bundle format. Its parent directory must exist, and it never replaces an
existing path. Relative paths resolve against the command's working directory.
The option also works with complete delivery when a saved bundle is useful.
Summary without a destination is rejected before analysis. For partial Evidence
and committed-file receipts after failure or cancellation, see
[Evidence publication and source recovery](../../reference/mcp-contracts.md#evidence-publication-and-source-recovery).
Summary without a destination is rejected before analysis. A publication failure
returns a typed error with the completed Evidence under
`details.partial_observation`; it does not report successful summary delivery.
Cancellation before publication prevents the new output. Once atomic
publication has started, it can complete despite cancellation; a cancelled
delivery then retains the committed path and byte count under
`details.diagnostics.evidence_bundle`.
Copy `normalized_result.parent_evidence_id` from the summary into the
`source.evidence_id` field of the `managed-members` request above, then save that
-6
View File
@@ -272,12 +272,6 @@ regular files only and rejects symbolic links.
## Byte entropy triage
Entropy admission preserves the caller's spelling in `caller_input_path`. On
Windows, the selected regular file's case-only filesystem spelling is used
consistently in committed paths through both CLI and MCP. Symlink, junction and
short-name aliases are not replaced with a different path. General path
normalization remains lexical and does not probe source-discovered paths.
`inspect_byte_entropy` / `inspect-byte-entropy` profiles any local regular file
without an analysis engine, execution, extraction or network access.
@@ -332,10 +332,6 @@ crashed run can be recovered by the next run. Failed descriptor or staging clean
could not be written or read, for example on a full disk, the export stops with
an error instead of decompiling the rest. Files and records already committed
stay, `decompile-index.json` is still written, and a later resume continues.
If the index was published before cancellation or a later failure, the error's
`diagnostics.published_output` retains its `output_directory` and `index_path`.
This receipt confirms publication, not completion of every selected function.
Failed staging cleanup retains the same receipt through cleanup retry.
Type inspection selects one exact database pathname or typed data address.
Struct/union fields, enums, pointers, arrays, typedefs, size, alignment, packing
@@ -1135,9 +1131,6 @@ original JSON scalar types. Values that JSON cannot represent faithfully,
including unsafe integers, negative zero and non-finite numbers, remain null
with a path-specific gap and incomplete capture coverage. Null alone does not
establish that an accessibility value was successfully read or absent.
Failed role, title, value or action reads add a path-specific gap and mark the
capture truncated. An empty `actions` array with an action-read gap means the
available actions are unknown.
An initial PID or window mismatch is `invalid_request` on `pid` or
`window_id` and leaves provider availability intact. A selected window that
-94
View File
@@ -1,94 +0,0 @@
# Windows Shell Link shortcuts
`inspect_windows_shortcut` / `inspect-windows-shortcut` answers whether one
selected local file is a Windows Shell Link (`.lnk`), which target path,
arguments, working directory, and icon location it records, which volume and
network names `LinkInfo` stores, and which ExtraData blocks follow the
strings. It reads the file without resolving or executing the target and does
not require a Windows SDK, COM, or a network.
```bash
rea inspect-windows-shortcut /absolute/path/to/shortcut.lnk --json
```
The CLI resolves relative paths against the caller's working directory. MCP
and the advertised input schema require an absolute filesystem path and
preserve that exact selection:
```json
{
"name": "inspect_windows_shortcut",
"arguments": { "path": "/artifacts/shortcut.lnk" }
}
```
## Observations
The inline Evidence result contains the artifact path, SHA-256, and byte
length. `header` is the 76-byte `ShellLinkHeader`: `LinkFlags` and
`FileAttributes` as raw values plus documented names, creation/access/write
FILETIMEs as a raw 64-bit decimal and ISO-8601 UTC when the timestamp is in
range, file size, icon index, `ShowCommand` (`normal`, `maximized`, or
`minimized`), and hotkey modifiers. Undocumented `ShowCommand` values are
decoded as `normal`, as specified.
`id_list` is present when `HasLinkTargetIDList` is set. Each item keeps its
file offset, size, and type byte. Root-folder, volume, and file-entry items
are decoded when those layouts match documented shell-item headers, including
the 8.3 short name on a file entry. Every other item is `raw` with a SHA-256
of its payload. Unknown items are not guessed.
`link_info` reports volume drive type, serial, label, local base path, and
common network relative link names, with Unicode variants when the header
declares them. ANSI strings use a byte-preserving Latin1 projection; the
original system ANSI code page is unobserved.
`string_data` reports name, relative path, working directory, command-line
arguments, and icon location according to `LinkFlags`, each with encoding,
`CountCharacters`, byte length, and the decoded value. `IsUnicode` selects
UTF-16LE; otherwise the Latin1 projection is used.
`extra_data` enumerates every block: signature, size, and offset.
`EnvironmentVariableDataBlock` and `IconEnvironmentDataBlock` expose ANSI and
Unicode targets. `TrackerDataBlock` exposes the NetBIOS machine id and droid /
birth-droid GUIDs. Version-1 UUID MAC and timestamp values are labelled
`evidence_kind: "derived"`. `KnownFolderDataBlock` and `SpecialFolderDataBlock`
keep their folder identifiers. `ConsoleFEDataBlock` keeps the code page.
`PropertyStoreDataBlock` and unrecognized signatures stay raw with SHA-256.
Unknown signatures are listed and are not dropped.
`observations` are derived only from those documented fields: whether
arguments are present, argument character length, target extension, whether
`ShowCommand` is minimized, and whether the icon location differs from the
recorded target path. That comparison is `null` unless the icon and a recorded
target are both full paths: a stored filename, relative path, or `%VAR%` form
cannot show that two locations differ. This workflow does not emit verdicts
about intent.
A missing optional section is observed absence of that structure. It is not
proof the shortcut never carried that data in another container.
## Limits and failures
The selected file is limited to 1 MiB by default (16 MiB maximum). At most 256
IDList items (4096 maximum) and 64 ExtraData blocks (1024 maximum) are
examined by default. Retained string payload bytes across StringData,
LinkInfo, and ExtraData are limited to 64 KiB by default (1 MiB maximum).
Malformed `HeaderSize` or `LinkCLSID`, and exhausted budgets, fail without
partial success. Truncation after a valid header, ExtraData size overruns, and
odd Unicode lengths return partial coverage with `coverage.failing_offset`.
A LinkInfo offset that points outside its structure is reported at the
offset field itself, so every issue offset lies inside the file.
Coverage is never `complete` when issues are present.
The selected path must be a regular file. Symbolic links are rejected.
UNC paths and other host-prefixed device paths, such as `\\server\share\a.lnk`
and `\\?\UNC\server\share\a.lnk`, are rejected before any filesystem call. On
Windows, a path whose parent directory is a symbolic link or junction is
rejected before anything follows that link. The check covers the directories
Windows opens after removing `.` and `..`, so `C:\alias\..\real\a.lnk` is
checked at `C:\real` only; a `\\?\` path is checked as written. A mapped
network drive cannot be told apart from a local drive by its path and is not
rejected.
This workflow does not resolve, open, or execute the link target or call COM,
and it does not contact a network for a path on a local volume.
+2 -15
View File
@@ -97,18 +97,6 @@ process observations under a shared comparison contract. It reports observed
differences with their locations, while residual unknowns or truncated
observations prevent a complete-equivalence claim. Matching scenario
commitments do not reveal whether redacted sensitive inputs were equal.
The MCP tool accepts each complete capture inline or as an exact
`{"kind":"retained-evidence","evidence_id":"ev_..."}` reference to capture
Evidence already retained by the current session.
Capture-local observer timing remains in Evidence for provenance, but scheduler
and observation latency alone do not make behavior changed: terminal,
interaction-dispatch, settlement/finalization, process-sample, and filesystem
checkpoint times are excluded from the default semantic equality projection.
To make a timing value part of the question, supply a `trace_spec` whose exact
event includes that field. Semantic fields such as output, interaction outcome,
exit status, finalization delivery, process identity/command, and filesystem
contents still participate in the default comparison.
For scenarios where independent scheduling can change event order, the CLI
also accepts an optional trace-specification JSON file as the third argument.
@@ -520,9 +508,8 @@ a representability limit, not a timer limit. The nominal lifecycle budget is tha
sum; polling, snapshotting and cleanup lie outside it. Scenario `events` keep
being dispatched during finalization and are recorded as usual. `finalization_ms`
of `0`, the default, keeps today's behaviour and the committed identity of every
existing scenario. `elapsed_ms` and `sent_at_ms` are retained timing provenance
like `settlement.elapsed_ms`. The default semantic comparison excludes these
capture-local times; an exact trace event can assert them explicitly.
existing scenario. `elapsed_ms` and `sent_at_ms` are timing data and take part in
exit comparison like `settlement.elapsed_ms`.
Every capture requires `truncation_details`, with separate accounting for:
+1 -5
View File
@@ -357,11 +357,7 @@ entire property and its sidecars; a `scope: "property-name"` redaction points
to its actual parent. No substitute property name or child pointer is invented.
Omitted subtrees are still validated, including canonical HAR base64. Explicitly sensitive artifact
paths are empty in Evidence and omitted from locations, while the original
SHA-256 and size remain available unless the digest itself is explicitly excluded.
CLI [caller-selected paths](../../reference/mcp-contracts.md#caller-selected-paths)
are excluded independently: a sensitive capture spelling can be omitted while a
safe `descriptor_set_path` remains, and a safe relative spelling can remain even
when its resolved parent path is sensitive. Failure messages, diagnostics and cleanup
SHA-256 and size remain available unless the digest itself is explicitly excluded. Failure messages, diagnostics and cleanup
resources follow the same explicit declarations without changing error types;
sensitive issue pointers identify a real ancestor instead of a fabricated
coordinate. Header/cookie authentication values and known
-7
View File
@@ -24,13 +24,6 @@ This validates the embedded bundle, not the rest of the snapshot container.
## Configure
Doctor uses the selected client's effective registration environment. An
unresolved setting that affects the probe keeps readiness unknown rather than
falling back to the terminal account. Kuna's home-based cache fallback includes
`USERPROFILE` when `HOME` is absent; an explicit cache root or a selected nonempty
`HOME` makes that fallback irrelevant. These home settings are probe dependencies
and are not persisted as ambient provider registration settings.
```bash
export REA_KUNA_COMMAND=/absolute/path/to/kuna
rea doctor --provider kuna
+1 -1
View File
@@ -267,7 +267,7 @@ profile stages before retrying their retained descriptors.
| Function identity | Every function result carries the entry address and Ghidra FunctionManager classification for external, thunk, and resolved thunk target. These are observations, not proof that unresolved targetless calls have been recovered. |
| Assembly and pseudocode | Assembly is complete Ghidra Listing text; pseudocode is Ghidra decompiler output. Neither is original source, and cross-provider comparison never treats Hopper and Ghidra text as equal or unequal semantic facts. |
| Instruction fast path | `read_function_instructions` returns every raw Listing instruction for the requested function without invoking the decompiler or whole-program name/string inventories. |
| Calls and references | Callers/callees contain only resolved functions. Reference edges preserve exact ReferenceManager type and call/jump/data/read/write/indirect/computed/conditional/terminal/external facts. Targetless computed flow remains unknown. A computed call's recorded targets stay observed callees, but they are static candidates rather than an exhaustive set, so callee coverage is `partial` and names those call sites. Synthetic entry-point references without actionable memory sources are omitted explicitly. |
| Calls and references | Callers/callees contain only resolved functions. Reference edges preserve exact ReferenceManager type and call/jump/data/read/write/indirect/computed/conditional/terminal/external facts. Targetless computed flow remains unknown. Synthetic entry-point references without actionable memory sources are omitted explicitly. |
| CFG | Dossiers use `BasicBlockModel` and retain only non-call successors inside the function body. CFG topology is address-normalized for comparison; provider-specific block construction remains a declared difference. |
| P-code value flow | Function dossiers include up to 3,000 high-p-code operations, at most 64 inputs per operation, and 12,000 def-use edges. Results identify memory reads/writes, branches, and calls by p-code opcode, and mark decompiler-dead operations. Large results report truncation, an omitted-operation lower bound, and known omitted-input/edge counts. The dossier is an intra-function graph; `trace_native_values` composes derived parameter/argument and return/output dependencies across resolved calls. It does not resolve memory aliasing, call side effects, runtime behavior, persistent state, or business meaning. |
| Result extent | Function instruction scans and native API boundary observations remain complete. P-code flow is the explicit bounded exception; its count fields distinguish exact retained counts from lower-bound or known-omitted counts. Caller cancellation and provider failures remain distinct. |
+1 -1
View File
@@ -233,7 +233,7 @@ admits it explicitly. Raw-binary and GameCube DOL targets are refused; open
them with Ghidra. Admission is not verification: the real workflows under
[Verification](#verification) cover x86_64 and arm64 only.
All profiles support procedure/string inventory and literal or regex search, procedure resolution, pseudocode, assembly, raw function instructions, resolved callees, incoming address references, and a function dossier. Inventories and modern pseudocode/assembly follow upstream pagination. Upstream limits on modern xrefs and callees are retained as explicit truncation limitations. In the modern headless profile, targets that are not proven x86-only use processor-neutral `callgraph` edges instead of the x86 instruction checks in upstream `callees` (mrexodia/ida-pro-mcp#549). REA requests depth zero: the pinned upstream emits the root's outgoing edges without scanning child functions, then REA batches function-name lookup for those addresses. The graph covers recognized function destinations only; imports and other non-function targets remain unknown. A server without `callgraph` returns an empty relationship list with an explicit coverage limitation. Neither `callees` nor `callgraph` reports the call sites behind its edges, so a computed call's targets are never established as exhaustive; callee coverage is therefore `partial` even for an uncapped reply, and a call path that does not reach its goal remains `unknown`. Graph and path workflows retain those limitations alongside observed edges. A failed or inconsistent graph reply remains an error. A transport-level truncated preview is rejected rather than reported as complete evidence; the error preserves upstream retrieval guidance.
All profiles support procedure/string inventory and literal or regex search, procedure resolution, pseudocode, assembly, raw function instructions, resolved callees, incoming address references, and a function dossier. Inventories and modern pseudocode/assembly follow upstream pagination. Upstream limits on modern xrefs and callees are retained as explicit truncation limitations. In the modern headless profile, targets that are not proven x86-only use processor-neutral `callgraph` edges instead of the x86 instruction checks in upstream `callees` (mrexodia/ida-pro-mcp#549). REA requests depth zero: the pinned upstream emits the root's outgoing edges without scanning child functions, then REA batches function-name lookup for those addresses. The graph covers recognized function destinations only; imports and other non-function targets remain unknown. A server without `callgraph` returns an empty relationship list with an explicit coverage limitation. Graph and path workflows retain those limitations alongside observed edges. A failed or inconsistent graph reply remains an error. A transport-level truncated preview is rejected rather than reported as complete evidence; the error preserves upstream retrieval guidance.
All profiles expose `list_documents` as the single target admitted for the session. Attached mode verifies its digest against IDA metadata before returning it; headless mode binds the digest-checked private input. `inspect_address_context`, `trace_feature`, and `binary_overview` can therefore resolve that identity without upstream document-listing tools. Segment enumeration is optional: when the selected profile cannot provide it, `binary_overview` still returns document and procedure/string counts and omits `segments` and `segment_count`; omission means unobserved, not empty. The modern headless profile also supports `resolve_containing_procedure`. An address resolves through upstream `lookup_funcs` to the canonical entry of the function that contains it; the body stays unavailable like other IDA identities. When no function contains it, REA reads the database-scoped minimal `survey_binary` and reports `not_in_procedure` for an address inside one of its segments (ends are exclusive) and `outside_segments` otherwise. A missing, truncated or inconsistent survey fails the read instead of guessing a reason. `trace_feature` marks a trace truncated, with a residual unknown for that address, when upstream caps the references it returned. Attached profiles do not declare address containment resolution.
-1
View File
@@ -157,7 +157,6 @@ npx rea-agents@latest setup
| بنية ELF دون تشغيل | الأقسام والمقاطع والرموز ومعلومات إعادة التموضع الأصلية، وآليات الحماية المحتملة المستدلّ عليها بالفحص الثابت | pwntools يوفّره المستدعي على Linux x64؛ [تشخيص الملفات الثنائية](../guides/native/binary-diagnostics.md) |
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
| Windows shortcuts | Shell Link target, arguments, working dir, ExtraData and Tracker droid facts | Built-in static LNK inspection; [Windows shortcuts](../guides/native/windows-shortcut.md) |
| شيفرة EVM البايتية | محددات التوجيه، وإزاحات البايتات، والمعاملات المستنتجة، وقابلية تغيير الحالة | مدخل محلي يحمل بايتات خامًا أو تمثيلًا سداسيًا عشريًا على Linux x64؛ [دليل EVM دون تشغيل](../guides/evm/evm-bytecode.md) |
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
-1
View File
@@ -157,7 +157,6 @@ REA benötigt Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) oder 26+ (>=26.12.1) so
| Offline-ELF-Struktur | Sektionen, Segmente, ursprüngliche Symbole/Relokationen und mögliche Schutzmaßnahmen aus statischer Analyse | Vom Aufrufer bereitgestelltes pwntools unter Linux x64; [Binärdiagnostik](../guides/native/binary-diagnostics.md) |
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
| Windows shortcuts | Shell Link target, arguments, working dir, ExtraData and Tracker droid facts | Built-in static LNK inspection; [Windows shortcuts](../guides/native/windows-shortcut.md) |
| EVM-Bytecode | Dispatch-Selektoren, Byte-Offsets, abgeleitete Argumente und Veränderbarkeit des Zustands | Lokale Eingabe mit Rohbytes oder Hexdarstellung unter Linux x64; [Offline-EVM-Leitfaden](../guides/evm/evm-bytecode.md) |
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
-1
View File
@@ -159,7 +159,6 @@ npx rea-agents@latest setup
| Δυαδικά αρχεία Rust | Ενσωματωμένα ονόματα, εκδόσεις, πηγές και σχέσεις εξάρτησης των crates από το cargo-auditable | Ενσωματωμένη στατική εξέταση ELF/PE/thin Mach-O· [δυαδικά αρχεία Rust](../rust-binaries.md) |
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
| Windows shortcuts | Shell Link target, arguments, working dir, ExtraData and Tracker droid facts | Built-in static LNK inspection; [Windows shortcuts](../guides/native/windows-shortcut.md) |
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](../binary-hardening.md) |
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
-1
View File
@@ -157,7 +157,6 @@ REA requiere Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) o 26+ (>=26.12.1), adem
| Estructura ELF sin ejecutar | Secciones, segmentos, símbolos/reubicaciones originales y posibles medidas de mitigación detectadas mediante análisis estático | pwntools proporcionado por quien llama, en Linux x64; [diagnóstico de binarios](../guides/native/binary-diagnostics.md) |
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
| Windows shortcuts | Shell Link target, arguments, working dir, ExtraData and Tracker droid facts | Built-in static LNK inspection; [Windows shortcuts](../guides/native/windows-shortcut.md) |
| Bytecode EVM | Selectores de despacho, desplazamientos en bytes, argumentos inferidos y mutabilidad | Entrada local con bytes sin procesar o representación hexadecimal, en Linux x64; [guía de EVM sin ejecutar](../guides/evm/evm-bytecode.md) |
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
-1
View File
@@ -149,7 +149,6 @@ REA به Node.js 22.x (>=22.23.4)، 24.x (>=24.22.1) یا 26+ (>=26.12.1)، به
| ساختار ELF آفلاین | بخش‌ها، سگمنت‌ها، نمادها و relocationهای اصلی و گزینه‌های احتمالی کاهش آسیب‌پذیری ایستا | pwntools تأمین‌شده توسط فراخواننده روی Linux x64؛ [تشخیص باینری](../guides/native/binary-diagnostics.md) |
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
| Windows shortcuts | Shell Link target, arguments, working dir, ExtraData and Tracker droid facts | Built-in static LNK inspection; [Windows shortcuts](../guides/native/windows-shortcut.md) |
| بایت‌کد EVM | selectorهای dispatch، offsetهای بایتی، آرگومان‌های استنباط‌شده و قابلیت تغییر وضعیت | دادهٔ خام/hex محلی روی Linux x64؛ [راهنمای EVM آفلاین](../guides/evm/evm-bytecode.md) |
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
-1
View File
@@ -156,7 +156,6 @@ REA nécessite Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) ou 26+ (>=26.12.1), ai
| Structure ELF hors ligne | Sections, segments, symboles et relocations d'origine, et protections statiques candidates | pwntools fourni par l'utilisateur sous Linux x64 ; [diagnostics binaires](../guides/native/binary-diagnostics.md) |
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
| Windows shortcuts | Shell Link target, arguments, working dir, ExtraData and Tracker droid facts | Built-in static LNK inspection; [Windows shortcuts](../guides/native/windows-shortcut.md) |
| Artefact WASM | Validation, sections, formes WAT exactes d'import/export et WAT décodé | WABT 1.0.42 fourni par l'utilisateur ; `inspect_wasm_artifact` |
| Bytecode EVM | Sélecteurs de dispatch, offsets en octets, arguments inférés et mutabilité | Fichier local brut ou hexadécimal sous Linux x64 ; [guide EVM hors ligne](../guides/evm/evm-bytecode.md) |
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
-1
View File
@@ -159,7 +159,6 @@ Alat tambahan dan dukungan sistem host bergantung pada target yang dianalisis:
| Tata letak ELF offline | Bagian, segmen, simbol/relokasi asli, dan kandidat mitigasi statis | pwntools yang disediakan pemanggil pada Linux x64; [diagnostik biner](../guides/native/binary-diagnostics.md) |
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
| Windows shortcuts | Shell Link target, arguments, working dir, ExtraData and Tracker droid facts | Built-in static LNK inspection; [Windows shortcuts](../guides/native/windows-shortcut.md) |
| Artefak WASM | Validasi, bagian, bentuk WAT import/ekspor yang tepat, dan WAT yang didekode | WABT 1.0.42 yang disediakan pemanggil; `inspect_wasm_artifact` |
| Bytecode EVM | Selector dispatch, offset byte, argumen yang disimpulkan, dan sifat mutabilitas | Pembawa data raw/hex lokal di Linux x64; [panduan EVM offline](../guides/evm/evm-bytecode.md) |
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
-1
View File
@@ -156,7 +156,6 @@ REA richiede Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) o 26+ (>=26.12.1), oltre
| Layout ELF offline | Sezioni, segmenti, simboli/relocazioni originali e candidati mitigazioni statiche | pwntools fornito dal chiamante su Linux x64; [diagnostica binaria](../guides/native/binary-diagnostics.md) |
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
| Windows shortcuts | Shell Link target, arguments, working dir, ExtraData and Tracker droid facts | Built-in static LNK inspection; [Windows shortcuts](../guides/native/windows-shortcut.md) |
| Artefatto WASM | Convalida, sezioni, forme WAT di import/export esatte e WAT decodificato | WABT 1.0.42 fornito dal chiamante; `inspect_wasm_artifact` |
| Bytecode EVM | Selettori di dispatch, offset dei byte, argomenti inferiti e mutabilità | Carrier raw/hex locale su Linux x64; [guida EVM offline](../guides/evm/evm-bytecode.md) |
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
-1
View File
@@ -157,7 +157,6 @@ REA には Node.js 22.x(>=22.23.4)、24.x(>=24.22.1)、または 26+ (>=
| オフライン ELF レイアウト | セクション、セグメント、元のシンボル/再配置情報、静的解析によるセキュリティ対策の候補 | Linux x64 で利用者が用意する pwntools;[バイナリ診断](../guides/native/binary-diagnostics.md) |
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
| Windows shortcuts | Shell Link target, arguments, working dir, ExtraData and Tracker droid facts | Built-in static LNK inspection; [Windows shortcuts](../guides/native/windows-shortcut.md) |
| EVM バイトコード | ディスパッチセレクター、バイトオフセット、推定された引数と状態変更の可否 | Linux x64 上のローカルの生バイト/16 進数の入力ファイル;[オフライン EVM ガイド](../guides/evm/evm-bytecode.md) |
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
-1
View File
@@ -159,7 +159,6 @@ REA에는 Node.js 22.x(>=22.23.4), 24.x(>=24.22.1) 또는 26+ (>=26.12.1)와 npm
| Rust 바이너리 | cargo-auditable이 내장한 크레이트 이름, 버전, 소스, 의존성 관계 | REA 내장 ELF/PE/thin Mach-O 정적 검사; [Rust 바이너리](../rust-binaries.md) |
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
| Windows shortcuts | Shell Link target, arguments, working dir, ExtraData and Tracker droid facts | Built-in static LNK inspection; [Windows shortcuts](../guides/native/windows-shortcut.md) |
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](../binary-hardening.md) |
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
-1
View File
@@ -159,7 +159,6 @@ REA-യ്ക്ക് Node.js 22.x (>=22.23.4), 24.x (>=24.22.1), അല്ല
| Rust binaries | ഉൾച്ചേർത്ത cargo-auditable crate പേരുകൾ, versions, sources, dependency edges | Built-in static ELF/PE/thin Mach-O പരിശോധന; [Rust binaries](../rust-binaries.md) |
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
| Windows shortcuts | Shell Link target, arguments, working dir, ExtraData and Tracker droid facts | Built-in static LNK inspection; [Windows shortcuts](../guides/native/windows-shortcut.md) |
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](../binary-hardening.md) |
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
-1
View File
@@ -157,7 +157,6 @@ REA wymaga Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) lub 26+ (>=26.12.1) oraz n
| Układ pliku ELF w trybie offline | Sekcje, segmenty, oryginalne symbole/relokacje i potencjalne mechanizmy zabezpieczeń wskazane przez analizę statyczną | pwntools dostarczone przez użytkownika w systemie Linux x64; [diagnostyka plików binarnych](../guides/native/binary-diagnostics.md) |
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
| Windows shortcuts | Shell Link target, arguments, working dir, ExtraData and Tracker droid facts | Built-in static LNK inspection; [Windows shortcuts](../guides/native/windows-shortcut.md) |
| Kod bajtowy EVM | Selektory wywołań, przesunięcia bajtowe, wywnioskowane argumenty i mutowalność | Lokalny plik z surowymi bajtami lub zapisem szesnastkowym w systemie Linux x64; [przewodnik EVM offline](../guides/evm/evm-bytecode.md) |
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
-1
View File
@@ -157,7 +157,6 @@ O REA requer Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) ou 26+ (>=26.12.1), alé
| Estrutura ELF sem execução | Seções, segmentos, símbolos/relocações originais e possíveis mecanismos de mitigação identificados por análise estática | pwntools fornecido pelo chamador no Linux x64; [diagnóstico de binários](../guides/native/binary-diagnostics.md) |
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
| Windows shortcuts | Shell Link target, arguments, working dir, ExtraData and Tracker droid facts | Built-in static LNK inspection; [Windows shortcuts](../guides/native/windows-shortcut.md) |
| Bytecode EVM | Seletores de despacho, offsets de bytes, argumentos inferidos e mutabilidade | Entrada local com bytes brutos ou representação hexadecimal no Linux x64; [guia de EVM sem execução](../guides/evm/evm-bytecode.md) |
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
-1
View File
@@ -199,7 +199,6 @@ Uneltele suplimentare și platformele acceptate depind de ținta analizată:
| Fișiere binare Rust | Nume, versiuni, surse și relații de dependență ale pachetelor crate încorporate prin cargo-auditable | Inspectare statică integrată a fișierelor ELF/PE/Mach-O cu o singură arhitectură; [binare Rust](../rust-binaries.md) |
| Certificate PE | Intrări Authenticode WIN_CERTIFICATE și câmpuri de identitate ale semnatarilor PKCS#7 și certificatelor X.509 | Inspectare statică integrată a fișierelor PE; [certificate PE](../pe-certificates.md) |
| Importuri PE | Numele DLL-urilor importate și încărcate întârziat, ordinale, RVA-uri ale IAT și hash-ul importurilor calculat de pefile | Inspectare statică integrată a fișierelor PE; [importuri PE](../pe-imports.md) |
| Scurtături Windows | Țintă Shell Link, argumente, director de lucru, ExtraData și identificatori Tracker | Inspectare statică integrată a fișierelor LNK; [scurtături Windows](../guides/native/windows-shortcut.md) |
| Note ELF | Proprietari și tipuri PT_NOTE/SHT_NOTE, câmpuri GNU build-id și ABI-tag | Inspectare statică integrată a fișierelor ELF; [note ELF](../elf-notes.md) |
| Întărire binară | PIE, NX, RELRO, CFG și indicatori de antet pentru ELF, PE și Mach-O | Inspecție statică integrată a antetelor; [întărire binară](../binary-hardening.md) |
| Rapoarte Apple IPS de eroare fatală | Metadate .ips, fire de execuție, imagini binare, registre și clase documentate de erori fatale | Inspectare statică integrată a datelor JSON; [Apple IPS](../apple-ips.md) |
-1
View File
@@ -159,7 +159,6 @@ REA требует Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) или 26+ (>=26.1
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
| Windows shortcuts | Shell Link target, arguments, working dir, ExtraData and Tracker droid facts | Built-in static LNK inspection; [Windows shortcuts](../guides/native/windows-shortcut.md) |
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](../binary-hardening.md) |
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
-1
View File
@@ -157,7 +157,6 @@ REA ต้องใช้ Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) หรื
| โครงสร้าง ELF แบบออฟไลน์ | เซกชัน เซกเมนต์ สัญลักษณ์/รีโลเคชันต้นฉบับ และตัวเลือกกลไกป้องกันที่พบจากการวิเคราะห์แบบสถิต | pwntools ที่ผู้ใช้จัดเตรียมบน Linux x64; [การวินิจฉัยไบนารี](../guides/native/binary-diagnostics.md) |
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
| Windows shortcuts | Shell Link target, arguments, working dir, ExtraData and Tracker droid facts | Built-in static LNK inspection; [Windows shortcuts](../guides/native/windows-shortcut.md) |
| ไบต์โค้ด EVM | ตัวเลือกฟังก์ชันสำหรับการเรียก ออฟเซ็ตไบต์ อาร์กิวเมนต์และความสามารถในการเปลี่ยนสถานะที่อนุมานได้ | ไฟล์บนเครื่องที่มีไบต์ดิบหรือข้อมูลเลขฐานสิบหกบน Linux x64; [คู่มือ EVM แบบออฟไลน์](../guides/evm/evm-bytecode.md) |
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
-1
View File
@@ -157,7 +157,6 @@ REA; Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) veya 26+ (>=26.12.1) ve npm gere
| Rust ikili dosyaları | Gömülü cargo-auditable crate adları, sürümleri, kaynakları ve bağımlılık ilişkileri | Yerleşik statik ELF/PE/thin Mach-O incelemesi; [Rust ikili dosyaları](../rust-binaries.md) |
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
| Windows shortcuts | Shell Link target, arguments, working dir, ExtraData and Tracker droid facts | Built-in static LNK inspection; [Windows shortcuts](../guides/native/windows-shortcut.md) |
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](../binary-hardening.md) |
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
-1
View File
@@ -157,7 +157,6 @@ REA потребує Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) або 26+ (>=26
| Структура ELF без запуску | Секції, сегменти, початкові символи/релокації та можливі захисні механізми за даними статичного аналізу | pwntools, наданий стороною, що викликає інструмент, у Linux x64; [діагностика бінарних файлів](../guides/native/binary-diagnostics.md) |
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
| Windows shortcuts | Shell Link target, arguments, working dir, ExtraData and Tracker droid facts | Built-in static LNK inspection; [Windows shortcuts](../guides/native/windows-shortcut.md) |
| Байт-код EVM | Селектори диспетчеризації, зміщення байтів, виведені припущення про аргументи та змінюваність стану | Локальні необроблені байти або шістнадцяткове представлення у Linux x64; [посібник EVM без запуску](../guides/evm/evm-bytecode.md) |
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
-1
View File
@@ -199,7 +199,6 @@ Mazeed tools aur host support target par depend karte hain:
| Rust binaries | Embedded cargo-auditable crate names, versions, sources aur dependency edges | Built-in static ELF/PE/thin Mach-O inspection; [Rust binaries](../rust-binaries.md) |
| PE certificates | Authenticode WIN_CERTIFICATE entries aur PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
| PE imports | Import aur delay-load DLL names, ordinals, IAT RVAs aur pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
| Windows shortcuts | Shell Link target, arguments, working dir, ExtraData and Tracker droid facts | Built-in static LNK inspection; [Windows shortcuts](../guides/native/windows-shortcut.md) |
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id aur ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](../binary-hardening.md) |
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
-1
View File
@@ -159,7 +159,6 @@ REA yêu cầu Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) hoặc 26+ (>=26.12.1)
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
| Windows shortcuts | Shell Link target, arguments, working dir, ExtraData and Tracker droid facts | Built-in static LNK inspection; [Windows shortcuts](../guides/native/windows-shortcut.md) |
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](../binary-hardening.md) |
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
-1
View File
@@ -160,7 +160,6 @@ REA 需要 Node.js 22.x(>=22.23.4)、24.x(>=24.22.1)或 26+ (>=26.12.1)
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
| Windows shortcuts | Shell Link target, arguments, working dir, ExtraData and Tracker droid facts | Built-in static LNK inspection; [Windows shortcuts](../guides/native/windows-shortcut.md) |
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](../binary-hardening.md) |
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
-1
View File
@@ -160,7 +160,6 @@ REA 需要 Node.js 22.x(>=22.23.4)、24.x(>=24.22.1)或 26+ (>=26.12.1)
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
| Windows shortcuts | Shell Link target, arguments, working dir, ExtraData and Tracker droid facts | Built-in static LNK inspection; [Windows shortcuts](../guides/native/windows-shortcut.md) |
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](../binary-hardening.md) |
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
-5
View File
@@ -28,11 +28,6 @@ this effective scope. A claim verifier must commit to its own surfaces and
their reachable dependencies, including their artifact and implemented-owner
digests. Callers do not need to duplicate the transitive closure in the request.
Listed `residual_unknown_ids` select unknown identities from the Evidence bundle.
The evaluator uses each identity's latest revision: resolved questions do not
block the boundary, while reopened questions do. An active, unallowed question
retains its cited supporting, contradicting and mutation Evidence in the result.
## Comparable verifier Evidence
At least one record explicitly cited by the latest verifier observation must
+6 -64
View File
@@ -10,9 +10,6 @@ and [session discovery](#identity-and-discovery). See [transports](#transports)
for the opt-in HTTP listener, [cancellation](#progress-and-cancellation)
for partial-result recovery and [first-query deadlines](#ghidra-first-query-deadlines-and-recovery)
when a cold provider exceeds the client's wait.
The shared [caller-path](#caller-selected-paths) and
[publication](#evidence-publication-and-source-recovery) fields also apply to CLI
Evidence.
## Tool results
@@ -224,7 +221,7 @@ graph, while `search_scope.discovery` describes whether discovery of applicable
direct relationships is established. A missing goal is `unknown` when either
discovery or a relationship read is unestablished. A found path is shortest
within the observed graph; undiscovered calls may produce a shorter path.
`truncated` describes relationship reads, not unestablished discovery. Ghidra's recorded incoming references do not establish every computed caller; computed outgoing candidates likewise retain unknown callee discovery. IDA function-edge replies retain their observed truncation status while unclassified call targets remain unknown in discovery.
`truncated` describes relationship reads, not unestablished discovery.
`build_call_path` applies the same distinction to supplied function Evidence:
its `search_scope.exhaustive` describes the supplied graph and
`search_scope.observed_status` preserves that graph's result. Evidence without
@@ -522,23 +519,6 @@ that buffer. It does not raise REA's reserved single-string limit, which is
raise that Node ceiling. It does not change analysis coverage or retained
content. Ordinary responses keep their existing complete result contract.
### Caller-selected paths
Evidence can include `caller_input_path` for the verbatim primary selection and
`caller_input_paths` for named auxiliary selections. Each auxiliary value is a
string or an ordered array of strings; repeated selections preserve order and
duplicates. Consumers must accept both forms.
These fields record request provenance outside semantic Evidence identity.
They do not replace the inspected identity in `subject` or prove format validity.
One-shot binary CLI requests retain relative spelling and symlink aliases;
provider inspections also keep the established `parameters.input_path` field.
Older records and workflows without a caller-path binding omit these fields.
A workflow's explicit sensitive-value policy can exclude primary and auxiliary
spellings independently; do not infer a missing primary from an auxiliary value.
See [capture redaction](../guides/web/web-network-captures.md) for its policy.
### Input validation resource allowance
Both HTTP and stdio admit at most 1,024 combined array elements and object
@@ -1376,9 +1356,11 @@ uses `cleanup_incomplete` and lists only the owned resource kinds that remain.
Once those owners confirm cleanup, composed failures recover their original
typed primary outcome and recovery guidance. Historical composition diagnostics
remain in `details.cleanup_history`, separate from the primary diagnostics.
For written Evidence and snapshot outcomes, use the
[publication receipt](#evidence-publication-and-source-recovery) separately from
cleanup status.
An atomic Evidence bundle or snapshot write can publish its complete output
before staging cleanup fails. In that case, `details.published: true`,
`details.published_path`, and `details.bytes` retain the publication receipt.
Use that receipt to distinguish completed publication from a write that never
committed. Staging resources remain owned until cleanup is verified.
Native call tracing and process capture retain available observations in
`details.partial_observation` on failure, including when cleanup succeeds.
The observation reports its partial coverage; cleanup details describe host
@@ -1450,46 +1432,6 @@ through provider cleanup, including repeated delivery by package runners.
Existing controlled-process cleanup and provider shutdown rules still apply;
REA never kills a process it cannot prove it owns.
### Evidence publication and source recovery
An atomic Evidence bundle or snapshot write can commit before later cancellation
or staging cleanup failure. Cancellation observed before publication leaves the
destination unchanged; once publication has started, the owning operation can
complete it.
Inspect the returned receipt before retrying or replacing output:
| Outcome | Receipt |
| ----------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| Evidence or snapshot publication committed, then staging cleanup failed | `details.published: true`, `details.published_path`, and `details.bytes` |
| CLI Evidence delivery cancelled after its bundle committed | `details.diagnostics.evidence_bundle` with the written path and byte count |
Receipt paths can be omitted when unavailable or explicitly excluded. A missing
path alone does not establish that publication failed.
A publication failure can retain completed Evidence in
`details.partial_observation`. The receipt confirms written output, not that
cleanup succeeded or every analysis stage completed. Staging resources remain
owned until cleanup is verified. Other output kinds use their own contracts,
such as [environment directory publication](../getting-started/cli.md#export-an-environment-bundle)
and [decompile output receipts](../guides/native/native-investigation.md).
For a requested one-shot Evidence bundle that did not commit, the error can
include `details.source_closure` when completed Evidence has source records or
links, or source collection failed. `publication` is `not_published`.
When collection yields source facts, `records` contains captured Evidence within
64 KiB; explicit sensitivity exclusions can withhold all inline records.
`omitted` lists up to 64 other identities, and `omitted_count` gives the total:
- `not_captured`: this invocation has no record to return; its ancestry remains unknown.
- `not_inlined`: the record was captured but is outside this bounded response.
`complete` is true only when source inspection succeeded and every cited record
is included or embedded in the observation. On inspection failure,
`collection_failure` names the error type and closure stays unknown. Captured
records can still be returned, but record and omission fields can be absent if
collection itself failed. The original operation failure remains primary.
The one-shot process retains nothing after exit; rerunning is a new observation.
## Request scope and local effects
Each tool request names the target and lifecycle it will use. Browser calls
@@ -28,49 +28,23 @@ and the recording and writes the record as Evidence.
out of every walk. A start returns once the capture reports the window
open; a refused or early-ended start stops the capture and reports its
output.
- `start_build_capture` takes `state_paths`, at most 16 files or folders in
the home, relative to it, after `~/` or absolute, without `..` and not the
home itself; the controller resolves each under the computer's home, both
as named and with its links followed (a path not there yet through its
nearest existing folder), refuses one outside it or in `~/.rea-build`
either way before launching anything, and passes each as named to the
capture as `--state-path`. A selected file is a document, or the app's own
state under a hidden folder of the home as named, before links, or when it
is a lock the app holds, such as LibreOffice's `.~lock.<name>#`.
- Acknowledgement: `capture-desktop-application` reports, beside each progress
message, an `event`: `window_open`; `checkpoint` with its name, trigger,
time, entries walked, changes and retained files and bytes, and up to ten
changed files, home-relative, those under a selected path first, then
those the app held open, caches and browser-engine storage last, with a count
of the rest, once it is taken, the baseline, `stop` and `exit` included;
and `checkpoint_refused` with the reason. `mark_build_checkpoint` writes
`checkpoint <name>` and waits for the event naming it, returning its counts
and changed files, both `null` when the REA in the computer predates them
and its event leaves them out. Names follow a narrow pattern, without the capture's own
names; a name already taken is refused.
time, entries walked, changes and retained files and bytes, once it is
taken, the baseline, `stop` and `exit` included; and `checkpoint_refused`
with the reason. `mark_build_checkpoint` writes `checkpoint <name>` and
waits for the event naming it, returning its counts. Names follow a narrow
pattern, without the capture's own names; a name already taken is refused.
- `stop_build_capture` sends `stop`, waits for the exit status, reads the
capture's Evidence and, when `seed` and `end` were acknowledged, runs
`describe-desktop-application-task --seed-checkpoint seed --end-checkpoint
end` and returns the changed files, their net change and suggested checks.
A draft with no files carries `notes`, coded `no_change`,
`unowned_changes` (up to five changed home paths with their roles, and a
count of the rest), `uncheckable_changes` (dconf databases whose app keys
were read and changed, which no check reads, a version kept without its
bytes counting as holding none), `unreadable_changes` (dconf
databases that changed but could not be read, with the read `error`) and
`no_document`; a drafted file whose suggestions are thin carries one
naming its `file`, coded `by_position`, `view_state_only`, `long_text`,
`cut_view` or `unreadable_view`.
- `export_build_environment` writes the agent's task under `~/.rea-build`,
runs `export-desktop-application` with it and the same checkpoints into a
new folder there, reports a refused task with the export's own reason,
lists the bundle with sizes and SHA-256, fetches it through the driver's
`data/get` in answers of at most 8 MiB, checks each file and writes the
caller's new or empty folder through the safe output tree. The export
leaves lock files the app holds while it runs out of the seed, but never a
document or selected path, listing them in its limitations and README; a
start from a bundle whose computer never gets ready names the files its
seed holds by a profile lock's name or as a document lock.
caller's new or empty folder through the safe output tree.
- `check_build_task` reads the one app's `data/get` through the card's
child URL, takes the state views beside its files and evaluates the task's
checks with `evaluateTaskChecks`, one result per check. With `stage`, the
@@ -280,123 +280,6 @@ taken 18, 23 and 21 turns for $0.70, $1.55 and $0.51 ($0.92). Cache reads
fell from 585-714K tokens to 181-225K; the cost is mostly cache writes,
which varied from 17K to 189K tokens whatever the turns.
## Naming the app's state
Run on macOS with source tests and gates; no build computer was started.
Each test below failed on the head before this change, with only the tests
and their fixtures added, and passes now.
- `buildCaptureStatePathsMcp.test.ts`: `start_build_capture` with
`state_paths` passes the note's path to the capture as `--state-path`, the
draft covers it as a document with no notes, and an export with a check on
it is accepted; without it the draft is empty with `unowned_changes`
(the note, `unrelated`) and `no_document`, and the export's refusal names
`state_paths`. A path through `..`, the home itself and 17 paths are
refused by the schema, `/etc/hosts` by the computer, and none starts a
capture. Before, `state_paths` was an unknown argument.
- `DesktopTaskDraftNotes.test.ts`: a draft with files has no notes; one with
no change between seed and end has only `no_change`; one whose changes lie
outside the owned paths lists five with their roles, desktop files last,
and counts two more, beside `no_document`.
- `stateRoles.test.ts`: a selected file under a hidden folder of the home is
application state, elsewhere a document.
- `DesktopApplicationExport.test.ts`, `appExportUnowned.test.ts`: the export's
limitations and README name folders the capture saw change outside the
owned paths, collapsed to their top folder and without desktop caches, up
to eight and a count, and the seeded lock file, WebKit storage and cache
folders.
## Lock files, changed files and held-open folders
Run on macOS with source tests and gates; no build computer was started. A
hold-out LibreOffice Calc bundle seeded `~/.config/libreoffice/4/.lock` and
showed no window at boot; its export also said nothing in `~/Documents` was
seeded while `seed_files` held `Documents/q1-sales.ods`. Each test below
failed on the head before this change, with only the tests and their
fixtures added, and passes now.
- `DesktopApplicationExport.test.ts`: a capture whose seed holds a profile
`.lock` and a document lock `.~lock.today.txt#` exports without either in
`seed_files` or the bundle, and lists both after "Left out of the seed" in
one limitation and the README; with `~/Documents` held open, its
limitation names `~/Documents/today.txt` as seeded from there.
- `appExportUnowned.test.ts`: profile, browser and document locks are lock
files, and `*.lock` or `*.lck` only in a hidden folder, so `Cargo.lock`
and `yarn.lock` in a project are not; the capture's limitation names the
held-open folders from `~/`, and the export's names the seed files there
or says there are none.
- `desktopCapture.test.ts`: a checkpoint's acknowledgement names its changed
files home-relative, the held-open document first, then state, then the
rest, without folders, and names ten of thirteen with three more.
- `buildCaptureMcp.test.ts`: `mark_build_checkpoint` returns the changed
files the capture acknowledged.
- `buildComputerMcp.test.ts`: a bundle's start that never got ready names
the lock files its seed holds; one without locks, or failing otherwise,
names none.
The Linux-only capture test's acknowledgement now expects the changed state
file; it is skipped on macOS and was not run.
## Linked state paths, dconf changes, project lock files and older checkpoint replies
Run on macOS with source tests and gates; no build computer was started.
Each test below failed on the head before this change, with only the tests
and their fixtures added, and passes now.
- `buildCaptureStatePathsMcp.test.ts`: the computer refuses state paths that
a link leads out of the home, existing or not yet (through the nearest
existing folder, or a dangling link), or into `~/.rea-build`, and starts
nothing; a link that stays in the home is followed and the path passed to
the capture as named. Before, the first such path started a capture.
- `DesktopCaptureScope.test.ts`: the capture scope records each selected
path as named beside the physical path it walks.
- `DesktopApplicationExport.test.ts`: with `~/.config/MuseScore` selected as
a link to `~/settings/MuseScore` beside a selected note, the bundle's
launch opens the note. With only the capture's record of selected paths
added, the settings file was still a document and the launch opened none.
- `DesktopTaskDraftNotes.test.ts`: a draft whose only change between `seed`
and `end` is to the app's keys in dconf carries `uncheckable_changes`
naming `~/.config/dconf/user`, not `no_change`; one where only another
group's keys changed still has `no_change`.
- `appExportUnowned.test.ts`: `*.lock` and `*.lck` are locks only directly
in a state directory, so `Projects/.repo/Cargo.lock` is not; and 20,000
separate unowned folders collapse in tens of milliseconds, where comparing
every pair took 12.9 seconds.
- `DesktopApplicationExport.test.ts`: `Projects/.repo/Cargo.lock` and
`.dotfiles/web/yarn.lock`, which the capture recognized as documents, and
a lock-named file the caller selected are seeded, a `*.lock` directly in a
state directory is left out, and the profile and document locks still are.
- `buildCaptureMcp.test.ts`: checkpoints acknowledged without
`changed_files` and `more_changed_files`, as the previous REA in a build
computer does, are taken with both `null`, also when a lost reply is read
again from the capture's log. Before, the host refused the acknowledgement
although the checkpoint was taken.
- `desktopCapture.test.ts`: a checkpoint's changed files name a document
under a selected path first, though the app did not hold it open, and
caches and browser-engine storage last, even one the app held open or in a
state directory, so ten KDE service-cache files no longer push the saved
document past the cut, as they did in a held-out Tellico build.
- `DesktopApplicationExport.test.ts`: with `~/.config/MuseScore` selected as
a link to `~/Documents` and the note inside it selected itself, the
launch opens the note while the settings beside it stay the app's; before,
the link's hidden name made the note application state and the launch
opened none.
- `DesktopTaskDraftNotes.test.ts`: a dconf database that changed but could
not be read gives `unreadable_changes` with the reader's error, not
`uncheckable_changes`.
- `buildCaptureMcp.test.ts`: `stop_build_capture` on a capture whose only
change between `seed` and `end` is a dconf database the draft cannot read
(no `dconf` on its `PATH`) returns the `unreadable_changes` note with its
`error`; before, the build tool dropped the field.
- `LinuxApplicationInventory.test.ts`: the inventory lists KDE's
`~/.config/<name>rc` before it exists, so a Tellico capture owns
`~/.config/tellicorc`, whose absence brought back Tip of the Day at boot;
`DesktopApplicationExport.test.ts` shows such a file, written during the
capture, owned and seeded (that test passed before, given the inventory).
The capture's recording of selected paths runs only on Linux; its scope is
tested on macOS, and the capture itself was not run.
## Limits of this verification
- The witness acts were scripted by this agent from the task's prompt; no
@@ -55,9 +55,7 @@ and the bundle holds neither the capture host's name nor its user's.
path and content, escapes decoded, for the host name and `user@host`
anywhere and the captured home as a whole path, with the replacements set
aside. `scrubbed_files` lists the rewritten seeds and `unscrubbed_files`
every file found, by bundle path; the README lists both. gzip seeds
compressed again only to clear their headers, their documents unchanged,
are in `recompressed_files` instead, which the README counts.
every file found, by bundle path; the README lists both.
- Tasks: checks whose values name `user@host` or the host name by that rule
are refused, like those that name the captured home, but not for their
field names, kind or id; and the checks that must fail on the seed are
@@ -68,8 +68,7 @@ folder to build the environment; this step builds nothing.
lists candidate state directories under the XDG folders and dot-folders
named after the package, executable, desktop entry's full id and last part,
and name, and every folder present there whose name is one of those in
another letter case, such as GIMP's `~/.config/GIMP`, and KDE's
`<name>rc` in the config folder, such as `~/.config/tellicorc`; a `flatpak run`
another letter case, such as GIMP's `~/.config/GIMP`; a `flatpak run`
launch uses the application id (its first operand
after options and their values) instead of the launcher's name and adds
`~/.var/app/<id>`. An `--executable` runs from the caller's
@@ -79,9 +78,7 @@ folder to build the environment; this step builds nothing.
`--state-path` values and absolute arguments other than `/` or a directory
containing the home directory, which the limitations name instead, plus the
`-wal` and `-journal` paths beside each of those selected paths that is not
a directory, so a database selected on its own is captured with its logs.
Each selected path is also recorded as named, beside its physical
spelling, so a role can follow from the name the caller gave it. The
a directory, so a database selected on its own is captured with its logs. The
baseline walk runs before launch and retains every file under the content
roots; each later checkpoint reports created, modified, deleted and retyped
paths against the previous walk and retains every changed file. A creation
@@ -41,11 +41,7 @@ generation reads the kinds of change.
([XML text checks](../xml-text-checks/spec.md)); key files up to 4 MiB
whose every line parses; zip archives by
their local file header, as their entries with each entry's own format and
view ([document views](../document-views/spec.md)); text; binary. A gzip
file, told by its magic bytes, is read as the document inside, its members
joined, within a zip entry's 16 MiB and 100-times limits, and its version
records `compression: "gzip"` beside that document's format; past the
limits, or damaged, it has a `view_error`.
view ([document views](../document-views/spec.md)); text; binary.
- Changes between versions are created, deleted, or lines, values, keys or
rows with added, removed and changed lists (100 each, with counts); content
for binary files and unreadable when a view is missing. Values are JSON
@@ -61,6 +57,5 @@ generation reads the kinds of change.
- No export, seed selection or task generation.
- No parsing of other binary formats (GVDB other than dconf, LevelDB, Realm,
protobuf), or of compression other than gzip (xz, Zstandard, bzip2); they
are listed as binary.
protobuf); they are listed as binary.
- No redaction: views contain the user's real data, as the capture does.
@@ -21,14 +21,7 @@ never runs code the agent wrote.
both ends whose count changed); and a draft task with a placeholder prompt
and checks suggested from the net change of documents, or of application
state when no document changed. Views are shown only for those files, cut at
200 items and 2,000 characters. A draft with no files says why in `notes`:
`no_change`, `unowned_changes` with up to five changed home paths and their
roles, `uncheckable_changes` with the dconf databases whose application
keys were read and changed, `unreadable_changes` with the dconf databases
that changed but could not be read and the read `error`, and
`no_document`; a drafted file whose suggestions are thin gets one naming
its `file`: `by_position`, `view_state_only`, `long_text`, `cut_view` or
`unreadable_view`.
200 items and 2,000 characters.
- The task file is JSON with a portable `name` other than `smoke`, a `prompt`
and 1 to 32 `checks`, parsed by a zod schema in `src/domain/desktop/`. Check
kinds: `file_present`, `file_absent`, `lines_present`, `lines_absent`,
@@ -1,10 +0,0 @@
# Implementation plan
1. Derive VM callable identity and intact option objects from existing semantic
owners after mutation indexing; retain unresolved spreads as unknown.
2. Retain proven cache reads and their parsed bases through the named worker
contract. Resolve only source-directory paths against admitted inventory.
3. Project declarations into explicit graph unknowns and compilation summaries,
preserving source ranges, unresolved paths and execution limitations.
4. Verify negative mutation/spread controls and public filesystem workflows;
document runtime qualification separately from static analysis.
@@ -1,43 +0,0 @@
# V8 cached-data compilation boundaries
## Analyst question
Which source-visible Node VM compilation sites declare or may receive cached
bytes, and which admitted artifact can be associated with those bytes?
## Scope and authority
Static application analysis recognizes member calls to `vm.Script` and
`vm.compileFunction` when the shared built-in callable owner establishes their
identity. Analysis reads admitted source and inventory only. It does not invoke
the target, validate V8 cache bytes, establish cache acceptance, or execute code.
Node separates compilation, cache rejection and subsequent execution; see the
[Node VM contract](https://nodejs.org/docs/latest-v24.x/api/vm.html).
## Owning facts
- Resolve complete callable identity after shared mutation facts settle. ESM
default/namespace imports and proven CommonJS bindings use the same owner.
Replaced methods do not establish VM authority.
- `cached_data_status` is `declared` for an explicit cachedData property,
`absent` for proven omission or a proven undefined cache value, and `unknown` for unresolved options or argument
spreads that can supply or shift the options position. Declaration does not
prove a valid runtime value or successful cache use.
- A proven `fs.readFileSync` or awaited `fs/promises.readFile` can identify a
cache path. Unawaited promises and unproven readers retain bounded source
expressions. A supplied string alone is not proof that a file was read.
- Preserve the parsed path base through the worker boundary. Only a proven
source-directory base permits an application inventory lookup. Relative
literal paths depend on the unknown target working directory; source paths
and unresolved expressions stay separate from resolved artifact identity.
- Graph cache boundaries remain explicit unknowns with source provenance.
Absent sites do not add cache-boundary uncertainty. Summary counts describe
declared compilations, never observed executions.
## Compatibility and verification
Existing source declarations and inventory remain available. Optional path-base
facts may be absent in historical analyses; omission does not prove a base.
Public application workflows verify worker serialization, summary and graph
projection with local files. No test that merely supplies cache-shaped bytes
establishes real V8 acceptance or target execution.
@@ -1,10 +0,0 @@
# Tasks
- [x] Model declared, absent and unknown cached-data sites.
- [x] Reuse built-in callable and option-object ownership.
- [x] Retain synchronous and awaited promise read provenance and path bases.
- [x] Carry facts through worker serialization to summary and graph consumers.
- [x] Exercise malformed authority, argument spreads and unresolved paths.
- [x] Verify public filesystem application workflows and document limitations.
Real V8 acceptance and execution remain outside this static feature's scope.
@@ -1,38 +0,0 @@
# Implementation plan
## Boundary and contract
Follow the existing binary-diagnostics inspectors. `parseWindowsShortcut` in
`src/native/windows/WindowsShortcutParser.ts` owns Shell Link bytes and
coverage; `src/domain/native/windowsShortcut.ts` owns the result schema.
`WindowsShortcutProvider` (`rea-windows-shortcut`, built in) owns path
validation and the stable regular-file read through `readStableArtifact` and
`ArtifactResourceScope`. `WindowsShortcutService` owns Evidence provenance with
`subject.format: "file"`. The CLI command and MCP tool consume that Evidence
and add no interpretation.
ANSI strings use a byte-preserving Latin1 projection because the original
system code page is not observable from the file. `IsUnicode` selects
UTF-16LE. Missing optional sections are reported as observed absence only.
## Incremental work and validation
1. Define the contract, limits and observations; add failing parser fixtures.
2. Implement the parser for the header, IDList, LinkInfo, StringData and
ExtraData with budgets and partial coverage.
3. Add the provider, service, composition, capability declaration and tool
effects; cover cancellation, symlink rejection and finalization cleanup.
4. Add the CLI command and MCP tool; cover acceptance and advertisement.
5. Update the guide, CLI page, README rows and the authored skill; run focused
tests, repository checks and the documentation route build.
## Effects and compatibility
The workflow reads one caller-selected local file. It adds no target execution,
injection, mutation, protection recovery or network access, so no maintainer
scope decision is required. Existing tools and `inspect_artifact` routing are
unchanged.
## References
- https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-shllink/
@@ -1,51 +0,0 @@
# Windows Shell Link shortcut inspection
## Problem and outcome
REA has no Shell Link reader. An analyst holding a local `.lnk` file cannot ask
which target, arguments, working directory, icon location, volume or network
names, and ExtraData blocks it records without resolving the target, calling
COM or using a Windows host. Add one static inspector that reads a selected
local file and returns those facts as inline Evidence.
## Requirements
- Add `inspect_windows_shortcut` (MCP) and `inspect-windows-shortcut` (CLI).
MCP requires an absolute path; the CLI resolves a relative path against the
caller's working directory.
- Accept only a regular file. Reject symbolic links, UNC and other
host-prefixed device paths before any filesystem call, and on Windows a path
whose parent is a symbolic link or junction.
- Validate the 76-byte `ShellLinkHeader` (`HeaderSize` `0x4C` and the Shell
Link CLSID) and decode `LinkFlags`, `FileAttributes`, FILETIMEs, file size,
icon index, `ShowCommand` and hotkey.
- Decode `LinkTargetIDList` items whose layouts match documented shell-item
headers; keep every other item raw with a SHA-256 of its payload.
- Decode `LinkInfo` volume and network names, `StringData` per `LinkFlags`, and
enumerate every ExtraData block. Unknown signatures stay raw with SHA-256.
Tracker version-1 MAC and timestamp values are labelled derived.
- Emit only documented-field observations: argument presence and length,
target extension, minimized `ShowCommand`, and whether the icon location
differs from a recorded full target path (`null` when not comparable).
- Bound file size, IDList items, ExtraData blocks and retained string bytes,
with caller-adjustable limits and documented maximums. Honor cancellation.
- Never open, resolve or execute the link target, call COM or contact a network
for a local-volume path.
## Acceptance
Synthesized fixtures cover a hidden-window PowerShell shortcut with an icon
mismatch, network-share `LinkInfo`, Tracker version 1, ANSI strings, truncated
`StringData`, an unknown ExtraData signature and a wrong CLSID. Malformed
headers and exhausted budgets fail without partial success; truncation after a
valid header returns partial coverage with `coverage.failing_offset`, and
coverage is never `complete` when issues are present. CLI and stdio MCP
acceptance, MCP advertisement, filesystem symlink, budget and cancellation
boundaries, stable-artifact consumers and native acquisition finalization are
covered.
## Out of scope
Resolving or launching the target, COM `IShellLink` calls, Windows SDK use,
recovering the original system ANSI code page, verdicts about intent, and
routing `.lnk` files through `inspect_artifact`.
@@ -1,11 +0,0 @@
# Tasks
- [x] Define the single-feature scope, contract, limits and plan.
- [x] Parse the header, IDList, LinkInfo, StringData and ExtraData with
synthesized fixtures, budgets and partial coverage.
- [x] Add the provider, service and Evidence provenance; verify symlink, UNC,
budget, cancellation and finalization boundaries.
- [x] Add the CLI command and MCP tool; verify acceptance and advertisement.
- [x] Document the workflow in the native Windows shortcut guide.
- [ ] Verify against real Explorer-written `.lnk` files and non-Latin1 system
ANSI code pages.
@@ -1,40 +0,0 @@
# Plan
## Shape
The container format is the only thing this feature knows, so the parser owns it and
everything else consumes its facts.
- `src/domain/v8/v8CodeCache.ts` holds the schema, the bit constants and the bounds, so a
reader and a test share one definition of what a container is.
- `src/v8/V8CodeCacheParser.ts` decodes a `Buffer` into retained strings and producer
facts. It is given bytes only; it never opens a file.
- `src/v8/V8CodeCacheProvider.ts` owns the observation budget, the file read and the
remediation text. It is the boundary that turns bytes into a reported fact.
- `src/cli/v8CodeCacheCommands.ts` and the MCP registration are thin adapters over the
provider, so both surfaces report the same facts.
## Measured premises
Each of these was measured on Node 24.22.1 before the parser was written, because the
container is undocumented and the layout had to be derived rather than assumed:
- `vm.Script.createCachedData()` writes `0x…|length` for its producer word.
- `vm.compileFunction` sets `0x20000000`, and `vm.SourceTextModule` sets `0x40000000`.
- The payload retains UTF-8 strings above the threshold, and the retained set grows with
the source's literals rather than with the bytecode size.
## Order
1. Pin the format in the domain schema so later steps cannot disagree about it.
2. Write the parser against the measured producer words.
3. Add the provider, the budget and its remediation.
4. Expose the CLI command, then the MCP tool, over the same provider.
5. Document the command and the report, and link the guide from both indexes.
## Budget
The retained-string scan is the only unbounded part of the read, so it is bounded on three
axes at once: a maximum string count, a minimum string length, and a maximum artifact size.
The provider reports which of the three was reached and whether raising or lowering it is
the caller's next step, so a refusal is actionable rather than a bare truncation.
@@ -1,58 +0,0 @@
# Inspect a V8 code cache container
## Outcome
A bytenode `.jsc`, the bytes `vm.Script.createCachedData()` writes, and a code cache
carried inside a Node single-executable application or an Electron application are all
opaque today: the analysis reports the containing file and nothing about the cache. An
analyst has to install bytenode, guess a V8 version, and run the file to learn anything,
which executes attacker-controlled code and changes the state under study.
`rea inspect-v8-code-cache <path> [--minStringBytes <n>]` reads one explicitly selected
container as static bytes and reports its producer, the strings it retains above a
threshold, and the recoverable source metadata, without executing the file, starting a
V8 runtime, installing bytenode, or invoking a decompiler. The same facts are available
through the `inspect_v8_code_cache` MCP tool.
## Requirements
- The reader accepts a file whose first words describe a V8 code cache header; anything
else is refused before the payload is walked, so an unrelated file cannot be read as a
cache by accident.
- The producer is decided from the header's producer bits, not from the file extension:
a module bit means `vm.SourceTextModule`, a compile-function bit means
`vm.compileFunction`, and neither bit means `unknown`. The unflagged case is not
`vm.Script` by inference — whether V8 marks a function cache is a build detail. A
`vm.compileFunction` cache reads `0x20000004` for a 4-unit source on Node 24.22.1 and
`0x00000004` on Node 22.23.4, so an unflagged word could be either producer. The
observed word stays in `header.fields` for a caller that trusts a specific build
fingerprint.
- Retained strings are reported above the caller's byte threshold, with
`--minStringBytes` bounded so a pathological threshold cannot make the read unbounded.
- Observation is bounded by a budget that reports which bound it hit and how to raise it,
rather than silently truncating.
- The container's file location is reported as given, and the payload is never written
back or repaired.
## Non-goals
- Decoding the payload back into source. The container's bytecode is version-specific and
the tool does not attempt to reconstruct source from it.
- Executing the container to obtain runtime facts.
- Recognising an encrypted or compressed wrapper around a cache.
## Verification
- The `vm.Script` producer word and its retained strings come from output this suite
generates through `vm.Script.createCachedData()`, and the `vm.compileFunction` case
compiles through `vm.compileFunction` for the same reason.
- The `vm.SourceTextModule` producer bit is checked by setting the measured bit on a real
`vm.Script` cache rather than by compiling through `vm.SourceTextModule`, so that case
covers the bit's meaning and not a cache from that producer.
- The CLI, MCP, absolute-path and decimal-argument boundary contracts have their own
tests.
- The budget tests call `observationBudgetRecovery` and assert its structured facts — the
boundary reached and which knob may move. `observationBudgetRemediation` renders that
struct into a sentence, so it is held to the struct rather than to its wording: the
sentence must not advise a knob the struct closed, and must mention a smaller artifact
exactly when the struct requires one. Rewording stays free; wrong advice does not.
@@ -1,14 +0,0 @@
# Tasks
- [x] Record the outcome, requirements and non-goals.
- [x] Measure the producer words and the retained-string layout on Node 24.22.1.
- [x] Add the domain schema, the bit constants and the observation bounds.
- [x] Add the parser, reading the payload by index into the integer table.
- [x] Add the provider with the budget and its remediation.
- [x] Add the CLI command and register the MCP tool over the same provider.
- [x] Document the command, the report and the bounds.
- [x] Link the guide from `docs/README.md` and the VitePress index.
- [x] Add parser, CLI, MCP, absolute-path and decimal-argument tests.
- [x] Hold the budget advice to the recovery struct rather than to its wording.
- [x] Run the static, formatting, duplication, documentation and test gates.
- [x] Address the independent review.
+7 -7
View File
@@ -127,13 +127,13 @@ its declaration, so the same bytes moved to another device memory are
Root `format_metadata` (`dfuse`) records:
| Field | Meaning |
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| `image_size`, `target_count` | `DFUImageSize` and `bTargets` from the prefix |
| `element_count`, `empty_element_count`, `data_bytes` | All elements, elements of 0 bytes (which have no entry), and element data bytes |
| `overlapping_element_count` | Elements that overlap an earlier element in address order within the same alternate setting |
| `device_version`, `product_id`, `vendor_id` | `bcdDevice`, `idProduct` and `idVendor` from the suffix |
| `suffix_crc`, `computed_crc` | The stored `dwCRC`, and the same CRC computed over every byte before it |
| Field | Meaning |
| ---------------------------------------------------- | ------------------------------------------------------------------------------- |
| `image_size`, `target_count` | `DFUImageSize` and `bTargets` from the prefix |
| `element_count`, `empty_element_count`, `data_bytes` | All elements, elements of 0 bytes (which have no entry), and element data bytes |
| `overlapping_element_count` | Elements that overlap an earlier element of the same alternate setting |
| `device_version`, `product_id`, `vendor_id` | `bcdDevice`, `idProduct` and `idVendor` from the suffix |
| `suffix_crc`, `computed_crc` | The stored `dwCRC`, and the same CRC computed over every byte before it |
Recognition already requires `bVersion` 1 and `bcdDFU` `0x011A`, so they are
not repeated. The DFU suffix CRC is CRC-32 without the final inversion, as
-4
View File
@@ -182,9 +182,6 @@ const PATH_SCOPES = {
// Four-host installed-package matrix and the Windows curated lane.
package: [
...WINDOWS_EXECUTABLE_SELECTION_INPUTS,
// Selected-file spelling requires the real Windows identity workflow.
"src/application/binaryDiagnostics/ByteEntropyService.ts",
"tests/acceptance/byteEntropyWindowsIdentity.test.ts",
// Both host compilers must be exercised when their installation, options,
// or entrypoints change; Linux's accelerated checker cannot stand in for Go.
"scripts/run-typescript.*",
@@ -633,7 +630,6 @@ const PATH_SCOPES = {
"src/ghidra/GhidraReleasePolicy.ts",
"scripts/verify-ghidra-bridge-sources.mjs",
"tests/conformance/ghidra/ReaArmContextProbe.java",
"tests/conformance/ghidra/ReaComputedCallCoverageProbe.java",
"tests/conformance/ghidra/ReaDecodeProbe.java",
".github/workflows/ghidra-bridge-sources.yml",
],
+5 -4
View File
@@ -487,8 +487,10 @@ const listenerDataDescriptor = (listener) => {
return descriptor.get === undefined ? undefined : null;
};
// Only a directly owned wrapper proves dispatch through the hook. A decorator's
// .listener metadata can name one without calling it; preserve that uncertainty.
// A decorator that wraps once's hook wrapper and names it through `.listener`
// already records through that wrapper. Registering the decorator as is keeps
// native self-removal working: the once wrapper removes itself by identity, and
// removeListener matches the decorator through its `.listener`.
const classifyHookListenerWrapper = (listener) => {
const visited = new SetConstructor();
let candidate = listener;
@@ -497,8 +499,7 @@ const classifyHookListenerWrapper = (listener) => {
hops <= MAX_LISTENER_CHAIN_HOPS && typeof candidate === "function";
hops += 1
) {
if (weakMapHas(hookListenerRemovalTargets, candidate))
return candidate === listener ? "owned" : "unknown";
if (weakMapHas(hookListenerRemovalTargets, candidate)) return "owned";
if (setHas(visited, candidate)) return "unowned";
setAdd(visited, candidate);
const descriptor = listenerDataDescriptor(candidate);
+1 -1
View File
@@ -19,7 +19,7 @@ export const SOURCE_TEST_PROJECTS = [
group: "source",
include: [
"src/*.test.ts",
"src/{android,apkSignature,apktool,jeb,adb,artifacts,browser,config,cutter,desktop,dotnet,electron,erlang,evm,firmware,flutter,frida,pcap,ghidra,go,gpu,hopper,ida,inspector,javascript,main,native,process,providers,python,reference,rust,server,sqlite,v8,wasm,windows}/**/*.test.ts",
"src/{android,apkSignature,apktool,jeb,adb,artifacts,browser,config,cutter,desktop,dotnet,electron,erlang,evm,firmware,flutter,frida,pcap,ghidra,go,gpu,hopper,ida,inspector,javascript,main,native,process,providers,python,reference,rust,server,sqlite,wasm,windows}/**/*.test.ts",
],
},
{
+3 -9
View File
@@ -9,9 +9,8 @@ import { tmpdir } from "node:os";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { npmInvocation } from "./lib/npm-invocation.mjs";
const root = join(dirname(fileURLToPath(import.meta.url)), "..");
const npm = process.platform === "win32" ? "npm.cmd" : "npm";
const run = (command, args, cwd) => {
const result = spawnSync(command, args, { cwd, stdio: "inherit" });
@@ -32,8 +31,7 @@ if (destination === undefined || !destination.endsWith(".tar.xz")) {
const archive = resolve(destination);
const work = await mkdtemp(join(tmpdir(), "rea-build-runtime-"));
try {
const pack = npmInvocation(["pack", "--pack-destination", work], process.env);
run(pack.command, pack.arguments, root);
run(npm, ["pack", "--pack-destination", work], root);
const [packed] = (await readdir(work)).filter((name) =>
name.endsWith(".tgz"),
);
@@ -45,11 +43,7 @@ try {
join(root, "package-lock.json"),
join(runtime, "package-lock.json"),
);
const install = npmInvocation(
["ci", "--omit=dev", "--omit=optional"],
process.env,
);
run(install.command, install.arguments, runtime);
run(npm, ["ci", "--omit=dev", "--omit=optional"], runtime);
run("tar", ["-cJf", archive, "-C", work, "rea-prod"], work);
const { size } = await stat(archive);
process.stdout.write(
+1 -20
View File
@@ -2333,26 +2333,7 @@ export class AdbProvider {
{ timeoutMs: PULL_TIMEOUT_MS, maxOutputBytes: PULL_MAX_OUTPUT_BYTES },
signal,
);
if (!pulled.ok) {
// A failed adb process can leave caller-visible bytes at the selected
// destination. Report only an observed regular file; its transfer and
// digest are unconfirmed, and the caller owns the output.
const output = await lstat(localPath).catch(() => undefined);
if (output?.isFile() === true && !output.isSymbolicLink())
pulled.error.retainPartialObservation({
provider_id: "adb",
operation,
result: {
client,
serial: input.serial,
local_path: localPath,
device_path: input.device_path,
digest_status: "unknown",
transfer_status: "unconfirmed",
},
});
return pulled;
}
if (!pulled.ok) return pulled;
try {
const digest = await hashAdbFile(
localPath,
-10
View File
@@ -15,16 +15,6 @@ import {
} from "./JadxConfiguration.js";
import { JADX_JAR_CONFIGURATION_REMEDIATION } from "./JadxRelease.js";
/** Client settings consulted by this readiness probe. */
export const JADX_DOCTOR_SETTINGS = [
"REA_JADX_MCP_JAR",
"JAVA_HOME",
"PATH",
"REA_JADX_HEAP_MIB",
"REA_JADX_ACTIVE_PROCESSOR_COUNT",
"REA_JADX_OPERATION_TIMEOUT_MS",
] as const;
/** Project the validated JADX configuration into the doctor contract. */
export const inspectJadxProvider = async (
environment: Readonly<Record<string, string | undefined>>,
+3 -6
View File
@@ -37,10 +37,8 @@ export type SourceClosureOmission = {
/** Bounded failure handoff for a completed observation whose bundle never committed. */
export type UndeliveredSourceClosure = {
readonly publication: "not_published";
/** The embedded source list was read and every cited record is in `records` or the observation. */
/** Every cited record is in `records` or embedded in the observation itself. */
readonly complete: boolean;
/** The embedded producer source list could not be read, so closure remains unknown. */
readonly collection_failure?: string;
readonly records: Evidence[];
readonly omitted_count: number;
readonly omitted: SourceClosureOmission[];
@@ -104,11 +102,10 @@ const collectSourceClosure = (
records.push(record);
inlineBytes -= bytes;
}
if (closure.size === 1 && inspection.ok) return undefined;
if (closure.size === 1) return undefined;
return {
publication: "not_published",
complete: inspection.ok && omittedCount === 0,
...(inspection.ok ? {} : { collection_failure: inspection.error.name }),
complete: omittedCount === 0,
records,
omitted_count: omittedCount,
omitted,
+1 -6
View File
@@ -543,7 +543,6 @@ const workflowAvailabilityFor = (
name === "inspect_python_pyc" ||
name === "inspect_apple_ips" ||
name === "inspect_erlang_beam" ||
name === "inspect_v8_code_cache" ||
name === "inspect_electron_fuses"
)
return { reason: "available", remediation: null };
@@ -622,11 +621,7 @@ const workflowAvailabilityFor = (
};
if (name === "inspect_pe_resources" || name === "inspect_native_exports")
return { reason: "available", remediation: null };
if (
name === "inspect_pe_certificates" ||
name === "inspect_pe_imports" ||
name === "inspect_windows_shortcut"
)
if (name === "inspect_pe_certificates" || name === "inspect_pe_imports")
return { reason: "available", remediation: null };
if (
name === "inspect_elf_notes" ||
@@ -128,7 +128,6 @@ export const readClientConfigurationFiles = async (
if (document.value !== undefined) {
const environment = clientRegistrationEnvironment(
document.value,
client.name,
path === client.configPath ? writeOverrides : undefined,
"fragment",
platform,
@@ -147,7 +146,6 @@ export const readClientConfigurationFiles = async (
}
const environment = clientRegistrationEnvironment(
effectiveClientConfiguration(documents, undefined, platform),
client.name,
writeOverrides,
"effective",
platform,
@@ -59,45 +59,15 @@ const environmentSchema = clientConfigurationRecordSchema.transform(
},
);
/**
* Clients that expand `$VAR` and `${VAR}` in a recorded `env` value from the
* environment the client itself runs with.
*
* REA does not replicate that expansion — it would have to predict a child's
* environment — but it must not report the literal text as the value either, or
* a consumer that searches for an inherited `PATH` searches for a literal
* `${PATH}` entry instead.
*/
const VAR_EXPANDING_CLIENTS: ReadonlySet<string> = new Set(["qwen_code"]);
/** Recorded setting names whose value is an expression, not a literal. */
const expressionSettings = (
clientName: string | undefined,
values: Readonly<Record<string, string>>,
): ReadonlySet<string> =>
clientName !== undefined && VAR_EXPANDING_CLIENTS.has(clientName)
? new Set(
Object.entries(values)
.filter(([, value]) =>
/\$(?:[A-Za-z_][A-Za-z0-9_]*|\{[A-Za-z_][A-Za-z0-9_]*\})/u.test(
value,
),
)
.map(([name]) => name),
)
: new Set();
/** Read explicit REA overrides without dropping peers when one value is invalid. */
export const clientRegistrationEnvironment = (
parsed: ClientConfigurationDocument | undefined,
clientName: string | undefined,
overrides: Readonly<Record<string, string>> = {},
validation: "fragment" | "effective" = "effective",
platform: NodeJS.Platform = process.platform,
): Result<Readonly<Record<string, string>>, ConfigurationError> => {
const details = clientRegistrationEnvironmentDetails(
parsed,
clientName,
overrides,
validation,
platform,
@@ -108,7 +78,6 @@ export const clientRegistrationEnvironment = (
/** Retain original strings and the parser-owned timeout facts. */
export const clientRegistrationEnvironmentDetails = (
parsed: ClientConfigurationDocument | undefined,
clientName: string | undefined,
overrides: Readonly<Record<string, string>> = {},
validation: "fragment" | "effective" = "effective",
platform: NodeJS.Platform = process.platform,
@@ -155,12 +124,7 @@ export const clientRegistrationEnvironmentDetails = (
overrides,
platform,
);
return validatedDetails(
environment,
validation,
platform,
expressionSettings(clientName, environment),
);
return validatedDetails(environment, validation, platform);
};
/** Setting names a registration does not carry as the value selected here. */
@@ -181,7 +145,6 @@ export interface RecordedProviderSettings {
*/
export const recordedProviderSettings = (
parsed: ClientConfigurationDocument,
clientName: string | undefined,
selected: Readonly<Record<string, string>>,
platform: NodeJS.Platform = process.platform,
): RecordedProviderSettings | undefined => {
@@ -189,7 +152,6 @@ export const recordedProviderSettings = (
if (names.length === 0) return undefined;
const details = clientRegistrationEnvironmentDetails(
parsed,
clientName,
{},
"fragment",
platform,
@@ -209,9 +171,7 @@ export const recordedProviderSettings = (
),
differing: names.filter(
(name) =>
expressions[name] === undefined &&
recorded[name] !== undefined &&
recorded[name] !== selected[name],
recorded[name] !== undefined && recorded[name] !== selected[name],
),
unresolved: names.filter((name) => expressions[name] !== undefined),
};
@@ -226,7 +186,6 @@ export const recordedProviderSettings = (
/** Resolve the async provider-specific budget through its retained reader owner. */
export const clientRegistrationEnvironmentDetailsWithBudget = async (
parsed: ClientConfigurationDocument | undefined,
clientName: string | undefined,
overrides: Readonly<Record<string, string>>,
validation: "fragment" | "effective",
resolveProviderRequestBudget: ProviderRequestBudgetResolver | undefined,
@@ -236,7 +195,6 @@ export const clientRegistrationEnvironmentDetailsWithBudget = async (
> => {
const details = clientRegistrationEnvironmentDetails(
parsed,
clientName,
overrides,
validation,
platform,
@@ -272,23 +230,14 @@ const validatedDetails = (
environment: Readonly<Record<string, string>>,
validation: "fragment" | "effective",
platform: NodeJS.Platform = process.platform,
expressionSettings: ReadonlySet<string> = new Set(),
): Result<ClientRegistrationEnvironmentDetails, ConfigurationError> => {
const runtimeEnvironment = snapshotEnvironment(environment, platform);
const unresolved = new Set(
Object.keys(
snapshotEnvironment(
Object.fromEntries([...expressionSettings].map((name) => [name, ""])),
platform,
),
),
);
const validated =
validation === "fragment"
? parseEnvironmentValues(runtimeEnvironment, unresolved)
: parseEnvironment(runtimeEnvironment, unresolved);
? parseEnvironmentValues(runtimeEnvironment)
: parseEnvironment(runtimeEnvironment);
if (!validated.ok) return validated;
return ok(environmentDetails(environment, validated.value, unresolved));
return ok(environmentDetails(environment, validated.value));
};
const environmentDetails = (
@@ -283,7 +283,6 @@ export const readClientRegistrationInspections = async (
compactInputSchemaByDefault(client) || client.name === "codex"
? await clientRegistrationEnvironmentDetailsWithBudget(
parsed,
client.name,
{},
"effective",
client.name === "kimi" || client.name === "codex"
@@ -380,7 +379,6 @@ export const readClientRegistrationInspections = async (
? undefined
: recordedProviderSettings(
parsed,
client.name,
options.providerSettings,
options.platform,
),
+2 -2
View File
@@ -48,7 +48,7 @@ it("joins a late provider inspection and retries only its unresolved cleanup own
return [];
},
});
const inspection = host.providerInspections!([]);
const inspection = host.providerInspections!();
let closing: Promise<unknown> | undefined;
await withVerifierCleanup(async () => {
await awaitAcknowledgement(
@@ -83,7 +83,7 @@ it("joins a late provider inspection and retries only its unresolved cleanup own
await host.close!();
expect(failure.cleanupIncomplete).toBe(false);
expect(closes).toEqual({ successful: 1, refused: 2 });
await expect(host.providerInspections!([])).rejects.toThrow(
await expect(host.providerInspections!()).rejects.toThrow(
"scope is closed",
);
}, [
+9 -17
View File
@@ -100,10 +100,8 @@ export interface DoctorProviderCheck {
/** Read-only provider inspection supplied by an outer composition adapter. */
export interface DoctorProviderInspection {
readonly id: string;
/** Null means the selected client's setting could not be determined. */
readonly configured: boolean | null;
/** Null means readiness cannot be established for the selected client. */
readonly available: boolean | null;
readonly configured: boolean;
readonly available: boolean;
readonly providerVersion: string | null;
/** Validated discoveries used as registration defaults. Setup preserves
* caller-selected configuration separately from these readiness facts. */
@@ -138,9 +136,7 @@ export interface DoctorHost {
brewHopperPath(): Promise<string | undefined>;
/** Renamed `Hopper*.app` launchers; consulted only where macOS bundles are probed. */
manualHopperPaths(): Promise<readonly string[]>;
providerInspections?(
clients: readonly string[],
): Promise<readonly DoctorProviderInspection[]>;
providerInspections?(): Promise<readonly DoctorProviderInspection[]>;
/** Resolve a parsed upstream request budget from the effective client environment. */
resolveProviderRequestBudget?: ProviderRequestBudgetResolver;
installationPaths?(): Promise<readonly string[]>;
@@ -281,7 +277,7 @@ const inspectDoctor = async (
checks: diagnosticChecks,
hopperPath,
providerInspections,
} = await collectDoctorDiagnostics(target, host, scope.clients);
} = await collectDoctorDiagnostics(target, host);
const hopperAvailable =
hopperPath !== undefined &&
diagnosticChecks
@@ -394,8 +390,9 @@ export const registrationEnvironmentDetail = (
].join(" ");
/**
* Report what the registration's file leaves out without gating health:
* doctor cannot observe the environment the client actually passes on.
* A healthy provider check describes this terminal, not the server a client
* starts. Report what the registration's file leaves out without gating health:
* the client may still pass the setting on, which doctor cannot observe.
*/
const registrationEnvironmentChecks = (
registrations: readonly ClientRegistrationStatus[],
@@ -624,7 +621,6 @@ export interface SystemDoctorHostOptions {
readonly providerInspections?: (
resourceScope: RetainedResourceScope,
signal: AbortSignal,
clients: readonly string[],
) => Promise<readonly DoctorProviderInspection[]>;
readonly resolveProviderRequestBudget?: (
provider: Parameters<ProviderRequestBudgetResolver>[0],
@@ -824,13 +820,9 @@ export const systemDoctorHost = (
...(options.providerInspections === undefined
? {}
: {
providerInspections: (clients) =>
providerInspections: () =>
resources.run(() =>
options.providerInspections!(
resourceScope,
inspectionSignal,
clients,
),
options.providerInspections!(resourceScope, inspectionSignal),
),
}),
...(resolveProviderRequestBudget === undefined
+1 -2
View File
@@ -22,7 +22,6 @@ export interface DoctorDiagnostics {
export const collectDoctorDiagnostics = async (
target: string | undefined,
host: DoctorHost,
clients: readonly string[] = [],
): Promise<DoctorDiagnostics> => {
const checks: DoctorCheck[] = [nodeCheck(host)];
checks.push(await hostCheck(host));
@@ -35,7 +34,7 @@ export const collectDoctorDiagnostics = async (
const hopperVersionCheck = await optionalHopperVersionCheck(host, hopperPath);
if (hopperVersionCheck !== undefined) checks.push(hopperVersionCheck);
const providerInspections = await host.providerInspections?.(clients ?? []);
const providerInspections = await host.providerInspections?.();
if (providerInspections !== undefined)
checks.push(...providerInspections.flatMap(providerDoctorChecks));
Loaded 100 of 541 files, more files were not shown because too many files have changed in this diff. Show more