Compare commits

...
Author SHA1 Message Date
gensokyo-dev 4453d724f7 test(cli): assert retained managed target selection provenance 2026-10-24 18:18:58 +00:00
Wenyu Chiou 1ca7d38764 fix(evidence): link composed workflows to consumed observations (#7028) 2026-10-25 02:11:32 +08:00
ed79bf9361 feat(artifacts): inventory and extract Node.js single executable application blobs (#7000)
* feat(artifacts): inventory and extract Node.js single executable application blobs

Adds a node-sea artifact reader that locates the NODE_SEA_BLOB payload in
Mach-O sections, ELF notes and PE RCDATA resources, decodes the blob
header, main script, optional snapshot and code cache, and named assets,
and extracts their stored bytes through the existing safe output tree.
The executable is never run. Resource-tree errors in a PE without a
NODE_SEA_BLOB name stay with native classification; truncated, unknown
or over-limit SEA declarations fail closed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016y53PdpyMAp9RSW65fatHe

* fix(artifacts): reuse PE resource facts and bound SEA asset tables

Move the bounded NODE_SEA_BLOB name probe into the shared PE resource
layout module so SEA admission uses the same header and section mapping
as the full parser; the layout reader now accepts a header prefix with
the file size. Bound SEA asset count and decoded name bytes before
records are created and report resource bounds as limit failures. Offer
the JavaScript next probe only for main scripts with a source suffix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016y53PdpyMAp9RSW65fatHe

* fix(artifacts): make SEA metadata construction cancellable

---------

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-25 02:10:41 +08:00
morluto 08b8e62914 refactor(mcp): consolidate retained-summary inspection workflows 2026-10-25 02:03:28 +08:00
morluto 9a13c4f420 refactor(config): centralize parsed registration environment keys 2026-10-25 02:03:28 +08:00
gensokyo-dev bc9efc9279 test(mcp): verify recovery before reading retained views (#7029) 2026-10-24 17:54:27 +00:00
Dhruvilkumar Ashokbhai Chodvadiyaanddhruvac29 ab30df08f7 fix(targets): keep original DOS header padding out of e_lfanew (#7013)
* fix(targets): keep original DOS padding out of e_lfanew

A complete DOS header with a nonempty relocation table that ended before
e_lfanew treated the padding dword at 0x3c as a Windows declaration. Without
a recognized Windows signature, fall back to the validated DOS structure so
original DOS images are not refused as malformed Windows targets. Preserve
unresolved-candidate handling when no relocation table delimits the padding.

* test(ghidra): cover DOS padding and unresolved header candidates

* fix(targets): read Windows signatures before dismissing DOS padding

* docs(targets): reconcile DOS padding admission contract

Document that relocation-table padding inside a complete DOS header is DOS data, while an unread Windows signature beyond the current probe is requested before that rule applies.

Aligns docs/installation.md with docs/ghidra-dos.md after the MZ admission fix.

---------

Co-authored-by: dhruvac29 <dhruvac29@users.noreply.github.com>
2026-10-25 01:54:20 +08:00
Stoian Andrei-ȘerbanandStoian Andrei-Șerban 403fef0d40 docs(i18n): add complete Romanian README (#7026)
Co-authored-by: Stoian Andrei-Șerban <11259126+An3treiu@users.noreply.github.com>
2026-10-25 01:54:08 +08:00
niko3xandmorluto de37dfbcc9 fix(website): correct Turkish translation errors (#7015)
* fix(website): correct Turkish translation errors

Correct misleading technical senses in the Turkish catalogue: port,
caller, assembly, instruction, checks, script and oracle, plus
capitalized suffixes, hâlâ, and Turkish number formatting.

Leaves msgid, msgctxt, #: references, obsolete entries and placeholders
untouched across 308 msgstr values.

* fix(website): preserve Turkish words and quotation context

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-25 01:51:24 +08:00
morluto 535926ca2b fix(analysis): retain one-shot target selection provenance
Retain the exact selected CLI path in one-shot provider Evidence while preserving canonical subject identity and provider inputs. The shared request-provenance boundary replaces the artifact-only special case across native, managed, and bundle-resource routes. Remove the redundant parameter fallback and make the private managed helper receive explicit Evidence parameters.

Validation: real macOS codesign fixtures through the compiled CLI retained app aliases for signature, plist, and absent asset inspections, with direct-target controls. Archive selection, managed direct analysis, and snapshot replay regressions passed; Windows-specific replay cases were skipped on macOS. Build, static, format, and docs checks passed. Snapshot-backed direct request provenance still follows its existing authenticated query contract.

Refs: #5479
2026-10-25 01:45:23 +08:00
morluto 84c30609e1 fix(browser): include IndexedDB versions in storage fingerprints
Hash the observed database version with object-store definitions at the CDP boundary. Missing, invalid, or imprecise versions preserve store evidence with incomplete coverage and an explanation. Mark the revised projection so historical store-only hashes cannot create false changes or prove unchanged versions. Preserve comparable metadata and independent storage changes.

Remove the redundant storage comparability wrapper and use each fingerprint's parsed scope instead of interpreting its identity string.

Validation: producer-to-comparison regressions and a compiled CLI to stdio MCP comparison passed, including version-only changes, invalid versions, legacy projections, and complete database additions/removals. Static and docs checks passed. CDP responses were simulated; no real browser runtime was exercised.
2026-10-25 01:45:23 +08:00
morluto 8b57dc630e fix(native): preserve metadata uncertainty in coverage
Explain unreadable ELF note section names without discarding complete note coverage. Keep export and PE import rows when only version or DLL-name metadata is unresolved, and stop claiming those retained rows were unreported.

Use native byte equality for the 4 MiB ELF extended-numbering source-preservation checks; generic deep equality enumerated every buffer byte and exhausted the test worker heap. The full fixture and byte coverage remain.

Validation: focused ELF note, native export, and PE import regressions plus complete CLI/MCP adapter files passed under the existing 512 MiB heap limit. Build, typecheck/lint, formatting, and documentation checks passed.
2026-10-25 01:45:23 +08:00
morluto 12f6e0974c fix(cli): retain Cutter discovery through shared finalization 2026-10-25 01:34:37 +08:00
morluto 900f21b982 fix(mcp): centralize validated partial Evidence delivery 2026-10-25 01:34:37 +08:00
Dhruvilkumar Ashokbhai Chodvadiyaanddhruvac29 97194c4b48 fix(ghidra): admit recordless MIPS-I legacy targets (#7014)
* fix(ghidra): admit recordless MIPS-I legacy targets

MIPS-I headers carry a zero-value ISA field (EF_MIPS_ARCH_1) and predate the ABI flags record. The recordless legacy admission only accepted EF_MIPS_ARCH_32 and EF_MIPS_ARCH_32R2, so every MIPS-I target was refused before provider selection even when its ELF32/ET_EXEC shape matched the existing inferred-o32 lane.

Admit MIPS-I through the same recordless legacy path, pin the endian-matched 32-bit MIPS language, and use a distinct support-lane identity so profile-bound snapshots invalidate. Declared-ABI MIPS-I headers remain refused because that lane is verified for MIPS32r2 only. The session reports that the generic 32-bit language also decodes later MIPS32 instructions and that MIPS-I subset conformance is not established from the header.

Fixes #6933.

* fix(ghidra): bind MIPS profiles to their target

Refuse a committed MIPS lane or ELF metadata that was resolved for a different target, including the same-endian MIPS-I and MIPS32 legacy lanes that share a language pin. Report the committed caller-selected MIPS-I language in the session limitation instead of claiming the provider pin.

---------

Co-authored-by: dhruvac29 <dhruvac29@users.noreply.github.com>
2026-10-25 01:16:51 +08:00
Wenyu Chiou a1967fbbdb fix(lldb): retain completed trace facts through cleanup failures (#7023) 2026-10-25 01:16:26 +08:00
gensokyo-dev 422ca5a8f8 test(cli): follow prepared projection ownership during cancellation (#7022) 2026-10-24 16:46:34 +00:00
gensokyo-dev 397aa62f2e test(web): align manifest path errors with artifact ownership (#7021) 2026-10-24 16:18:25 +00:00
41cf869949 fix(environment): bound create learning and invalidate stale write proofs (#6929)
* feat(environment): report what an environment serves with an environment-coverage view

A new inspect_analysis_view kind over capture Evidence. It projects the
state an environment derives with the given template rules and reports
each tool as answered from state, replayed with its view reason, a served
write, or a withheld write with its reason, the records behind it and a
hint about what to record next. The hints cover every write reason, so a
new reason without one fails to compile.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(environment): build an environment bundle in one command

rea environment-build <capture> [directory] inspects a HAR (or reads
capture Evidence), projects the environment manifest from the in-memory
Evidence, authenticates the pair, prepares the state and writes the bundle
environment-export writes. Every bundle file is byte-identical to the
three-step path for the same capture and options.

The report adds the capture and manifest identities, the coverage summary,
warnings (a loopback capture named after its file, credential-named body
members by pointer) and the next command. --dry-run writes nothing. The
default template rules are the four the published environments use.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(environment): cover environment-build and the coverage view

Domain tests check the hint table against the state view's reasons, the
summary, and the view against the environment-state view it summarizes.
Service tests build the fixture capture and compare every file with the
three-step path, a dry run, loopback naming and the refusals. A CLI test
compares a HAR built in one step with the three CLI steps byte for byte,
and an MCP test projects the view from every source kind.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(environment): build the real-agentenv lane's bundle with environment-build

The lane now runs the command users run: it builds the Grafana-shaped HAR
with environment-build and the packed checkout, checks the report, then
runs the bundle with agent-env as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(environment): document environment-build and the coverage view

The agent environments guide opens with the one-step build: the report,
the hint for each withheld write reason, and what a bundle ships. The CLI
guide, the capture guide and the skill reference name the command and the
environment-coverage view.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): harden environment-build after review

- The credential scan walks with its own stack, so a deeply nested body
  cannot crash the build, and it also reports credential-named header
  names, query names, form body members and string arrays.
- The build checks SIGINT between stages: cancelled before the bundle
  stage it writes nothing, during the write it names the written bundle.
- A dry run opens the package tarball, so it refuses what a build refuses.
- Repeated template rules are refused, a HAR saved as .json gets a hint,
  file-derived names follow the bundle name rules, `localhost.` counts as
  a loopback origin, and the next command is shell-quoted.
- --capture-timeout-ms passes through to the HAR inspection, and a
  missing directory is refused before inspection.
- The no-observation hint covers updates and deletes of entities the state
  never held; the coverage view states that it does not apply the
  serve-time admission checks; the inspect_analysis_view description
  names the view.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(environment): state the build's conditions and record its verification

The guides now state when the one-step bundle equals the three-step one,
which headers inspection excludes, what the credential warning scans, how
cancellation behaves, and that the coverage view does not apply the
serve-time admission checks. The spec folder records the census, the end
to end runs, the gates and the review.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): dispatch a pending interrupt before each build stage check

The manifest, state and coverage stages run synchronously, so a SIGINT
that arrived during them was only dispatched once the bundle write had
begun. Each check now yields first, so an interrupt before the bundle
stage cancels the build with nothing written.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): scan scenario credentials, own the dry-run tarball and serialize Evidence once

A browser scenario's retained header values and the query parameters of its recorded URLs are scanned for credential
names as a HAR's are, through a review projection of its records. A dry run admits and releases the package tarball
through the export's regular-file scope, so a close it cannot confirm keeps its owner, and checks for cancellation
after that last await. Evidence is written into one exactly sized buffer instead of chunks joined by a copy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(environment): record the review round's checks

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(environment): format the build spec

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): scan every form occurrence and MIME-less scenario bodies

The build's credential scan collapsed form bodies with Object.fromEntries, so a
repeated field kept only its last occurrence: `api_key=secret&api_key=` warned
about nothing although the Evidence ships the value, and repeated values were
undercounted. Form fields are now scanned as ordered name/value pairs, as
headers and query strings already are.

The review projection of a capture_browser_scenario dropped a request body
recorded without a media type, so the scan never saw it. Review keeps such a
body's text with an empty media type; the manifest projection still reads only
bodies it can classify.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): scan structured HAR form params and the request side of unattributed scenario transactions

A HAR request recorded with postData.params and no text is scanned by those parameters, each occurrence counting, and
the review projection keeps the request side of a scenario transaction whose response cannot be attributed, so the
credential warning covers what evidence.json ships in both cases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): validate build paths and read dry-run tarballs

* fix(environment): report bodies too large for the credential scan

Text bodies over the scan's 4 MiB limit, including a response decoded only as a prefix, are reported as a
credential-scan-incomplete warning by record and side, never by content, so an absent credential-named-members
warning no longer reads as a complete check. The spec and agent-environments guide also state the path validation and
dry-run tarball read that e52ae1cd added.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(verification): release cold source transform services

* fix(javascript): retain capture context along covered mutation paths

* fix(environment): admit serialized exports and scan HAR form fallbacks

* fix(environment): admit in-memory build inputs against heap headroom and scan SaveHar null-text forms

The inspected HAR's Evidence and the projected manifest are checked against live heap headroom before they are
serialized, with the same check the file route uses, so a capture too large to parse and seal is refused as
resource_constraint before anything is written. A request whose postData text is SaveHar's null is scanned by its
structured params.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): measure the credential scan limit in UTF-8 bytes

A body counts against the 4 MiB limit by its UTF-8 length, not its UTF-16 code units, so multibyte text over the
limit is reported as unscanned instead of parsed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(javascript): erase inline type imports before binding values

* fix(environment): parse credential form bodies by their media type

* fix(environment): review untyped retained form fields

* fix(environment): bound request comparisons and preserve write proofs

* fix(environment): keep write proofs and bundle review complete

* fix(environment): finalize entity overrides with simulated views

* test(environment): restore 0895c89b's untyped form tests and hint

Integrating main took main's credential scan and coverage view, which
dropped 0895c89b's tests and its body-unusable hint. Main's scan passes
the tests, now reading the scenario by event. The hint names the
response too, since extraction and serving also withhold a write as
body-unusable when its recorded response cannot be read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(environment): scan a scenario content event recorded twice

Main's event scan reads both content events of a transaction whose
content the capture recorded twice; the test fails on 0895c89b, whose
review projection dropped that transaction's response. The spec names
event pointers among the first 20.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(environment): record the review round's checks

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): retain contradictions and trim retired state

* fix(environment): distinguish mixed array order from changed values

* fix(environment): keep dependent create state pinned

* fix(environment): keep checking retired view observations

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-24 23:57:30 +08:00
gensokyo-dev c94bd27e85 fix(runner): scope internal Node notices to the supervisor (#7020) 2026-10-24 15:57:23 +00:00
morluto d9189aebc4 fix(artifacts): retain completed acquisition facts through finalization
Forward the file owner's completed stable-read hash and byte count through descriptor-close failure or cancellation in PE resources, certificates and imports, ELF notes and kernel modules, native exports, Apple IPS, and APK signature inspection. Preserve the primary failure, exact cancellation reason, and failed-close cleanup lease.

Validation: 32 acquisition-finalization regressions across eight providers and 30 existing APK provider, lifecycle, and identity tests passed. Existing native filesystem regressions and typecheck/lint also passed. No external analysis engines or Android device were used.
2026-10-24 23:51:20 +08:00
morluto 72e75e9a48 fix(metadata): preserve declared phandles and relocation fields
Keep malformed canonical devicetree phandles unresolved instead of falling back to legacy identities. Report the PE dynamic relocation offset and section at their exact source widths with Size-gated absence and zero preservation. Clarify classic kernel-module version coverage and byte-preserving string encoding.

Validation: 158 focused parser, service, filesystem, and finalization regressions; 14 CLI/MCP tests; build, typecheck/lint, formatting, and docs checks passed. Fixtures establish parser behavior; no external analysis engines were exercised.
2026-10-24 23:51:20 +08:00
morluto e9abf39848 refactor(javascript): remove superseded full-IR analysis pipeline 2026-10-24 23:49:25 +08:00
gensokyo-dev cc7cc8155a test(javascript): bound route scan work independently of timing (#7016) 2026-10-24 15:37:10 +00:00
morluto 8c6cddcffb refactor(javascript): retire synchronous artifact graph runner 2026-10-24 23:33:09 +08:00
morluto 798450638e test(verifiers): retire superseded source-loading regression
The shared source-loading suite covers the former generic command and pre-exit lineage regression, plus package ownership, error-class identity, native module behavior, and preservation of a caller-owned compiler context. Remove the overlapping older fixture after validating those controls.
2026-10-24 23:14:20 +08:00
morluto 731109b7d7 refactor(analysis-view): consolidate result and budget ownership
Reuse the shared immutable-result reader for Interface Builder while checking its current parent binding on every read. Accept truthful optional schema outputs and validate normalized JSON before freezing or caching it, preserving explicit undefined facts without caching them.

Move accumulator construction and inline admission policy into the domain. One prepared projection retains the same budget across admission and projection while preserving standalone defaults, custom budgets, and authentication order.
2026-10-24 23:11:02 +08:00
morluto 399e2226ca refactor(resources): remove redundant helper exports and lease aliases
Keep ASAR header details and the Godot header parser module-local. Use the canonical lease types in Hopper consumers, including release-cache publication, while preserving acquisition and cleanup behavior.
2026-10-24 23:11:02 +08:00
gensokyo-dev 1184819cb9 fix(verifiers): load shared source without compiler processes (#7012) 2026-10-24 15:02:22 +00:00
morluto bff03bfc97 test(setup): retain Hermes policies across registration refresh 2026-10-24 22:54:05 +08:00
morluto 4f212c6a30 fix(analysis): preserve artifact and selection error ownership 2026-10-24 22:54:05 +08:00
69f99c5198 feat(environment): time browser scenario records so scenario environments learn a write clock (#6967)
* feat(browser): record when scenario requests were observed and how long they took

Request events carry observed_at, the wall-clock time the provider observed
the request start, and finished or failed events carry the browser's
elapsed_ms when it reports one. Both are optional, so earlier captures still
parse, and scenario comparisons leave them out as a committed built-in rule.
The real-Chrome network lane checks every request and finished response is
timed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(environment): time browser scenario records so their state learns a write clock

Scenario records take startedDateTime from when each request was observed and
time from the browser's duration, as HAR entries are timed. A timed scenario
of the book session learns the same clock and serves the same writes as its
HAR; an untimed one keeps record order and learns no clock, as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(environment): document scenario request times and timed scenario records

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): start scenario records at the browser's own request start

The provider observes a request up to tens of milliseconds after the browser
starts it while it reads other responses, so a record starting then and
ending after the browser's duration mixed two clocks. Finished and failed
events now carry the browser's started_at, records prefer it and fall back to
observed_at, and comparisons drop it too. Tests cover the fallback for a
request that never ended or ended twice, a partly untimed capture keeping
record order, comparing an untimed capture with a timed one, and the
real-Chrome lane checks every finished request carries the browser's start
and duration.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(environment): document the browser start time, its fallback lag and older readers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(environment): record the scenario timing verification

Gates, the real-Chrome lane, prompt-recorded and re-recorded published
scenario captures built with and without their times, the stale published
environments left unrepublished, and the review.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(browser): compare timed completion events in the request-time comparison test

The comparison test's captures had no finished or failed events, so dropping
started_at and elapsed_ms from them went untested. The captures now end one
request and fail the other, the test asserts both carry times, compares them
shifted by seven seconds and against an untimed capture, and a control shows
a changed failure on a timed completion is still reported.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-24 22:48:50 +08:00
b4ba96a9a5 feat(firmware): inspect flattened devicetree blobs (#7009)
* feat(firmware): inspect flattened devicetree blobs

Adds inspect-devicetree and inspect_devicetree for FDT v16/v17 blobs
without target execution or external dependencies. Results include
validated headers, reservations, a node/property tree with bounded raw
previews, and derived standard values and selected phandle references.
Parsing enforces file, tree, and decoded representation budgets and
rejects malformed input without partial success. Summary delivery
supports retained Evidence pagination, with tests covering parsing,
service validation, CLI delivery, and MCP access after the input is
removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016y53PdpyMAp9RSW65fatHe

* fix(firmware): reject conflicting devicetree phandles and bound page summaries

A node whose phandle and linux,phandle disagree no longer resolves under
either value; the conflict is reported as a limitation. Finalization
failures after a completed read retain the file-snapshot-hash partial
observation, matching the Go provider. Devicetree view pages carry
fixed-size context and counts instead of repeating summary strings, so a
large bootargs no longer pushes every page over the MCP response budget.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016y53PdpyMAp9RSW65fatHe

---------

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-24 22:45:22 +08:00
Ahmed 7e57b8c212 fix(process): preserve caller-selected working directory in scenario validation errors (#7003)
* fix(process): preserve caller-selected working directory in scenario validation errors

* test(process): cover caller-selected working-directory alias preservation
2026-10-24 22:41:34 +08:00
dba8a31978 feat(elf): inspect Linux kernel module metadata (#7010)
* feat(elf): inspect Linux kernel module metadata

Adds inspect-kernel-module and inspect_kernel_module for ELF32/ELF64
ET_REL Linux kernel modules, read statically without executing them.
Reports ordered .modinfo entries with duplicates preserved, __versions
symbol CRCs, the presence of .gnu.linkonce.this_module and __ksymtab,
.init.text and .exit.text sizes, and an appended module signature
trailer with its PKCS#7 byte range and SHA-256. gzip, xz and zstd
containers are reported unsupported with external decompression as the
next probe. Malformed headers and exhausted entry budgets return no
partial success.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016y53PdpyMAp9RSW65fatHe

* fix(elf): bound kernel module ksymtab listing and tighten contracts

Charge listed __ksymtab* sections against max_entries, omit section names
longer than 256 bytes with ksymtab_name_exceeds_limit, and aggregate the
non-file-backed limitation so repeated huge names cannot inflate Evidence.
Use fixedTupleSchema for the signature pad bytes with projection parity
tests, and assert the late-cancellation reason and retained observation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016y53PdpyMAp9RSW65fatHe

* fix: resolve merge integration errors

---------

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-24 22:35:20 +08:00
morluto de088fcec6 fix(verifiers): join model fixture request finalization
Keep each admitted HTTP handler owned through Evidence writes and error rendering, including after its client disconnects. Close joins graceful transport shutdown and handler work before publishing completion, and propagates exact late failures while preserving ordinary request diagnostics.

Share lifecycle ownership across OpenAI, Anthropic, and Gemini fixtures while retaining protocol-specific parsing and responses. Exercise real disconnects, graceful concurrent and repeated close, ordinary 400s, and opaque late failures.
2026-10-24 22:28:39 +08:00
morluto 48ff37f6ea fix(verifiers): consolidate shared helper ownership
Use JSON.parse directly in package verification and retain one parsed Cursor recovery snapshot. Route native command source loading through the shared transform-service owner so completed command cleanup leaves no transform child.

Preserve JSONC validation and caller inputs. Cover the actual public source-loading lifecycle with an authored command and verified process lineage.
2026-10-24 22:28:39 +08:00
791524c2fa fix(importer): reach package-main selection for trailing-slash directory references (#4219)
* fix(importer): reach package-main selection for trailing-slash directory references

posix.normalize preserves a trailing slash in the specifier, so normalized was sub/ while dirname(match) was sub; the resolvedThroughIndex comparison failed and selectPackageMain never ran for require("./sub/"). Compare against the slash-stripped directory so trailing-slash directory references take the same package-main path as the plain spelling. Boundary rows pair each trailing-slash request with the existing manifest fixtures and are validated against Node's own resolver in the same table.

Fixes #4189

* fix(importer): derive trailing-slash directory identity with path semantics

The lookbehind strip kept a trailing slash when the final component ended in a line terminator (require("./pkg\n/")), so dirname(match) still never matched and the trailing-slash path skipped package-main selection for those directories. Join the normalized path with "." instead: path semantics collapse any trailing slash run regardless of the preceding character, and behave identically to the strip on every other spelling. Boundary fixture gains a newline directory with a package.json, an index and a declared main so the index path (not the no-match branch) exercises the comparison.

Addresses Codex P1 on #4219

* fix(importer): normalize package-main targets with path semantics

---------

Co-authored-by: Hemang Varshney <218273197+hemv-857@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 23:14:19 +09:00
c6a2576aba fix(contracts): reject NUL bytes in objdump and rizin local path inputs (#3600)
Co-authored-by: qorvr <330186599+qorvr@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
2026-10-24 23:13:34 +09:00
3d84215bb2 feat(pe): inspect TLS callbacks and load configuration (#7008)
* feat(pe): inspect TLS callbacks and load configuration

Adds inspect-pe-load-config and inspect_pe_load_config for PE32 and
PE32+ images, with artifact hashes and file ranges in the returned
evidence. Reports callback addresses and mappings, plus size-covered
security-cookie, SafeSEH, CFG, EH-continuation and XFG fields;
flag-derived mitigation claims are explicitly labeled inferred. Missing
directories remain null, and malformed storage or exhausted read budgets
return errors without partial success. Adds documentation and tests for
service validation, CLI/MCP access, malformed inputs and stable-file
handling.

x86 images that declare the legacy 64-byte load-config directory are
read to the structure's own Size when those bytes are file-backed, as
the Windows loader does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016y53PdpyMAp9RSW65fatHe

* fix(pe): retain snapshot facts and bound load-config section lookup

Keep the completed stable-read path, SHA-256 and byte count as a
file-snapshot-hash partial observation when descriptor close fails or is
cancelled. Report the checked Size span as structure_location and add it
to Evidence locations beside the declared directory range. Resolve
callback sections through a sorted segment index built once per image,
keeping the ambiguity failure. Pin every load-config field to offsets
transcribed from the PE/COFF spec and llvm COFF.h at its exact Size
boundary for PE32 and PE32+.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016y53PdpyMAp9RSW65fatHe

---------

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 22:06:41 +08:00
02e049744f feat(environment): build agent environments from a prompt (#6946)
* feat(environment): report what an environment serves with an environment-coverage view

A new inspect_analysis_view kind over capture Evidence. It projects the
state an environment derives with the given template rules and reports
each tool as answered from state, replayed with its view reason, a served
write, or a withheld write with its reason, the records behind it and a
hint about what to record next. The hints cover every write reason, so a
new reason without one fails to compile.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(environment): build an environment bundle in one command

rea environment-build <capture> [directory] inspects a HAR (or reads
capture Evidence), projects the environment manifest from the in-memory
Evidence, authenticates the pair, prepares the state and writes the bundle
environment-export writes. Every bundle file is byte-identical to the
three-step path for the same capture and options.

The report adds the capture and manifest identities, the coverage summary,
warnings (a loopback capture named after its file, credential-named body
members by pointer) and the next command. --dry-run writes nothing. The
default template rules are the four the published environments use.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(environment): cover environment-build and the coverage view

Domain tests check the hint table against the state view's reasons, the
summary, and the view against the environment-state view it summarizes.
Service tests build the fixture capture and compare every file with the
three-step path, a dry run, loopback naming and the refusals. A CLI test
compares a HAR built in one step with the three CLI steps byte for byte,
and an MCP test projects the view from every source kind.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(environment): build the real-agentenv lane's bundle with environment-build

The lane now runs the command users run: it builds the Grafana-shaped HAR
with environment-build and the packed checkout, checks the report, then
runs the bundle with agent-env as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(environment): document environment-build and the coverage view

The agent environments guide opens with the one-step build: the report,
the hint for each withheld write reason, and what a bundle ships. The CLI
guide, the capture guide and the skill reference name the command and the
environment-coverage view.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): harden environment-build after review

- The credential scan walks with its own stack, so a deeply nested body
  cannot crash the build, and it also reports credential-named header
  names, query names, form body members and string arrays.
- The build checks SIGINT between stages: cancelled before the bundle
  stage it writes nothing, during the write it names the written bundle.
- A dry run opens the package tarball, so it refuses what a build refuses.
- Repeated template rules are refused, a HAR saved as .json gets a hint,
  file-derived names follow the bundle name rules, `localhost.` counts as
  a loopback origin, and the next command is shell-quoted.
- --capture-timeout-ms passes through to the HAR inspection, and a
  missing directory is refused before inspection.
- The no-observation hint covers updates and deletes of entities the state
  never held; the coverage view states that it does not apply the
  serve-time admission checks; the inspect_analysis_view description
  names the view.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(environment): state the build's conditions and record its verification

The guides now state when the one-step bundle equals the three-step one,
which headers inspection excludes, what the credential warning scans, how
cancellation behaves, and that the coverage view does not apply the
serve-time admission checks. The spec folder records the census, the end
to end runs, the gates and the review.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): dispatch a pending interrupt before each build stage check

The manifest, state and coverage stages run synchronously, so a SIGINT
that arrived during them was only dispatched once the bundle write had
begun. Each check now yields first, so an interrupt before the bundle
stage cancels the build with nothing written.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(browser): send declared requests from a scenario page with a request step

A capture_browser_scenario step `request` sends one declared HTTP request
through the page's own fetch, with its cookies and session, the way the
application's frontend would, and reads the whole response so the capture
records the transaction like any other. The path must resolve to the start
URL's origin, and the page must be on that origin when the step runs.
Query values and header values may be literals or declared secrets, and a
header may read its value from a cookie or a local storage entry, which is
then redacted from later records like a declared secret. `expected_status`
fails the step on any other status. Scenarios without a request step are
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(browser): verify request steps in a real browser through to a built environment

verify:browser:request records a write session against a fixture item API
that refuses writes without its CSRF cookie header and stored bearer
token. It checks that every request lands in its own step, that the page
values authenticated each write and appear in no record, header or body,
that an unexpected status fails the step and cancels the rest, and that
environment-build serves the recorded create, update and delete.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(environment): export environment bundles over MCP

export_environment_bundle writes the bundle environment-build writes, from
capture Evidence retained in the session or one capture Evidence file,
into an absolute output directory that must not exist and whose parent
must exist, as extract_firmware and export_web_scripts require; it never
creates the parent or reuses a directory. It returns the same report,
whose schema now lives in the domain so the CLI and the tool share it, and
dry_run reports without writing. A loopback capture without a file needs a
name. The build's refusal messages no longer name CLI flags.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): scan scenario credentials, own the dry-run tarball and serialize Evidence once

A browser scenario's retained header values and the query parameters of its recorded URLs are scanned for credential
names as a HAR's are, through a review projection of its records. A dry run admits and releases the package tarball
through the export's regular-file scope, so a close it cannot confirm keeps its owner, and checks for cancellation
after that last await. Evidence is written into one exactly sized buffer instead of chunks joined by a copy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(environment): record the review round's checks

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(skill): build agent environments from a prompt with request steps and the bundle tool

Add the environments reference to the skill and install it with setup: record
against a disposable instance, sign in through the UI, write with request
steps and read each write back, check coverage, re-record by withheld reason
and export the bundle. Route it from SKILL.md, name agent environments in the
skill description, and document the request step, the export tool and
recording writes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(browser): split the request lane and header resolution into smaller functions

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style(environment): format the build spec

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): say when an untimed capture learns no write clock

Coverage of a capture whose records are not all timed, which includes every
browser scenario capture, now states that the state learns no clock, so an
update stamped with a server-set modification time stays unexplained-change
however often it is recorded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(skill): give agents a whole scenario shape with step timeouts

Agents guessed the scenario schema field by field and two hung on steps
without timeout_ms. The reference now starts from a complete scenario with a
timeout on every step, and says a scenario capture cannot serve updates the
application stamps with a modification time.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(browser): keep request steps on their origin and their page values redacted

- Follow no redirects: a redirect fails the step and nothing reaches the
  location it names (checked in real Chrome), and check the page's origin
  again in the page as the request is sent.
- Send nothing once the step's time has run out or it was cancelled while
  header values were read; the in-page deadline is what remains of the step's.
- Keep every page value redacted under a repeated or colliding label, keep a
  value's first marker when it is read again, and leave values shorter than
  8 characters unredacted rather than rewriting ids, ports and hosts.
- Refuse a header name given twice, User-Agent (Chrome drops it) and line
  breaks in literal values and prefixes; fail before sending when a secret or
  page value holds them.
- Append query entries after the path's literal query without re-encoding it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(environment): document request step limits and word bundle refusals for both surfaces

Document redirects, timeouts, page value redaction, HttpOnly cookie sources
and header refusals for request steps. Refusals shared by environment-build
and export_environment_bundle no longer name CLI flags, and the retained
loopback refusal no longer prints an empty file name. The skill reference
asks for read-backs after each write, shows the coverage view's source, names
package_tarball for unreleased builds and the browser path a scenario ships.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): scan every form occurrence and MIME-less scenario bodies

The build's credential scan collapsed form bodies with Object.fromEntries, so a
repeated field kept only its last occurrence: `api_key=secret&api_key=` warned
about nothing although the Evidence ships the value, and repeated values were
undercounted. Form fields are now scanned as ordered name/value pairs, as
headers and query strings already are.

The review projection of a capture_browser_scenario dropped a request body
recorded without a media type, so the scan never saw it. Review keeps such a
body's text with an empty media type; the manifest projection still reads only
bodies it can classify.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(browser): close request step edge cases from the second review

- Append query entries correctly after a path ending in "?", "&" or "#".
- Refuse 3xx expected statuses, which an unfollowed redirect can never
  report, and say a redirected write may already have been applied.
- Redact a page value also as fetch sends it, without surrounding spaces
  and tabs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(environment): document page value body costs, redirects and MCP dry runs

Document that reading a page value shrinks the bodies a capture keeps under
the default allocation limit, that redirected writes may have landed, that
the MCP dry_run still checks its output directory and which refusals name
the tool, and keep the .har hint in the HAR-as-JSON refusal for both
surfaces.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(environment): record the prompt-to-environment verification

Gates, the real-browser lanes, five environments built from prompts by an
agent on disposable VMs and evaluated with two models, and the two reviews.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(skill): count rollback failures from the managed skill files

The environments reference made the skill eight files, so the recovery tests now derive the counts they expect from
MANAGED_SKILL_FILES instead of fixing them at six and seven.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(adb): restore the portable Java float pattern a merge rewrote

A lint autofix during a merge unescaped the hyphens in javaFloatValuePattern, which is invalid in a v-mode character
class, so the advertised start_adb_activity schema failed to compile in every JS regex mode. The file now matches main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(browser): show the request step inside a whole scenario

Every JSON example in the browser scenario guide is a capture_browser_scenario input the catalog test parses, so the
request step example now sits in a launch scenario instead of standing alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): scan structured HAR form params and the request side of unattributed scenario transactions

A HAR request recorded with postData.params and no text is scanned by those parameters, each occurrence counting, and
the review projection keeps the request side of a scenario transaction whose response cannot be attributed, so the
credential warning covers what evidence.json ships in both cases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): validate build paths and read dry-run tarballs

* fix(environment): report bodies too large for the credential scan

Text bodies over the scan's 4 MiB limit, including a response decoded only as a prefix, are reported as a
credential-scan-incomplete warning by record and side, never by content, so an absent credential-named-members
warning no longer reads as a complete check. The spec and agent-environments guide also state the path validation and
dry-run tarball read that e52ae1cd added.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(verification): release cold source transform services

* fix(javascript): retain capture context along covered mutation paths

* fix(environment): guard request dispatch and report export limits

* fix(environment): admit serialized exports and scan HAR form fallbacks

* fix(environment): admit in-memory build inputs against heap headroom and scan SaveHar null-text forms

The inspected HAR's Evidence and the projected manifest are checked against live heap headroom before they are
serialized, with the same check the file route uses, so a capture too large to parse and seal is refused as
resource_constraint before anything is written. A request whose postData text is SaveHar's null is scanned by its
structured params.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): measure the credential scan limit in UTF-8 bytes

A body counts against the 4 MiB limit by its UTF-8 length, not its UTF-16 code units, so multibyte text over the
limit is reported as unscanned instead of parsed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(browser): send request steps only before their deadline and keep page reads on the start origin

The page receives the step's absolute deadline and a one-second start window, so a busy page that runs the request
late, or after the step is cancelled, sends nothing; local storage is read only on the start origin; the response is
drained without keeping its body; an unparseable path is a field issue; retained Evidence is refused for heap headroom
before it is serialized; and the skill says how to reach server-assigned ids.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* style: format integrated export admission

* fix(javascript): erase inline type imports before binding values

* fix(browser): run the request lane under the shared verifier command owner

The browser request lane runs each CLI call through createPackageVerifierCommands on a private runtime root, so a
timeout, output overflow or failure stops the CLI and the browser it launched before the root is removed, and a failed
stop is reported with the root retained. The verification record states the lane's current counts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): scan scenario content by event and read bodies by their media type

A browser scenario's credential scan reads every retained network-content event, including a response attached
mid-flight and a transaction no record represents, with event pointers, and each transaction URL's query once; the
review record projection it replaces is removed. A body is parsed as a form or as JSON by its recorded media type,
falling back to its shape, so valid forms such as `password=x&` are scanned and sentences are not read as forms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-24 21:51:41 +08:00
d361c53c50 feat(environment): serve writes by what their recordings show (#6897)
* fix(environment): keep read-only POSTs out of the write list

A POST that repeats the latest read of its path, tunnels only read
operations, names a lookup in its path's last meaningful word, or renews
a session no longer counts as a state write: it is replayed, does not cut
initial windows, and does not end observation searches. A tool stays a
write when a related read line changes across one of its calls.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): take an entity an unplanned write made when a read first shows it

A successful item read of an entity the gate's copy never held, after a
write no built plan models, puts the entity into the copy instead of
failing the read, so later writes to it are checked as usual. An entity a
modelled write touched is never taken back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): plan a create into the type its returned id is read under

A POST whose path names no state type becomes a create into the type
whose item read first names an id its response returned, when the id is
new to the state; serving creates in that read's collection. A successful
POST that returned an id a type's reads only show after it also ends that
type's initial window, so entities a capture created stay out of the
initial state.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): end a type's initial reads at the write its reads disagree across

When a type's reads disagree across a write under another root, the
type's initial window now ends at the earliest write within that change
and the state is extracted again, instead of leaving every view of the
type replayed. The cut composes with the creation cut; the earlier wins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): replay request lines that show entities in another form

A read line that shows the same entities in another form, by an extra
query parameter that adds or drops members, or conflicts with a base
line on a shared entity, is a variant: it leaves the view's checks and
the entity observations, the view names it with the query keys that pick
it out, and a call matching those keys replays its recording. A filter
role whose name matches no field needs two agreeing values.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(environment): specify and record the state-target changes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): keep a read-only POST a write when its evidence is incomplete

Body-level container operations are skipped like query ones, a lookup is named only
by the path's last literal segment and never alongside a write word, a tool with a
redirected or unanswered call is not reclassified, and the mutation veto is resolved
over all candidates at once so another read-only call cannot explain a read change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): answer a field selection only when the state shows it exactly

Serving tests a field selection before any variant key: a projection value a replayed
read gave, or one no checked line gave other than `*`, replays. A list sets aside a
line asking for some fields only before the identity proof, and a dropped variant's
membership change no longer rewrites the type.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): hold adopted entities and linked creates to what the recording shows

An adopted entity is charged against the view's budget like a created one. A linked
create holds only for its exemplar's path arguments: serving refuses others and the
gate counts such a call as unmodelled. A response's identity-named id links at any
length, but a short one only to a type read before the call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(environment): record the review fixes' measurements

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): release a discarded planning pass before planning again

A rewrite cut clears the first pass, so the budget rewinds to a checkpoint taken after
the write cutoffs and created ids before the state is extracted again. Rewrite spans are
charged and appended without spreading them into an argument list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): link no create whose id an earlier read showed, and read only whole batches

A linked create is rejected when an earlier list of its type or read of its collection
showed the returned id. Body ids are walked without spreading children into an argument
list. An RPC call reads only when every operations entry names an operation of its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): let the gate skip exactly the reads serving replays

Serving's replay decision moves to the domain as replayReason, and the write gate uses it:
a read serving replays can neither approve nor veto a write, and a read of every field
beside a replayed selection is checked. A selection read missing a required field is
still skipped, since part of an entity can neither confirm nor check it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(environment): record the second review round's checks

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): read writes, links and creation cuts only from evidence that rules out other causes

- A read-only POST repeats only a read that finished before it began; the veto compares
  the latest read finished before a call with the first begun after it, and counts a
  recorded absence on one side as a change. `modify` and `replace` are write words.
- A redirected or unanswered write lets a later item read adopt the entity it may have made.
- A creation cut counts a read still running when the POST began as after it, and a list
  read shows its listed entities' own ids, not those they nest.
- A linked create's item read must come before any other tool's write.
- Line shapes are charged to the budget as they are built.
- A `fields=*` list line is whole, not a field selection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(environment): record the third review round's checks

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): tie linked creates to their returned ids and order state by the lines left

- A linked create is observed only in a read of an id its response returned,
  envelopes included, and serving takes its recorded path values in either spelling.
- A list read shows a suffix-named identity (`projectId`) distinct across its entities.
- A read-only POST compares the read that finished last before it began.
- An entity's first sighting comes from the lines left after variant lines are dropped.
- The pairwise variant pass stops at a work limit, leaving every line checked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(environment): record the fourth review round's checks

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): keep foreign keys, stale reads and double decoding out of write evidence

- A list read counts a suffix-named field as its entities' ids only when no
  id-named field identifies them, so an `ownerId` beside `id` cuts no window.
- A read-only POST is compared with the read that finished last among those no
  other write follows, so a consuming call stays a write.
- A linked create's source path values compare as spelled on both sides, so no
  spelling is decoded twice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(environment): record the fifth review round's checks

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): compare linked sources by position, decode path fields once and bound read and variant scans

- A linked create's source values compare segment by segment, so a template
  naming two parameters alike cannot accept a changed first one.
- Serving fills a linked create's path fields with its arguments decoded once,
  as the gate learned them.
- The read-only check reads each related line's records once per call, between
  the nearest other writes.
- Member signatures are JSON arrays, so a name holding the separator still
  conflicts; the variant work limit counts line pairs as well as entities.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(environment): record the sixth review round's checks

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): replay late and variant-only items, fill linked path fields from the exemplar and charge membership spans

- An item line first recorded after the initial window that names a new shaping
  parameter and shows other members is a replayed variant, like an initial one.
- An item only a replayed line showed, which the state never started with,
  answers with its own recording; one the state held and lost stays missing.
- A linked create fills its path fields from its exemplar's path decoded once,
  whichever accepted spelling the call sends.
- Each membership change span is charged against the budget before it is kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(environment): record the seventh review round's checks

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): blame a failed read on the writes whose fields it disagrees on

A failed item read with no interfering unmodelled write contradicts its creates and
deletes, and an update only when a field the update wrote or its response showed
differs. A create is contradicted when a later read embeds it elsewhere and the state
does not. A create member is fixed to its recorded value when a constant it would
have to change derives from it: its text inside a constant, a referenced entity's name,
or an instant beside a sent date, matched by related field names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): explain unsent update changes and nested clocks by what the recording shows

An update's unsent change can be a modified-by stamp beside a created-by counterpart, a
value derived from the one sent member stored nowhere (accepted only while the target
holds the recorded before-value), or a sent clock the call left in its own second.
Objects whose clocks all moved with the request are nested clocks, opaque tokens of a
type's own length are generated, and an enveloped body is learned as its one object.
A write takes only its recorded query, an echoed reference to a nested object and a
state word paired with other fields stay at their recorded values, and a slug is tried
before a token.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): order collections by whole lines and sort only arrays a read-back proves sorted

A collection's initial order comes from the first list line that shows every entity its
type's sightings do and applies no order. A view renders a string-array field sorted
only after a counted read-back shows the written values in an order that neither the
written order nor existing-values-first explains, and never once a read showed it
unsorted. Sort and direction parameters share one vocabulary and are never learned as
inert, and a create placed by disagreeing sorts or a random id falls back to last.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(environment): serve bulk writes at a collection and target item actions

A DELETE, PUT or PATCH at a collection, or a POST at its delete path, whose body names
held entities by id is applied to each named entity, and serving refuses it unless every
id is held. A POST to an entity's action path that answers nothing or the entity is an
update of it; it is served only when a recorded call found each scalar it sets already
at that value. Served bulk bodies advertise their items, and compare probes pass them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): decide late item lines against the lines left, charge replay keys and bisect rewrite cuts

- Item lines first recorded after the initial reads are decided once variants
  are dropped, against the lines left: a new shaping parameter showing other
  members, or a selection of some fields leaving members out, replays.
- Each replay key is charged against the budget before it is kept.
- A rewrite span's earliest write is found by bisection over the writes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(environment): record the eighth review round's checks

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): set aside response members a path mints anew on every read

A top-level UUID or clock that differs on every repeat of one path's successful reads,
with no write between, is left out of that path's item, list and document comparisons
and named in the view's per_response_members. Serving keeps the recorded value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): count only reads finished before a linked create began as showing its id held

A list still running when the call began may show what the call made, as the
creation cut already decides, so it no longer refuses the link.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): count a list total nested in a paging object

An item total in a top-level object beside the list, such as pageInfo.totalRows, is a
counter on the same evidence as a top-level one: it must equal the number listed, and
one that differs marks the line partial. Serving and the gate render it from the state
through its JSON pointer and leave the object's other members as recorded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): take a created entity's nested clocks from its read-back

After a create's generated values pass, its stored nested clocks take the instants the
first later read of the entity shows, as an update's already do, so a create whose
response and read spell the clock differently is confirmed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): import the compare tool definition as a type

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(environment): describe the new write rules and trim repeated parsing

The environment and capture docs describe sorted arrays, per-response members, nested
counters, item actions, bulk writes, the new write rules and refusals, and their known
limits. `_order` is a direction only. Sorted-array retries and the per-response scan
parse only the reads they need, and identifier words come from one helper.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): confirm no update whose clocks or fields the evidence cannot show it writing

An unsent clock an update left in the second it ran keeps the update unconfirmed until
another call shows the clock stamped. A field the state lacks that the read and the
update's response both show is the update's change, so without a rule for it the read
confirms nothing. A modified-by name stamp brings its id sibling along, and created
entities are indexed by id when a failed read is checked for embeds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): show pinned query and body values in the served catalog and apply bulk writes atomically

A pinned query parameter is required with its recorded value as the only choice, and a
call that leaves it out takes that value. A fixed body member shows its recorded value,
and an enveloped body its wrapping, in the schema, the description and the refusal.
Bulk writes go through the same query pin and update or remove their entities at once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(environment): specify and record the write-rule changes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): keep served creates' items missing, check same-type earlier reads, and bound late lines and read-only stretches

- An item only a replayed line showed replays only while no served create has
  added to its type, so a created and deleted item stays missing.
- A linked create's earlier item-read veto checks only reads of the candidate's
  type or collection that show the item, so another type's id does not hide it.
- An item read answered with JSON null shows no id to a creation cut.
- Late lines' member names are charged before they are kept.
- The read-only check precomputes nearest writes and passes once over each
  write-free stretch per path, skipping lines whose reads all agree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(environment): record the ninth review round's checks

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): link creates past empty item reads, sweep repeated reads once and keep only returned ids for creation cuts

- A linked create passes over a later item read naming its id whose body shows
  no item, such as JSON null, and links the next one.
- Read-only repeated reads are found in one sweep: each path's reads wait by
  end time until a call's start passes them, and a write clears them.
- A creation cut keeps, and charges, only the shown ids some creation returned.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(environment): record the tenth review round's checks

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): take bulk ids literally, check each recorded call's query once, and keep learned sorts open to contrary reads

Bulk writes take body ids as written, refuse a call naming one entity twice, size updates from the replacements
installed, generate each entity's values from that entity, and delete only with the members their exemplar sent
besides the ids. Comparison translates bulk body ids with the target collection's mapping.

Update rules echo a creation clock sent back unchanged, require every held clock, and refuse a nested clock once any
recorded call kept its object. A recorded call sent with another query than its exemplar's, including item actions,
documents and deletes, neither confirms nor teaches its plan, and only successful calls with that query relax a
create's guards.

A counted read listing a learned array unsorted drops the sort from its view, sort evidence comes only from the
entities the read covers, and a top-level counter whose name starts with a slash is named by its escaped pointer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(environment): record the review fixes' checks

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* perf(environment): compare variant lines through shared entities and parse each compared body once

The variant pass indexes a tool's lines by the entities they show and compares only lines that share one, charging
the index to the budget while it is held; the pairwise work limit still decides when a tool keeps every line checked.
Read-only decisions keep each response body's canonical digest, taken once while its parse is charged, across calls
and veto passes. The creation cut no longer parses reads at or after the write that ends the initial reads, and reads
the ids a body shows inside its lease. Adoption looks up a read's target only after an unplanned write before it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(environment): record the eleventh review round's checks

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* perf(environment): list a type's collections only when the path's own holds no id, and parse variant base lines once

The write gate's item target lists the type's held collections, and the removed targets', only when the path's own
collection holds none of its ids, so a served item view's reads no longer walk every entity of the type. The variant
pass parses the base lines' queries once for all the variants they key, and not at all when it found none.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(environment): record the twelfth review round's checks and the sweep's cost

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(environment): record the 50-capture sweep's wall, CPU and peak memory

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): remove duplicate schema after main integration

* fix(environment): honor recorded bulk identity kinds

* fix(environment): retain scoped evidence and honor write defaults

* test(environment): keep SDK regressions independently scoped

* docs(environment): record scoped write verification and measured costs

* fix(environment): serve bulk writes whose response lists each entity they wrote

A bulk call answering an array with one element per entity its body names, in the body's order, is classified by
its elements, so the existing renderer serves it from the entities each call writes. Responses listing entities out
of order, with differing members or id spellings, wrapped elements or an envelope still refuse.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(environment): measure the 50 captures at the scoped-envelope head

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(environment): classify bulk responses and release verifier transforms

* fix(environment): classify and verify complete bulk responses

* fix(javascript): retain capture context along covered mutation paths

* test(environment): verify bulk responses through the real MCP SDK

* test(environment): separate bulk response proof regressions

* fix(environment): preserve request absence and generated value relationships

* fix(environment): verify complete nested clocks on creates

* fix(javascript): erase inline type imports before binding values

* fix(environment): share complete generated value verification

* fix(verifiers): load the Windows source graph only when needed

* fix(environment): validate write responses before stored clock restamps

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-24 20:52:03 +08:00
gensokyo-dev 347b6e4d95 test(verifiers): assert shared command diagnostics and identity (#7002) 2026-10-24 12:51:53 +00:00
吴佳浩Alben 2e86294c86 fix(firmware): stop recommending a raise at the staging budget ceiling (#6971)
The staging refusal told callers to increase max_output_files or max_output_bytes within its schema maximum. At the schema ceiling that recovery does not exist, so the refusal was unactionable exactly where the least room remains.

firmwareStagingBudgetRemediation now branches on the selected value against the schema maximum: below the ceiling it names the room and the control, at the ceiling it states that a further raise is unsupported and points at a smaller firmware byte range. The schema maxima move into named constants so the advice and the accepted range cannot drift apart.

Verified with a module test for both budgets at the ceiling and one below it (red on the previous single-form remediation), plus the existing firmware suites.
2026-10-24 21:35:30 +09:00
gensokyo-dev 73320bb2c9 fix(ci): isolate browser verifier D-Bus sessions (#6999) 2026-10-24 12:15:46 +00:00
morluto 2250e67bc2 fix(verifiers): consolidate native client command lifetimes
Migrate the remaining native verifier commands to shared ownership with explicit stdin contracts, command-before-fixture cleanup, and failure receipts. Preserve observed catalog counts and unknowns, retained artifacts, native assertions, and selected launch inputs.

Remove the obsolete raw exec export and share output persistence across native verifiers. Exercise actual entrypoints with authored failed preflights and acknowledged descendants, plus filesystem failures and opaque error preservation.
2026-10-24 19:40:50 +08:00
morluto cd1878f3fe fix(javascript): bound loop capture and rest-copy expansion 2026-10-24 19:36:20 +08:00
gensokyo-dev be2811e414 fix(javascript): exclude erased imports from runtime bindings (#6994) 2026-10-24 11:22:23 +00:00
morluto 0402012a95 fix(verifiers): share command ownership and preserve stdin
Move command and retained resource lifetimes into a shared verifier owner, keeping package workspace release as a separate dependency. Migrate Pi and Hermes verification to that owner while preserving open pipes, explicit EOF, diagnostics, and retained labs.

Require positive writable finalization before accepting EOF delivery. Cover premature close, opaque failures, late ownership delivery, mixed cleanup outcomes, exact retry, and real descendant cleanup with authored process boundaries.
2026-10-24 19:16:42 +08:00
gensokyo-dev 9895f1870c test(evaluation): find failure gate within cleanup callbacks (#6988) 2026-10-24 10:50:50 +00:00
gensokyo-dev dd02d8ff69 fix(javascript): bound cyclic mutation captures by their paths (#6986) 2026-10-24 10:11:27 +00:00
morluto b22b0ef22a refactor: retire test-only fallback exports
Remove duplicate MCP defaults, the fixed CDP deadline message, and the canned Windows authority reason after verifying their only consumers were tests.

Pin independent expected budget facts, exercise active default and selected CDP deadlines, and verify exact authored native diagnostic propagation. Preserve malformed-input, cancellation, omission, and authority assertions. Verified 222 focused cases, 9 README cases, build, static checks, docs, discovery, and duplication at 3.58%.
2026-10-24 18:04:35 +08:00
morluto 89006609f1 fix(javascript): separate erased types from runtime semantics
Record erasure during definition indexing and reuse it across runtime references, captures, effects, module links, routes, Electron and Vite. Preserve source type links and emitted decorator and computed-key effects; keep actual runtime reads and escapes conservative.

Validation: 1,647 focused tests and 9 documentation tests passed, with build, static and documentation gates. The inherited reference-lifetimes stress file exhausts a 512 MiB worker on both unmodified 18451e479 and this change; that file remains unverified. TypeScript 7.0.2 emit was inspected for ES2022 and ES2018 without executing emitted code.
2026-10-24 18:00:43 +08:00
Ariel Fikru Avicennaandgensokyo-dev cab78de506 feat(ghidra): analyze GameCube/Wii DOL executables through Ghidra (#6240)
* feat(domain): parse and validate GameCube/Wii DOL headers

Add a pure parser for the 0x100-byte DOL header (7 text and 11 data
slots, BSS, entry point). Used slots must lie after the header and
inside the file and the 0x80000000..0x81800000 load window; text slots
are 4-byte aligned; sections may not overlap in memory or in the file;
the entry point must be inside a text section. BSS may overlap data
sections, as .sdata/.sdata2 do in shipped DOLs.

Refs #1741

* feat(binary): admit caller-selected GameCube/Wii DOL targets

* feat(ghidra): import GameCube/Wii DOL targets through a section-map pre-script

* test(ghidra): cover DOL launcher arguments, attestation and raw profile digests

* test(ghidra): add the real verify:ghidra:dol lane and DOL docs

* fix(ghidra): read only the DOL header and section ranges instead of the whole file

* test(ghidra): derive DOL lane addresses from readelf and admit Gekko from the install catalog

* fix(ghidra): attest DOL block permissions, space, overlay and load state

* fix(ghidra): keep load-image permissions optional for older inspections

* fix(contracts): accept DOL targets in binary_session output

* fix(ghidra): adapt the DOL lane to the current upstream address schema

Validate DOL load-image coordinates with the upstream observed-address
schema, drop a duplicate import left by the rebase, and re-pin the raw
profile digests to the unmodified upstream resolver.

* fix(ghidra): preserve bridge inventory symbol provenance

* fix(ghidra): align DOL verification with shared workflow contracts

* feat(domain): parse and validate GameCube/Wii DOL headers

Add a pure parser for the 0x100-byte DOL header (7 text and 11 data
slots, BSS, entry point). Used slots must lie after the header and
inside the file and the 0x80000000..0x81800000 load window; text slots
are 4-byte aligned; sections may not overlap in memory or in the file;
the entry point must be inside a text section. BSS may overlap data
sections, as .sdata/.sdata2 do in shipped DOLs.

Refs #1741

* feat(binary): admit caller-selected GameCube/Wii DOL targets

* feat(ghidra): import GameCube/Wii DOL targets through a section-map pre-script

* test(ghidra): cover DOL launcher arguments, attestation and raw profile digests

* test(ghidra): add the real verify:ghidra:dol lane and DOL docs

* fix(ghidra): read only the DOL header and section ranges instead of the whole file

* test(ghidra): derive DOL lane addresses from readelf and admit Gekko from the install catalog

* fix(ghidra): attest DOL block permissions, space, overlay and load state

* fix(ghidra): keep load-image permissions optional for older inspections

* fix(contracts): accept DOL targets in binary_session output

* fix(ghidra): adapt the DOL lane to the current upstream address schema

Validate DOL load-image coordinates with the upstream observed-address
schema, drop a duplicate import left by the rebase, and re-pin the raw
profile digests to the unmodified upstream resolver.

* docs(ghidra): qualify the DOL memory window

* refactor(ghidra): own DOL BSS ranges in the parsed header

* docs(ghidra): record real DOL workflows and task costs

* docs(ghidra): record final DOL integration verification

* fix(mcp): share the native procedure search row contract

* docs(ghidra): record public procedure search verification

---------

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-24 17:56:31 +08:00
morluto d01ee7b19a fix(verifiers): own setup terminal and snapshot lifetimes
Join identity-verified PTY family cleanup and native exit before workspace removal, retaining failed cleanup capabilities for retry. Replace duplicate kill-and-reject loops with shared process ownership and deadlines.

Capture configuration before setup using owned file handles and compare raw bytes. Preserve interactive cancellation, terminal restoration, redirected output, and selected inputs; remove the test-only forwarding export. Verified 122 focused cases, 9 README cases, build, static checks, docs, discovery, and duplication at 3.58%.
2026-10-24 17:54:18 +08:00
Saiyam JainandSaiyam Jain ddde7276c4 test(erlang): cover lambdas names filter and chunks miss in BEAM views (#6973)
Add lambdas names-filter selector cases to checkSelectors:
- names: ['start'] returns the matching lambda with resolved fields
- names: ['missing'] returns an empty page

Add a chunks no-match case:
- names: ['Xyzz'] returns an empty page for an absent chunk tag

Also clarify in docs/erlang-beam.md what names selects for each
collection: the four-character IFF tag for chunks, the resolved
function name (never null) for lambdas, and the decoded atom name
string for atoms.

Co-authored-by: Saiyam Jain <saiyam@example.com>
2026-10-24 17:39:57 +08:00
gensokyo-dev 48c203f356 fix(build): prepare release dependency types during installation (#6978) 2026-10-24 09:24:43 +00:00
morluto 18451e4797 test(release): type patched history options before runtime setup 2026-10-24 17:21:08 +08:00
morluto df5c48ffda docs(cutter): explain bridge descriptor discovery configuration 2026-10-24 17:21:08 +08:00
morluto 9dfa44f776 fix(cancellation): retain caller reasons at admission checkpoints 2026-10-24 17:21:08 +08:00
morluto 3fb26985c4 fix(native): classify dyld refusals before export fallback 2026-10-24 17:21:08 +08:00
morluto af3f025688 fix(il2cpp): accept names in native type-reference views 2026-10-24 17:21:08 +08:00
morluto 43c7255dbd fix(android): preserve binary class selectors across metadata calls 2026-10-24 17:21:08 +08:00
Dery Ferdikaandgensokyo-dev a45ee81167 fix(ghidra): enforce request limits before encoding (#6917)
* fix(ghidra): enforce the bridge request ceiling before writing

Hopper and Cutter both measure an oversized payload and refuse it before the
request reaches its bridge, using the shared REA_BRIDGE_MAX_REQUEST_BYTES
ceiling. Ghidra had no ceiling on either end: GhidraWire.request wrote the
stringified request straight to the socket, and the Java bridge reads request
lines with an unbounded readLine() after its bounded 64 KiB handshake.

Serialize once, measure the encoded payload, and refuse before the operation
is registered as pending and before any socket write, so an oversized request
never reaches the bridge. Carry the typed constraint as the cause and
re-attribute it in projectPrimarySessionError, so callers see the same
resource constraint Hopper and Cutter already report.

The Ghidra bridge reads no ceiling of its own, so its recovery text names that
only the REA client applies the setting instead of asking for a bridge restart.

Validation: src/ghidra/GhidraClientWire.test.ts (3 cases, red then green),
src/config/bridgeMaxRequestBytes.test.ts (16), src/ghidra/GhidraRequestQueue.test.ts
(25); tsc --noEmit over the changed set exits 0; oxlint reports 0 errors;
oxfmt --check clean; verify:module-boundaries passes. The two failures in the
wider src/config+src/ghidra+src/hopper+src/cutter sweep
(captureDecoderHeap, GhidraAnalysisSeeds symlink EPERM) reproduce identically
on pristine bded9c628 with this change stashed. The Ghidra fixture suite
cannot run on this Windows host: fakeGhidra.mjs renames bridge-endpoint.json
and hits EBUSY on 3 of 3 runs, so the fixture-level test was not added.

* fix(ghidra): preflight request bytes before encoding

---------

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-24 17:17:52 +08:00
f2fa7f648d feat(android): project a structured manifest component inventory (#5025)
* feat(android): project a structured manifest component inventory

* fix(android): derive provider export defaults from the target SDK and match namespaced attributes

* fix(android): bound resolved names and authorities in the component inventory

* fix(android): separate nonliteral exported values from omission

* fix(android): bound manifest component projection

* fix(android): preserve permission override semantics

* fix(android): retain unresolved package identities

* test(android): cover multibyte manifest name budget

* fix(android): measure inherited permission once

* fix(android): preserve unknown symbolic permissions

* fix(android): preserve manifest component uncertainty

* fix(android): report unsupported manifest facets

* fix(android): preserve manifest identities

* fix(android): preserve alias target references

* fix(android): validate alias targets and authorities

* fix(android): bound alias target expansion

* fix(android): index alias target spellings

* fix(android): preserve alias identities and isolate legacy defaults

* fix(android): keep legacy component defaults typed as unavailable

* docs(android): record manifest projection and real JADX costs

* fix(android): preserve SDK defaults and manifest namespaces

* docs(android): verify SDK and namespace ownership corrections

* fix(android): accept DEX class names and hexadecimal SDK levels

* docs(android): record DEX and SDK follow-up verification

* fix(android): validate manifest package identity separately

* docs(android): record final package-identity verification

* fix(android): preserve exact DEX component and alias names

* docs(android): record exact-name integration verification

* fix(android): retain declared foreground service type text

* docs(android): record final declared-value validation

---------

Co-authored-by: BaaRai <baarai@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-10-24 17:14:23 +08:00
morluto d9660eb032 fix(verifiers): share package command and workspace ownership
Join canonical owned command cleanup before releasing package verifier workspaces. Replace raw package command wrappers, the updater supervisor, WASM cleanup composite, and boolean-only retention with explicit capability ownership.

Preserve selected environments, npm arguments, no-deadline calls, clean exit status, and distinct aggregate/per-stream output budgets. Exercise actual timeout descendants, cancellation, retained cleanup retry, and workspace admission.
2026-10-24 16:58:18 +08:00
morluto de9a8898dc fix(artifacts): preserve opaque close hook failures
Tag close outcomes with Result so rejected undefined remains a failure. Preserve all hook attempts, retained resource owners, aggregate causes, and cleanup retry.
2026-10-24 16:56:49 +08:00
685 changed files with 72020 additions and 11339 deletions

No files matched your search

@@ -1,8 +1,8 @@
---
name: reverse-engineer-anything
description: Reverse engineer native, managed, Electron/JavaScript, packaged, firmware, and browser targets with REA. 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.
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. 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: "46"
version: "49"
---
# REA
@@ -133,6 +133,9 @@ explicit path or endpoint and do not need it.
`capture_browser_scenario` with declared actions. See
[runtime observation](references/runtime-observation.md) for connect/launch
and the focused follow-up browser tools.
- Agent environment (AgentEnv bundle) of a web application: read
[environments](references/environments.md) before recording; it records with
`capture_browser_scenario` and writes the bundle with `export_environment_bundle`.
- Fiddler Everywhere traffic or a native `.saz` capture: read the
[Fiddler workflow](references/runtime-observation.md#fiddler-everywhere-traffic-debugging).
Inspect saved SAZ with `inspect_web_network_capture` and `format: "saz"`;
@@ -259,6 +262,14 @@ 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).
- 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
and section, and the size-covered security cookie, SafeSEH, CFG,
EH-continuation and XFG fields. Guard-flag mitigation claims are inferred
linker declarations, not proof of enforcement. A missing directory is observed
absence of that declaration. See the
[PE load configuration guide](https://github.com/morluto/rea/blob/main/docs/pe-load-config.md).
- ELF note tables: `inspect_elf_notes` with the explicit local ELF32/ELF64
file. This built-in target-free inspection requires no disassembler. Preserve
PT_NOTE and SHT_NOTE ranges, owners, types, descriptor SHA-256, and GNU
@@ -273,6 +284,16 @@ explicit path or endpoint and do not need it.
body; other types, including `"288"` stackshots, return no crash result.
Opened Mach-O and dSYM UUID joins remain unobserved. See the
[Apple IPS guide](https://github.com/morluto/rea/blob/main/docs/apple-ips.md).
- Linux kernel module metadata: `inspect_kernel_module` with the explicit local
ELF32/ELF64 relocatable file. This built-in target-free inspection requires
no Linux host or `modinfo`. Preserve `.modinfo` key order and duplicate keys,
`__versions` CRC hex, section file ranges, and the appended signature
trailer's PKCS#7 SHA-256. A missing section is observed absence when section
names are known, not proof the module never carried that metadata.
`struct module` is not decoded. The signature is not validated. gzip, xz, and
zstd containers are unsupported; decompress into a separate file with an
external codec utility, then inspect the decompressed `ET_REL` image. See the
[kernel module guide](https://github.com/morluto/rea/blob/main/docs/kernel-modules.md).
- CPython 3.7–3.13 `.pyc` header, nested marshal code objects and bytecode
identity: `inspect_python_pyc` with the explicit local file. This built-in
target-free inspection requires no Python installation. Preserve magic,
@@ -471,6 +492,9 @@ as complete while required questions remain open.
[references/runtime-observation.md](references/runtime-observation.md)
- Evidence paging, comparisons, residual unknowns, and verification:
[references/evidence-workflows.md](references/evidence-workflows.md)
- Agent environments (AgentEnv bundles) of a web application, recorded from
a live instance or a HAR:
[references/environments.md](references/environments.md)
## Finish the task
@@ -0,0 +1,216 @@
# Agent environments from a live application
Use this when the user asks for an agent environment (an AgentEnv bundle) of a
web application: a folder `agent-env` deploys, whose MCP tools answer from what
one recording of the application observed and serve the writes that recording
confirmed. The workflow records the application once with
`capture_browser_scenario`, checks what the environment would serve, records
again if a hint names a gap, then writes the bundle with
`export_environment_bundle`. For a HAR the user already has, skip to
[export](#export-and-hand-off) through `inspect_web_network_capture`, or use
`rea environment-build <capture.har> <dir>`.
## Before recording
- Record only against an instance the user says is disposable or seeded for
this purpose: every write step creates, edits or deletes its data. If the
user has not said so, ask before any scenario that writes. REA does not
start, seed or reset applications.
- Credentials come from environment variables the user names. Declare each as a
scenario secret, `{secret_id, environment_variable}`; the variable must be set
in the environment REA's MCP server started with. Never ask for a password in
chat. Declared secrets are redacted as the capture records.
- Map the application first with `inspect_web_page` (and `capture_web_screenshot`
when layout matters): the login form's locators, and the API calls behind the
pages that list and edit the entities the user asked about.
## Record a session an environment can learn writes from
One `capture_browser_scenario` records the whole session. Start from this
shape, with the application's own locators, paths and headers:
```json
{
"browser": { "mode": "launch", "executable_path": "/usr/bin/chromium" },
"start_url": { "url": "http://127.0.0.1:3000/login" },
"secrets": [
{ "secret_id": "admin_password", "environment_variable": "APP_PASSWORD" }
],
"capture": {
"network": {
"request_body": true,
"response_body": true,
"header_values": true
}
},
"actions": [
{
"step_id": "user",
"action": "fill",
"timeout_ms": 15000,
"locator": { "kind": "css", "selector": "input[name=username]" },
"value": { "source": "literal", "value": "admin" }
},
{
"step_id": "password",
"action": "fill",
"timeout_ms": 15000,
"locator": { "kind": "css", "selector": "input[name=password]" },
"value": { "source": "secret", "secret_id": "admin_password" }
},
{
"step_id": "sign-in",
"action": "press",
"timeout_ms": 15000,
"locator": { "kind": "css", "selector": "input[name=password]" },
"key": "Enter"
},
{
"step_id": "signed-in",
"action": "wait_for",
"timeout_ms": 15000,
"locator": { "kind": "css", "selector": "nav a[href='/settings']" },
"state": "visible"
},
{
"step_id": "folders-1",
"action": "request",
"timeout_ms": 15000,
"method": "GET",
"path": "/api/folders",
"expected_status": [200]
},
{
"step_id": "create-folder-1",
"action": "request",
"timeout_ms": 15000,
"method": "POST",
"path": "/api/folders",
"json": { "title": "Scratch folder 1" },
"headers": [
{
"name": "X-CSRFToken",
"value": { "source": "cookie", "name": "csrftoken" }
}
],
"expected_status": [200, 201]
}
]
}
```
Give every step a `timeout_ms`. A step without one waits until the scenario
is cancelled, so a locator that never matches hangs the capture, and the
client with it. Match `wait_for` locators to exactly one element.
1. Sign in through the UI: `fill` the user and password fields (the password
as a secret reference), submit, then `wait_for` an element only a signed-in
user sees.
2. Read with `request` steps: each collection the environment needs, twice
before any write, so fields that change on their own show up; one item of
each.
3. Use `request` steps only when the API lets the caller choose each scratch
object's ID or a prior read already established it. Request paths are fixed
strings; no action substitutes a value from an earlier response. For
server-assigned IDs, create through the UI and open the new object's detail
view to record its read, edit and delete through UI actions. If that journey
cannot be scripted, leave the write unserved and report the limitation;
never guess an ID or create objects with no supported cleanup path.
Record two numbered scratch objects per write type,
with varied free text. After each write, and before the next one, read the
object back by id and then its list: create 1, read 1, list, create 2,
read 2, list; never several writes before their reads.
4. Leave more than a second between a create and its first edit
(`wait_for_timeout` with `duration_ms: 1100`).
5. Update both objects and read each back; delete both and read each back
(expect 404) and the list.
About `request` steps:
- `path` is on the start URL's origin; the page must be on that origin.
- Header values are literals, declared secrets, a cookie the browser holds
(`{source: "cookie", name}`), or a local storage entry
(`{source: "local_storage", key, json_pointer}`), with an optional
`prefix` such as `"Bearer "`. Copy the headers the application's own
requests send (a CSRF header, a bearer token). Values read from the page
are redacted from records made after they are read, not from the login
response that returned them, and values shorter than 8 characters are not
redacted at all.
- Reading a page value makes later bodies costlier to keep: after a CSRF
token, bodies over about 47 KiB are kept as `unavailable`. When the API
returns larger responses, raise `capture.limits`
(`max_network_body_allocation_bytes`, `max_total_event_allocation_bytes`).
- Request steps follow no redirects: a redirect fails the step, and
`expected_status` cannot name a 3xx status. Use the path the redirect names,
such as the one with the trailing slash.
- Set `expected_status` on every step, so a refused call fails its step and
the later steps report not having run, instead of recording read-backs of
nothing.
Keep it to about 60 to 160 requests. A scenario with no `request` step is
recorded exactly as before.
The capture returns its whole Evidence, every recorded body included, which is
often more than a client shows inline. Before checking coverage, confirm that
every entry of `normalized_result.steps` has `status: "completed"`; if the
client saved the result to a file, search it for `"status":"failed"` and read
that step's `error`. A failed sign-in or write cancels every step after it.
Use the result's `evidence_id` for coverage and export; do not paste the
Evidence back into another tool.
## Check coverage
`inspect_analysis_view` with `source: {kind: "retained-evidence",
evidence_id}` (the capture's id) and `view: {kind: "environment-coverage",
template_rules: ["after-uid-literal", "opaque-token", "sibling-variance",
"prefix-variance"]}`. It reports read tools
answered from the state or replayed (`reads.recorded_reasons`), the served write
tools, and each withheld write with its `reason` and `hint`. It does not apply
the serve-time catalog and state size checks; the export reports those.
Re-record a withheld write at most twice, and only for a recording gap. Stop at
once when the reason is application behaviour, and report the write as
withheld with its hint:
| Reason | What to do |
| --------------------- | --------------------------------------------------------------------------------------------- |
| `no-state-target` | Re-record: read the collection or item the write changes. |
| `no-observation` | Re-record: read the entity back by id after the write; read it first for an update or delete. |
| `unconfirmed` | Re-record: read the entity and its list after the write. |
| `incomplete-entities` | Re-record: read the entity through every view that lists it. |
| `unexplained-change` | Re-record once with a second, different call of the same write. |
| `no-call` | Re-record once with a write that succeeds. |
| `side-effect` | Stop: the application changes other entities. |
| `contradicted` | Stop: a later read disagrees with the modelled effect. |
| `body-unusable` | Stop: the body is not a JSON object. |
Use new scratch names on each attempt; the first attempt's objects may still
exist.
New browser scenario captures include request times, so a fully timed capture
can learn a write clock and serve updates that stamp modification times. Older
captures or ones missing a request start keep record order and learn no clock.
When coverage reports that limitation, record again with the current version
before concluding that the timestamp change cannot be served.
## Export and hand off
`export_environment_bundle` with `source: {kind: "retained-evidence",
evidence_id}` (or `{kind: "evidence-file", path}`), an absolute
`output_directory` that does not exist yet and whose parent does, and a `name`
(required when the application runs on `localhost` or an IP address). It returns
the coverage again, `tool_count`, the written files and warnings; `dry_run:
true` reports without writing. The bundle installs `rea-agents` of this REA's
version from the registry; when the user runs an unreleased REA build, pass the
`package_tarball` they give you (a packed `rea-agents` tarball) instead.
Tell the user how many tools answer from the state, which writes are served and
which are withheld and why, and the run command (`agent-env run <folder>`). Say
that the bundle ships the recorded responses and the capture's local paths
(for a browser scenario, the browser executable's path), and
list any `credential-named-members` warning pointers for review. Never publish
or share the bundle yourself.
See
[agent environments](https://github.com/morluto/rea/blob/main/docs/agent-environments.md)
for what the state, replay and the bundle establish, and
[evidence workflows](evidence-workflows.md) for the manifest and state views.
@@ -106,6 +106,13 @@ each plan states why in its `reason`. It replays the capture instead with
`--replay-only` or when the state is unavailable (not derivable, or a catalog
over the discovery budget), and its stopped document's `state` says which
applied.
Use `view: {kind: "environment-coverage"}` with the same rules to see what an
environment built from a capture would serve: read tools answered from the
state or replayed with their reason, and each write tool served or withheld
with a hint about what to record next; it does not apply the serve-time
catalog and state size checks. `rea environment-build <capture.har> <dir>`
inspects, projects and exports in one step and prints the same coverage, or
`null` when the bundle serves replay.
For a PE/ELF export question, `inspect_native_exports` accepts `detail: "summary"`
and retains the complete table. Query `inspect_analysis_view` with
@@ -125,6 +125,19 @@ 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).
## ELF note tables
Use `inspect_elf_notes` for an explicit local ELF32/ELF64 image when the
@@ -148,6 +161,25 @@ uppercase image UUIDs, and integer literals that exceed IEEE-754 range. Only
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
@@ -0,0 +1,55 @@
name: Real Android metadata bridge
on:
push:
branches: ["**"]
paths:
- .github/workflows/real-android-metadata.yml
- bridge/android/**
- src/android/JadxRelease.ts
- scripts/fixtures/android/**
- tests/conformance/android/**
- tests/fixtures/android/static-initializers/**
- tests/fixtures/android/direct-invokes/**
- .nvmrc
workflow_dispatch:
workflow_call:
permissions:
contents: read
concurrency:
group: android-metadata-${{ github.ref }}
cancel-in-progress: true
jobs:
android-metadata:
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
- name: Fetch checksum-pinned engine and public APK fixture
run: node scripts/fixtures/android/fetch.mjs "$RUNNER_TEMP/rea-android"
- name: Compile and exercise the real metadata bridge
shell: bash
run: |
export JAVA_HOME="$JAVA_HOME_21_X64"
android_root="$RUNNER_TEMP/rea-android"
classes="$android_root/classes"
jar="$android_root/jadx-headless-mcp-0.7.1-all.jar"
mkdir -p "$classes"
"$JAVA_HOME/bin/javac" -J-Xmx512m -J-XX:ActiveProcessorCount=2 \
-classpath "$jar" -d "$classes" \
bridge/android/ReaJadxBridge.java \
tests/conformance/android/ReaJadxMetadataRegression.java
"$JAVA_HOME/bin/java" -Xmx512m -XX:ActiveProcessorCount=2 \
-classpath "$classes:$jar" ReaJadxMetadataRegression \
"$android_root/ApiDemos-debug.apk" \
"$GITHUB_WORKSPACE/tests/fixtures/android/static-initializers" \
"$classes" "$GITHUB_WORKSPACE/tests/fixtures/android/direct-invokes" \
| tee "$android_root/metadata.log"
grep -F 'RAW_CLASS_SELECTOR_REGRESSION_PASS nested=true metadata=true references=true' \
"$android_root/metadata.log"
+2 -1
View File
@@ -48,7 +48,8 @@ jobs:
- uses: ./.github/actions/portable-build-cache
with:
build_cache_artifact: ${{ inputs.build_cache_artifact }}
- run: npm run verify:browser
# Own a fresh bus so Chromium never depends on an inherited session address.
- run: dbus-run-session -- npm run verify:browser
env:
REA_BROWSER_EXECUTABLE: /usr/bin/google-chrome
REA_BROWSER_NO_SANDBOX: "true"
+2 -1
View File
@@ -44,7 +44,8 @@ jobs:
- uses: ./.github/actions/portable-build-cache
with:
build_cache_artifact: ${{ inputs.build_cache_artifact }}
- run: env -u DBUS_SESSION_BUS_ADDRESS npm run verify:browser:runtime
# Own a fresh bus so Chromium never depends on an inherited session address.
- run: dbus-run-session -- npm run verify:browser:runtime
env:
REA_BROWSER_EXECUTABLE: /usr/bin/google-chrome
REA_BROWSER_NO_SANDBOX: "true"
+2 -1
View File
@@ -46,7 +46,8 @@ jobs:
build_cache_artifact: ${{ inputs.build_cache_artifact }}
- name: Install the pinned compiler into an isolated fixture directory
run: npm install --prefix "$RUNNER_TEMP/rea-web-source-map-compiler" --no-audit --no-fund --package-lock=false esbuild@0.25.10
- run: npm run verify:browser:source-maps
# Own a fresh bus so Chromium never depends on an inherited session address.
- run: dbus-run-session -- npm run verify:browser:source-maps
env:
# Retain browser launch argv/PID/stderr in the verifier's captured
# CLI failure diagnostics without changing its deadline or retrying.
+2 -1
View File
@@ -1,7 +1,7 @@
<div align="center">
**English** · [简体中文](docs/readme/README_zh.md) · [繁體中文](docs/readme/README_zh-TW.md) · [日本語](docs/readme/README_ja.md) · [한국어](docs/readme/README_ko.md) · [Türkçe](docs/readme/README_tr.md) · [Русский](docs/readme/README_ru.md) · [Tiếng Việt](docs/readme/README_vi.md) · [ไทย](docs/readme/README_th.md) · [Deutsch](docs/readme/README_de.md) · [Español](docs/readme/README_es.md) · [Français](docs/readme/README_fr.md) · [Українська](docs/readme/README_uk.md) · [Polski](docs/readme/README_pl.md) · [Português (Brasil)](docs/readme/README_pt-BR.md) · [العربية](docs/readme/README_ar.md) · [فارسی](docs/readme/README_fa.md) · [Bahasa Indonesia](docs/readme/README_id.md) · [Italiano](docs/readme/README_it.md)
· [Ελληνικά](docs/readme/README_el.md) · [മലയാളം](docs/readme/README_ml.md) · [Roman Urdu](docs/readme/README_ur-Latn.md)
· [Ελληνικά](docs/readme/README_el.md) · [മലയാളം](docs/readme/README_ml.md) · [Roman Urdu](docs/readme/README_ur-Latn.md) · [Română](docs/readme/README_ro.md)
# REA: Reverse Engineer Anything
@@ -195,6 +195,7 @@ Additional tools and host support depend on the target:
| 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) |
| 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) |
| Apple IPS crashes | .ips metadata, threads, images, registers and documented crash classes | Built-in static JSON inspection; [Apple IPS](docs/apple-ips.md) |
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](docs/kernel-modules.md) |
| Erlang BEAM | IFF chunks, atoms, exports, imports, lambdas and the Code header | Built-in static BEAM inspection; [Erlang BEAM](docs/erlang-beam.md) |
| WASM artifact | Validation, sections, exact import/export WAT forms and decoded WAT | Caller-supplied WABT 1.0.42; `inspect_wasm_artifact` |
| EVM bytecode | Dispatch selectors, byte offsets, inferred arguments and mutability | Local raw/hex carrier on Linux x64; [offline EVM guide](docs/evm-bytecode.md) |
+9 -5
View File
@@ -269,12 +269,16 @@ class ReaJadxBridge {
"type", field.getType().toString(), "def_pos", 0)).toList();
List<String> inner = node.getInnerClasses().stream().map(ClassNode::getFullName).toList();
Map<String, Object> summary = Map.of(
"full_name", cls.getFullName(), "name", cls.getName(),
"full_name", cls.getFullName(), "raw_name", cls.getRawName(), "name", cls.getName(),
"method_count", methods.size(), "field_count", fields.size(),
"inner_class_count", inner.size(), "inner_classes", inner,
"methods", methods.stream().map(MethodSnapshot::summary).toList(), "fields", fields);
index.computeIfAbsent(cls.getFullName(), ignored -> new ArrayList<>())
.add(new ClassSnapshot(cls, methods, summary));
ClassSnapshot snapshot = new ClassSnapshot(cls, methods, summary);
index.computeIfAbsent(cls.getFullName(), ignored -> new ArrayList<>()).add(snapshot);
// Keep the binary identity supplied by JADX; '$' can also be a literal
// name character, so replacing separators would invent lookup aliases.
if (!cls.getRawName().equals(cls.getFullName()))
index.computeIfAbsent(cls.getRawName(), ignored -> new ArrayList<>()).add(snapshot);
}
}
classes = Collections.unmodifiableMap(index);
@@ -454,7 +458,7 @@ class ReaJadxBridge {
"markers", code.getMarkers(), "body", code.getText()));
}
List<JavaNode> uses;
String target = string(args, "class_name");
String target = (String) cls.summary().get("full_name");
MethodSnapshot selectedMethod = null;
int overloadIndex = 0;
if (name.equals("get_xrefs_to_class")) uses = cls.node().getUseIn();
@@ -522,7 +526,7 @@ class ReaJadxBridge {
SessionHolder holder = new SessionHolder(defaults);
ServerCapabilities capabilities = new ServerCapabilities(
new ServerCapabilities.Tools(null), null, null, null, null, null, null, Map.of());
Server server = new Server(new Implementation("rea-jadx-bridge", "5", null, null, null),
Server server = new Server(new Implementation("rea-jadx-bridge", "6", null, null, null),
new ServerOptions(capabilities), "", ignored -> Unit.INSTANCE);
SessionToolsKt.registerSessionTools(server, holder);
ClassToolsKt.registerClassTools(server, holder);
+3
View File
@@ -643,6 +643,9 @@ public final class ReaGhidraBridge extends HeadlessScript {
mapping.addProperty("initialized", block.isInitialized());
mapping.addProperty("loaded", block.isLoaded());
mapping.addProperty("overlay", block.isOverlay());
mapping.addProperty("readable", block.isRead());
mapping.addProperty("writable", block.isWrite());
mapping.addProperty("executable", block.isExecute());
mapping.addProperty("length", source.getLength());
FileBytes backing = source.getFileBytes().orElse(null);
int sourceIndex = backing == null ? -1 : files.indexOf(backing);
+193
View File
@@ -0,0 +1,193 @@
import ghidra.app.script.GhidraScript;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.mem.MemoryBlockSourceInfo;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
/**
* Rebuild a GameCube/Wii DOL memory map from the validated load ranges REA's TypeScript
* admission wrote. BinaryLoader maps the whole file once at address 0; this
* script replaces that block with text, data, and BSS blocks at their load addresses.
*/
public final class ReaGhidraPrepareDol extends GhidraScript {
// Canonical v2 header, 18 section rows, 12 gap rows and entry, using uint32 hex.
private static final int MAX_PLAN_BYTES = 13 + 18 * 49 + 12 * 30 + 17;
private static final class Section {
final String kind;
final int index;
final long fileOffset;
final long address;
final long size;
Section(String kind, int index, long fileOffset, long address, long size) {
this.kind = kind;
this.index = index;
this.fileOffset = fileOffset;
this.address = address;
this.size = size;
}
}
private record BssGap(long address, long size) {}
@Override
public void run() throws Exception {
String[] arguments = getScriptArgs();
if (arguments.length != 1) {
throw new IllegalArgumentException(
"GameCube DOL preparation requires exactly one section table path"
);
}
if (!currentProgram.getExecutableFormat().equals("Raw Binary") ||
!currentProgram.getLanguageID().getIdAsString().startsWith("PowerPC:BE:32:")) {
throw new IllegalArgumentException(
"GameCube DOL preparation requires a BinaryLoader import in big-endian 32-bit PowerPC"
);
}
Path tablePath = Path.of(arguments[0]);
if (!Files.isRegularFile(tablePath, LinkOption.NOFOLLOW_LINKS)) {
throw new IllegalArgumentException("GameCube DOL section table is not a regular file");
}
if (Files.size(tablePath) > MAX_PLAN_BYTES) {
throw new IllegalArgumentException("GameCube DOL plan exceeds the canonical format's byte bound");
}
List<Section> sections = new ArrayList<>();
List<BssGap> bssGaps = new ArrayList<>();
int expectedSections = -1;
int expectedGaps = -1;
long entry = -1;
for (String line : Files.readAllLines(tablePath)) {
String[] fields = line.trim().split("\\s+");
switch (fields[0]) {
case "plan" -> {
requireFields(fields, 4, line);
if (expectedSections >= 0 || !fields[1].equals("2")) {
throw new IllegalArgumentException("Duplicate or unsupported GameCube DOL plan header");
}
expectedSections = Integer.parseInt(fields[2]);
expectedGaps = Integer.parseInt(fields[3]);
// A DOL has 18 slots, including at most 11 data slots and 12 BSS gaps.
if (expectedSections < 1 || expectedSections > 18 || expectedGaps < 0 || expectedGaps > 12) {
throw new IllegalArgumentException("GameCube DOL plan counts exceed the format's slot bounds");
}
}
case "section" -> {
requireFields(fields, 6, line);
if (expectedSections < 0 || sections.size() >= expectedSections) {
throw new IllegalArgumentException("GameCube DOL section exceeds its declared count");
}
sections.add(new Section(
fields[1],
Integer.parseInt(fields[2]),
Long.decode(fields[3]),
Long.decode(fields[4]),
Long.decode(fields[5])
));
}
case "bss-gap" -> {
requireFields(fields, 3, line);
if (expectedGaps < 0 || bssGaps.size() >= expectedGaps) {
throw new IllegalArgumentException("GameCube DOL BSS gap exceeds its declared count");
}
long address = Long.decode(fields[1]);
long size = Long.decode(fields[2]);
if (address < 0 || size <= 0) {
throw new IllegalArgumentException("GameCube DOL BSS gap is not a positive range");
}
bssGaps.add(new BssGap(address, size));
}
case "entry" -> {
requireFields(fields, 2, line);
entry = Long.decode(fields[1]);
}
default -> throw new IllegalArgumentException("Unknown GameCube DOL table line: " + line);
}
}
if (expectedSections != sections.size() || expectedGaps != bssGaps.size() || entry < 0) {
throw new IllegalArgumentException("GameCube DOL section table is incomplete");
}
Memory memory = currentProgram.getMemory();
List<FileBytes> files = memory.getAllFileBytes();
if (files.size() != 1) {
throw new IllegalStateException("GameCube DOL preparation requires exactly one imported file image");
}
FileBytes fileBytes = files.get(0);
AddressSpace space = currentProgram.getAddressFactory().getDefaultAddressSpace();
// Remove the whole-file block before creating section blocks, so no two blocks claim one address.
for (MemoryBlock block : memory.getBlocks()) {
for (MemoryBlockSourceInfo source : block.getSourceInfos()) {
if (source.getFileBytes().isPresent() && source.getFileBytes().get().equals(fileBytes)) {
memory.removeBlock(block, monitor);
break;
}
}
}
for (Section section : sections) {
if (section.fileOffset + section.size > fileBytes.getSize()) {
throw new IllegalArgumentException(
"GameCube DOL " + section.kind + " " + section.index + " exceeds the imported file"
);
}
boolean text = section.kind.equals("text");
if (!text && !section.kind.equals("data")) {
throw new IllegalArgumentException("Unknown GameCube DOL section kind: " + section.kind);
}
MemoryBlock block = memory.createInitializedBlock(
"." + section.kind + section.index,
space.getAddress(section.address),
fileBytes,
section.fileOffset,
section.size,
false
);
block.setRead(true);
block.setWrite(!text);
block.setExecute(text);
}
// Apply the domain owner's exact uninitialized ranges; do not reinterpret BSS here.
int gapIndex = 0;
for (BssGap gap : bssGaps) {
MemoryBlock block = memory.createUninitializedBlock(
".bss" + gapIndex++,
space.getAddress(gap.address()),
gap.size(),
false
);
block.setRead(true);
block.setWrite(true);
block.setExecute(false);
}
Address entryAddress = space.getAddress(entry);
MemoryBlock entryBlock = memory.getBlock(entryAddress);
if (entryBlock == null || !entryBlock.isExecute()) {
throw new IllegalArgumentException("GameCube DOL entry must lie in an executable section");
}
addEntryPoint(entryAddress);
if (!disassemble(entryAddress) || createFunction(entryAddress, "__start") == null) {
throw new IllegalStateException("GameCube DOL entry cannot be decoded as a function");
}
currentProgram.getOptions(Program.PROGRAM_INFO).setBoolean("REA raw binary prepared", true);
currentProgram.getOptions(Program.PROGRAM_INFO).setBoolean("REA GameCube DOL prepared", true);
}
private static void requireFields(String[] fields, int count, String line) {
if (fields.length != count) {
throw new IllegalArgumentException("Malformed GameCube DOL table line: " + line);
}
}
}
+3
View File
@@ -34,8 +34,10 @@ export default defineConfig({
{ text: "Rust dependencies", link: "/rust-binaries" },
{ text: "PE certificates", link: "/pe-certificates" },
{ text: "PE imports", link: "/pe-imports" },
{ text: "PE TLS/load config", link: "/pe-load-config" },
{ text: "ELF notes", link: "/elf-notes" },
{ text: "Apple IPS crash reports", link: "/apple-ips" },
{ text: "Linux kernel modules", link: "/kernel-modules" },
{ text: "CPython pyc files", link: "/python-pyc" },
{ text: "Erlang BEAM", link: "/erlang-beam" },
{
@@ -80,6 +82,7 @@ export default defineConfig({
{ text: "Ghidra raw binary", link: "/raw-binary" },
{ text: "Ghidra NativeAOT", link: "/ghidra-nativeaot" },
{ text: "Ghidra PowerPC", link: "/ghidra-ppc" },
{ text: "Ghidra GameCube DOL", link: "/ghidra-dol" },
{ text: "Ghidra semantics", link: "/ghidra-provider" },
],
},
+292 -19
View File
@@ -1,16 +1,20 @@
# Agent environments from recorded captures
Use `rea environment-serve` to serve an environment manifest as an agent
environment: an MCP server whose tools answer from what one retained web
network capture observed, plus the environment card the agentenv framework
reads to discover it. By default the server derives the
[state the capture observed](#the-state-a-capture-observed) at startup and
answers read tools from it, and the write tools whose effect the capture
confirmed by changing it; `--replay-only` replays the capture's records
Use `rea environment-build` to turn one recorded capture into an agent
environment bundle in one offline step: a folder `agent-env run` builds and
runs, whose MCP server answers from what the capture observed. See
[Build a bundle in one step](#build-a-bundle-in-one-step).
The steps it composes are separate commands too. `rea environment-serve` serves
an environment manifest as an agent environment: an MCP server whose tools
answer from what one retained web network capture observed, plus the
environment card the agentenv framework reads to discover it. By default the
server derives the [state the capture observed](#the-state-a-capture-observed)
at startup and answers read tools from it, and the write tools whose effect the
capture confirmed by changing it; `--replay-only` replays the capture's records
instead. Nothing is fetched from the recorded application; every answer comes
from retained Evidence. `rea environment-export` writes the same pair as a
bundle folder `agent-env run` builds and runs; see
[Export a bundle](#export-a-bundle).
bundle folder; see [Export a bundle](#export-a-bundle).
```sh
rea inspect-web-network-capture /absolute/session.har har --format json > capture.json
@@ -36,6 +40,185 @@ The third command serves that manifest from the state it derives from the
capture; see [Serve from the state](#serve-from-the-state). Both served files
are complete REA Evidence JSON as the commands write it with `--format json`;
the `evidence` member of `view.json` is the whole of `capture.json`.
A `view.json` can also name the capture file as
`{"kind": "evidence-file", "path": "capture.json"}`, which projects it as
retained Evidence instead of charging the whole capture as inline input.
## Build a bundle in one step
```sh
rea environment-build grafana.har ./grafana --json
agent-env run ./grafana
```
`rea environment-build <capture> [directory]` takes a `.har` file, or capture
Evidence JSON from `inspect-web-network-capture` (HAR) or
`capture-browser-scenario`, and writes the bundle
[Export a bundle](#export-a-bundle) describes. It inspects a HAR as
`inspect-web-network-capture` does (Evidence input skips this), projects the
environment manifest from that Evidence as retained Evidence, authenticates
the pair, prepares the state the bundle's server derives at startup, and
writes the bundle with the export. For the same capture path, every file it
writes is byte-identical to what `inspect-web-network-capture`,
`inspect-analysis-view` and `environment-export` write, Evidence ids included,
when `view.json` lists the same rules in the views' order and the export is
given the build's name. It reads one file and writes one folder: it fetches
nothing and launches no browser (HAR inspection runs REA's own decoder
process), so record a browser scenario with `capture-browser-scenario` first
and pass its Evidence. Other capture formats are refused, as the manifest view
refuses them, and a HAR document saved as `.json` is refused with a hint to
name it `.har`.
- `--template-rule <rule>`, repeated, replaces the default opt-in rules
`after-uid-literal`, `opaque-token`, `sibling-variance` and
`prefix-variance`, the rules the published capture-built environments use;
`--format-rules-only` applies none, the views' own default. Rules apply in
the views' fixed order, the report lists them, and a repeated rule is
refused as the views refuse it.
- `--name` names the env folder and env id. Without it the name is the
manifest's, unless the capture's origin is a loopback name or an IP literal:
the capture file then names it (`grafana.har` and `grafana.capture.json`
become `grafana`) and a `name-from-file` warning says so. A file name that
yields no portable label (kebab-case, at most 64 characters, not a Windows
device name) is refused; pass `--name`.
- `--package-tarball` works as for the export, and `--capture-timeout-ms`
as for `inspect-web-network-capture`.
- `--dry-run` runs every stage and prints the report with `status: "planned"`
and the files a build would write, but writes nothing and does not check the
directory, which may then be omitted. It reads and hashes a
`--package-tarball` as a build does before copying it, so it refuses the
tarballs a build refuses, including one whose contents cannot be read.
The directory must be absent or empty, as for the export; the build never
replaces a folder. A refusal before the write leaves nothing behind. A capture
Evidence file, an inspected HAR's Evidence and the projected manifest must each
fit the live heap headroom for parsing and sealing, checked before the file is
read or the Evidence serialized; one that does not is refused as
`resource_constraint`. Each
refusal names its stage: `environment-build` for the build's own checks,
`inspect_web_network_capture` and `inspect_analysis_view` for inspection and
projection, `environment-export` for the bundle. SIGINT cancels the build
(`cancelled`); before the bundle stage nothing is written, and a cancellation
during the write names the bundle it wrote. Stage progress is logged to stderr
at info level; the report is the only output on stdout:
```json
{
"status": "built",
"directory": "/work/grafana",
"name": "grafana",
"capture": {
"evidence_id": "ev_…",
"operation": "inspect_web_network_capture",
"records": 105,
"input": "har"
},
"manifest": {
"evidence_id": "ev_…",
"tools": 48,
"excluded_groups": 0,
"template_rules": [
"after-uid-literal",
"opaque-token",
"sibling-variance",
"prefix-variance"
]
},
"state": { "served": true, "evidence_id": "ev_…" },
"coverage": {
"reads": {
"state": 36,
"recorded": 6,
"recorded_reasons": { "query-varies": 3, "…": 1 }
},
"writes": {
"served": [{ "tool": "delete_folders_by_uid", "effect": "delete" }],
"withheld": [
{
"tool": "post_folders",
"effect": "create",
"reason": "unconfirmed",
"hint": "No later read showed the effect. Read the entity and its list after the write.",
"exemplar_ordinal": 94,
"contradicted_by": null
}
]
}
},
"tool_count": 37,
"package_source": { "kind": "registry", "specifier": "rea-agents@…" },
"files": [{ "path": "README.md", "bytes": 4210 }],
"warnings": [{ "code": "name-from-file", "message": "…" }],
"next": ["agent-env run /work/grafana"]
}
```
`coverage` is the state's read views and write plans summarized, and `null`
when the state is unavailable and the bundle serves replay. The
[environment coverage](web-network-captures.md#environment-coverage) view
reports the same summary for any capture without building, so an agent can
check it after each recording. The view summarizes the state as derived; it
does not apply the serve-time admission checks, so for an environment whose
served catalog exceeds the discovery budget, or whose state exceeds what
`data/add` loads, it still reports the state while the build reports `coverage:
null` and the bundle serves replay. Each withheld write carries a hint about
what to record next; hints never change what is served:
| Reason | Hint |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `no-state-target` | The write's path names no collection or item the capture reads. Read the collection or item it changes. |
| `no-call` | No recorded call of this tool was modelled, for example because every call failed. Record a successful call. |
| `body-unusable` | The retained request or response cannot be modelled. Record a successful call with a complete supported request and reproducible response. |
| `no-observation` | Nothing showed the entity or document this write changed. Read a created entity back by id right after the write; for an update or delete the state must already hold the entity, so read it before the write or record the create that made it. |
| `unexplained-change` | The write changed fields it did not send, in a way no rule explains. A second, different call of the same tool can show the pattern. |
| `contradicted` | A later read disagreed with the modelled effect. Check the read at contradicted_by. |
| `side-effect` | The write changed something besides its own entity, which the environment does not model. |
| `unconfirmed` | No later read showed the effect. Read the entity and its list after the write. |
| `incomplete-entities` | The write is confirmed, but the views that show its entity cannot render it in full. Read the written entity through every view that lists it. |
The bundle ships `evidence.json`, the whole capture Evidence, response bodies
included. Inspection excludes the values of the standard authentication headers
(`Authorization`, `Cookie` and `Set-Cookie` among them) and URL userinfo; a
custom key header such as `X-Api-Key`, a token in the query string and tokens
an application returns in bodies remain. The environment views refuse captures
inspected with `--sensitive-value`, so scrub a capture before building it. The
report's `credential-named-members` warning counts recorded header names, query
parameter names and JSON or form body members whose names look like
credentials (whole name segments such as `token`, `secret`, `password`,
`api_key`, `authorization` or `session`) and hold a value, and lists the first
20 by record and pointer, never by value. A body is read as a form or as JSON by
its recorded media type, or by its shape when it has none or one naming
neither. A browser scenario is
scanned by event (`event 4 response /token` names
`normalized_result.events.items[4]`): every retained request and response
content event, including a response whose request began before the browser was
attached and a transaction no record represents, and each transaction URL's
query once. Bodies larger than 4 MiB of UTF-8 text are not
scanned; `credential-scan-incomplete` counts them and lists up to 20 record or
event pointers, including base64 bodies whose decoded scan was cut. An empty name
warning does not establish that these bodies were checked. Names do not establish sensitivity,
so the build never refuses on them. Capture
Evidence also records the capture file's absolute path (`subject.local_path`,
`parameters.capture_path`, `locations`, `artifact.path`), and the bundle
carries it: build from a neutral folder when that path should not ship.
Over MCP, `export_environment_bundle` builds the same bundle and returns the
same report. Its `source` is capture Evidence the session retained
(`{"kind": "retained-evidence", "evidence_id"}`), such as a
`capture_browser_scenario` or `inspect_web_network_capture` result, or a
capture Evidence file (`{"kind": "evidence-file", "path"}`); inspect a HAR with
`inspect_web_network_capture` first. Retained Evidence is written into the
bundle as `--format json` writes it, so the bundle is the one
`environment-build` writes from that file. `output_directory` must be an
absolute path that does not exist yet, whose parent does; the tool creates it
and refuses an existing directory, empty or not. `name`, `template_rules`,
`package_tarball` and `dry_run` work as `--name`, `--template-rule`,
`--package-tarball` and `--dry-run` do, and retained Evidence has no file to
name the environment after, so a loopback or IP origin needs `name`. Unlike
`--dry-run`, `dry_run` still checks `output_directory`. Destination refusals
name `export_environment_bundle`; the others name their stage as
`environment-build`'s do.
## From a browser scenario
@@ -125,6 +308,51 @@ the replayed body is the bytes the browser exposes for each response, with any
declared secret replaced by its redaction marker (a body that redaction had to
empty replays no body).
To record writes, sign in through the UI and then send the application's API
calls with [request steps](browser-scenario-contract.md#request-steps): each
sends one request to the start origin through the page's own `fetch`, with the
session's cookies, and a header can carry the CSRF cookie or stored token the
application's own requests send. A write is served only when the capture shows
its effect, so read each collection before writing, read every written entity
back by id and in its list right after the write, and leave more than a second
between a create and its first edit. A request step's path is fixed when the
scenario is written and cannot use a value an earlier response returned, so an
id must be known before the run: one the caller chooses, such as a `uid` in the
create body, or one an earlier scenario created. When the server assigns ids,
read the list after each create (the create's response or the list shows the
new entity), and record updates and deletes in a second scenario that reads
the first one's objects by id before editing them, or make them through the
UI. The environment coverage view names each
write it withholds and what to record next. A scenario with write requests
changes the application's data: record against an instance whose data may
change.
```json
{
"step_id": "create-folder-1",
"action": "request",
"method": "POST",
"path": "/api/folders",
"headers": [
{
"name": "X-CSRFToken",
"value": { "source": "cookie", "name": "csrftoken" }
}
],
"json": { "title": "Scratch folder 1" },
"expected_status": [200, 201]
}
```
Each record takes its start and duration from the browser's timing (the time
the scenario observed the request where the browser reported no start), so the
state orders and overlaps
records as it does a HAR's and learns a write clock: an update the application
stamps with a modification time can be served. A scenario captured before
these times were recorded has none; its records keep request order and the
state learns no clock, so such updates stay `unexplained-change` until it is
recorded again.
## What a manifest is
An environment manifest is a projection of one application's recorded request
@@ -433,7 +661,7 @@ Each read tool answers according to its view in the state:
| View | Answer |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `item` | The entity the path arguments name, rendered into the recorded envelope of `envelope_ordinal`. Arguments match as replay matches them: each collection segment equals the held collection's segment as written or after percent-decoding, and the identity is the last argument percent-decoded, then as written; values are never re-split on `/`. Any entity the state holds answers, including one `data/add` loaded; query arguments only decide `replay.exact_match`, except that a call matching one of the view's `projected_query_keys`, or on a view with `projected_record_ordinals` asking for some fields only with a value other than `*` no checked line gave or one a replayed read gave, replays its recording. An entity the state does not hold answers the tool's latest recorded 400, 404 or 410 response with `state.view` `missing`, or is a tool error when none was recorded; one lacking a field every recording carried is a tool error, unless a read showed the application leaves that field out; a body is never invented. |
| `item` | The entity the path arguments name, rendered into the recorded envelope of `envelope_ordinal`, whose `per_response_members` (such as a request id), like a list's or a document's, keep their recorded values. Arguments match as replay matches them: each collection segment equals the held collection's segment as written or after percent-decoding, and the identity is the last argument percent-decoded, then as written; values are never re-split on `/`. Any entity the state holds answers, including one `data/add` loaded; query arguments only decide `replay.exact_match`, except that a call matching one of the view's `projected_query_keys`, or on a view with `projected_record_ordinals` asking for some fields only with a value other than `*` no checked line gave or one a replayed read gave, replays its recording. An entity the state does not hold answers the tool's latest recorded 400, 404 or 410 response with `state.view` `missing`, or is a tool error when none was recorded; one lacking a field every recording carried is a tool error, unless a read showed the application leaves that field out; a body is never invented. |
| `list` | When the arguments name one of the lines the view checked exactly (its latest recordings before the first related write; every path and query value equal to the recording's, and every recorded query parameter supplied), that recorded envelope with its array rendered from the state: every entity of the collection in state order for `membership: "all"` or a line `all_record_ordinals` names, or the recorded members still held. A call with no query argument answers from such a line of the collection its path arguments name that only pages its list, with `replay.exact_match` false. On a line the view's `query` answers, and for a call whose every query argument has a role there (an argument-less call aside), the answer is the query evaluated over the state: filtered, searched, sorted and paged, with counters at the number listed and `query.totals` at the number matched, and `state.membership: "query"`; such a call answers on a query or whole line of its collection with `replay.exact_match` false. A value an `inert` parameter never recorded, a parameter without a role, a call matching one of the view's `projected_query_keys`, or a field selection the checked lines do not show (as for `item`), replays its recording. A member lacking a field every recording carried makes the call a tool error rather than a partial list. |
| `document` | When the arguments name the document's checked line exactly, the state's value for it; a tool error when the state holds none. |
| `recorded` | The recording, as replay answers it. |
@@ -451,6 +679,16 @@ An item or list view that always spells a clock field otherwise than the
entity's stored value names that spelling in `clock_formats`, and a value it
never recorded, such as a created entity's clock, is rendered in it: a list that
writes `+00:00` where item reads write `Z` lists a new entity the same way.
A view that names a field in `sorted_arrays` lists that string array sorted, as
a read-back showed the application does, while the state keeps it in the order
it was written, so a write's own answer echoes it. If a later counted read
contradicts that ordering, the view stays recorded and its earlier reads no
longer confirm served writes. Members a view names in
`per_response_members`, such as a request id each response mints, keep the
value its recording carried. A list's `counters` and `query.totals` are
top-level members or members one level down in a paging object
(`/pageInfo/totalRows`); either is set to the number listed or matched, and
members beside it such as `isLastPage` stay as recorded.
A list or document call that names no checked line, including a line recorded
after a write or with other query values, answers from the recording. Every
@@ -480,20 +718,38 @@ tools are not served, so no call claims a change it does not make; each one's
plan states why in its `reason`. A served write changes the live state, which
every MCP session and `data/get` then see:
| Effect | Call |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `create` | Adds an entity to the collection the path names, built from the observed entity with each ruled field taken from the call or generated, at the plan's `insert` position. A plan tied to its exemplar's collection writes only there. |
| `update` | Sets the sent members on the entity the path names, constants for members accepted only with their recorded value, and the clock, increment, slug, unique and renewed changes the plan models. |
| `delete` | Removes the entity the path names. |
| `document` | Merges the body into the plan's document object, or replaces it for `PUT`. |
| Effect | Call |
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `create` | Adds an entity to the collection the path names, built from the observed entity with each ruled field taken from the call or generated, at the plan's `insert` position. A plan tied to its exemplar's collection writes only there. |
| `update` | Sets the sent members on the entity the path names, constants for members accepted only with their recorded value, and the clock, nested clock, increment, slug, stamp, derived, unique and renewed changes the plan models. |
| `update` (action) | A `POST`, `PUT` or `PATCH` to an item path plus an action segment (`/{type}/{id}/archive`) sets, on the entity before the segment, each scalar the recorded calls showed the action set (`set`), with any sent members; it is served only when a recorded call found those fields already set, so a toggle never is. |
| `delete` | Removes the entity the path names. |
| bulk `update` or `delete` | A `DELETE`, `PUT` or `PATCH` at a collection, or a `POST` to it plus `delete`, `remove` or `destroy`, applies its effect to every entity the body names, as recorded: an array of ids or of objects carrying the id field, at the root or as the body's one array member (`{"IDs": […], "Read": true}`). |
| `document` | Merges the body into the plan's document object, or replaces it for `PUT`. |
An update's unsent changes follow the plan's rules. A `held-clock` is a clock
the client sends back as it read it and the application stamps anew (Ghost's
`updated_at`): the call must send the entity's value and the field takes the
write's clock. A `nested-clock` sets the clocks inside an object the entity
holds, in that object's recorded format. A `stamp` writes the acting user
the entity already names under its `created…by` counterpart into a
`modified…by` or `updated…by` field. A `derived` field takes a value showing
the one sent member's text that the entity stores nowhere (a field Ghost
derives from `html`), only with that member's recorded value. An action's
`set` rule sets a scalar to the value the recorded calls left it at.
Its answer has the status and content type of the plan's recorded response,
with the written entity or its identity rendered in, no body, or the recorded
body. A call whose body is not an object, sends a member the capture did not
show stored, sends a fixed member with another value or a member of another
JSON type, or lacks a required member is a tool error naming the accepted
members; so is a rendered answer missing a field the recorded response
carried, and an update or delete of an entity the state does not hold answers
members; so is a call sending query parameters other than the exemplar's (they
may change what the body does), a body not wrapped in the exemplar's envelope
(Ghost's `{"posts": [{…}]}`), a bulk call naming an id the state does not hold,
a `held-clock` member sent with another value than the entity holds, a call
sending a `derived` rule's member to an entity that does not hold the value the
recorded call found, a nested clock on an entity holding no such object, and a
rendered answer missing a field the recorded response carried, and an update or delete of an entity the state does not hold answers
the recorded 400, 404 or 410 of the tool or of an item view of the type, if
any. A refused call leaves the state unchanged. Values a write generates are
deterministic: the clock starts at `write_clock` and advances one second per
@@ -502,7 +758,8 @@ largest they generated, and UUIDs and tokens derive from the state Evidence
id, tool, field and write count, skipping held values. `data/reset` and
`data/add` restart the clock, counter and sequences, so the same calls after a
reset give the same state. Answers carry `state: {"source": "state", "view":
"create" | "update" | "delete", …}` or `"document-write"`.
"create" | "update" | "delete", …}` or `"document-write"`; a bulk answer adds
`ids`, every entity it wrote in body order, with `id` naming the first.
The data plane serves three JSON-RPC methods:
@@ -657,6 +914,22 @@ after an update and referential rules on delete are not modelled. An exported
bundle carries no state file: its server derives the state at startup, as the
export does to predict what it serves.
Known limits of served writes:
- A recorded view answers from its recordings after a served write: Mailpit's
`get_messages`, recorded as `list-differs`, still lists a message a served
delete removed.
- Served n8n workflow updates accept any `versionId`, while served Ghost post
updates require `updated_at` to match the entity (a `held-clock`).
- A slug derived from a title can repeat across served creates with the same
title, where the application would make it unique.
- A served create's sequence id is the state's next value, not the gaps an
application's own sequence would leave.
- Nested clocks are served in the spelling of the write's own response, which
can differ from what a later read of the application shows.
- A served write to an entity that a replayed (unserved) create made answers
as for a missing entity, since the state never held it.
## Export a bundle
`rea environment-export` writes the same pair as a bundle folder the agentenv
+54
View File
@@ -34,6 +34,53 @@ One-shot CLI output remains complete. Older servers require the existing complet
request after schema discovery. APK authenticity and runtime behavior remain
unverified.
## Manifest component projection
`inspect_android_package` also returns a static inventory of declared
activities, aliases, services, receivers and providers from the decoded
manifest. `declared_name` preserves the source attribute; `name` resolves a
relative name only when the manifest package is known and the resolved copy
fits the UTF-8 byte budget. An unresolved package or exhausted name budget
retains the declared name and marks the inventory partial. Empty component
names and names with forbidden DEX characters retain their raw spelling and
are not treated as identities. Valid DEX spaces, including boundary spaces,
are preserved when resolving component names and matching alias targets.
Whitespace in the manifest package is rejected by its separate package grammar.
Component permissions inherit the application
permission when no component permission is declared, except an activity alias
with no permission: Android documents that case as requiring no permission
through the alias. `permission_source` distinguishes a local declaration,
inherited value, no requirement (including an explicit empty override), and a
value omitted because its resource/manifest-placeholder token is unresolved
or the total permission-byte budget was reached. Resource and placeholder
references are not expanded at this parser boundary. Repeated inherited
values share one inventory-wide byte budget.
Provider `android:readPermission` and `android:writePermission` overrides are
not projected as separate fields; their presence marks the inventory partial
and is called out in `limitations`. Relative component names are not resolved
against a manifest package with surrounding whitespace.
Class-name segments follow DEX `SimpleName` characters, including a leading
digit or hyphen, rather than Java source identifier rules. Relative-name
expansion does not prove class existence, DEX-version compatibility or runtime
loadability. SDK defaults accept JADX's decimal and hexadecimal integer
spellings; the original SDK declaration strings remain unchanged.
`exported` is the declared literal `true` or `false`, otherwise null.
`exported_default` is the documented default only where it can be established;
it remains null for unknown provider target SDKs, nonliteral declarations, or
components with filters targeting API 31 or later that omit the now-required
explicit declaration. A limitation records the latter validation error.
Provider authorities and all inventory dimensions are bounded. `status:
"partial"` and `limitations` identify omitted declarations or values; a
capacity limitation appears only when an actual collection limit was reached.
The projection does not merge manifests, expand resource references, or
observe runtime enabled/export state.
The service `foreground_service_type` field preserves the declared attribute,
including an empty value or surrounding spaces; `null` means the attribute is
absent or the component is not a service. This field does not validate service
type tokens or establish runtime eligibility.
## Supply tools
Use a **full JDK 17 or newer** (including its compiler module) and
@@ -193,6 +240,13 @@ the same source byte limit and explicit truncation reporting.
full reported display types, fields and inner class names. Synthetic members
that code generation hides can appear in the inventory; it stays consistent
after method decompilation within the retained session.
Class and method inspection and reference tracing accept either the exact
reported class name or its original binary name, including `$` in nested and
anonymous classes. The bridge keeps both identities from JADX; it does not
replace separators or guess suffix matches. Ambiguous selectors are refused.
Follow-up calls preserve the selected name, while results report the frozen
JADX display name. Quote `$` names in the shell, for example
`'com.example.Manager$WorkerThread'`.
Exact DEX prototypes are reported from frozen JADX method metadata as
`dex_descriptor`, for example `(I)V`. Display names can be JADX aliases;
the descriptor alone is not a complete raw DEX method ID. Overload indices
+8 -2
View File
@@ -239,13 +239,19 @@ serialized size of the report together with its limitations (which repeat
archive names) is then checked against an 8 MiB output budget (the exact check
is authoritative); an over-budget report is refused, not truncated.
The whole-file reader retains read chunks and a concatenated buffer; total
process memory exceeds the selected file's size. It also computes the complete
The whole-file reader allocates the selected snapshot and bounded read scratch;
total process memory exceeds the selected file's size. It also computes the complete
file SHA-256, so inspection is not header-only. Content digests are recomputed
at most once per SHA-256/SHA-512 algorithm during one inspection. Input,
inflation, decoded-record and serialized-output bounds keep work finite;
resource refusals are distinct from malformed format errors.
If the stable read completes but closing its descriptor fails or is cancelled,
the error retains `details.partial_observation` with the selected path, complete
file SHA-256 and byte count. These facts describe the acquired file; they do not
establish any decoded signing facts. Failed cleanup remains visible until the
provider releases the descriptor on a cleanup retry.
A file that is not a ZIP is refused as an unsupported target. A damaged ZIP
(local headers but no end-of-central-directory), a central directory that
cannot be walked or whose declared record count does not account for all of
+2
View File
@@ -100,6 +100,8 @@ one section or symbol, or a stable page without re-running the decoder. CLI
one-shot invocations pass portable inline Evidence JSON; an MCP session uses
the retained `evidence_id`. Page `limit` is required and bounded by the
measured MCP stdio budget documented on the tool contract.
An empty retained section or symbol collection has no valid item index. Its page
view can still return an empty page.
Malformed authenticated layout input remains rejected with the owning schema's
safe field diagnostics under `source.normalized_result`, including the field
path and type or value reason. Evidence authentication and decoder validation
+8
View File
@@ -94,6 +94,14 @@ content. Actual JSON properties with those names remain content. Reference-only
objects, unsupported values, and opaque cookie partition identities remain
incomplete. Previously exported fingerprints are not recomputed; recapture both
sides when comparing captures produced across this decoder change.
IndexedDB schema fingerprints with `projection_version: 2` include the observed
database version and object-store definitions. A missing, malformed, or imprecise
version leaves the schema fingerprint incomplete while retaining observed store
facts. Historical fingerprints without this marker covered object stores only:
matching historical hashes cannot establish unchanged database versions, and a
historical hash is not compared with a version-bearing hash. Recapture both
sides for a complete IndexedDB version comparison; other proven storage changes
remain reportable.
Script comparison groups observations by reported URL and compares unordered
content samples. Producer inventory keys include optional metadata, so a changed
+130 -6
View File
@@ -10,8 +10,10 @@ liveness deadlines. `INPUT_JSON` may be inline JSON or a JSON file path.
The minimal request contains only browser launch/connect selection, `start_url`,
and at least one `actions` entry. Launch `headless` defaults to `true` and can
be set to `false` where the host supports a visible browser. Navigations follow
the destinations named by the scenario and the page's ordinary browser requests;
there is no separate origin allowlist or request-interception layer. Environment
the destinations named by the scenario and the page's ordinary browser requests,
and [request steps](#request-steps) send only to the start URL's origin;
there is no separate origin allowlist. Request steps use a temporary dispatch
gate for cancellation; ordinary page requests retain their browser behavior. Environment
settings (including service workers blocked by default), empty storage, and a
final sanitized URL capture are supplied by default. Set
`environment.service_workers` to `allow` when the application requires them.
@@ -204,7 +206,8 @@ language environments.
HTTP(S) URLs may include ordinary query values and fragments directly. Use
ordered structured query entries when a value needs to reference a declared
environment-backed secret. Form, storage, and cookie values are either literal
strings or declared secret references. Raw URL userinfo credentials are rejected.
strings or declared secret references; request step headers can also read a
cookie or local storage value from the page. Raw URL userinfo credentials are rejected.
Captured credential-header
values are not retained, and declared secret values are redacted automatically.
A secret may be declared solely to
@@ -315,6 +318,113 @@ external browser without closing it. The request itself defines the operation;
there is no additional REA grant step. The browser and host still enforce their
own access and process rules.
## Request steps
A `request` step sends one HTTP request from the page with the page's own
`fetch`, so the browser attaches the session's cookies and the request is
recorded in the step's network events like any request the application makes.
Use it to record an application's API, including writes, when driving the UI
for each call would be slower or less exact than calling the endpoint the UI
calls. It is the only action that changes application state without a user
gesture, so run scenarios with write requests only against an instance whose
data you may change. This scenario creates one item:
```json
{
"browser": {
"mode": "launch",
"executable_path": "/absolute/path/to/chromium"
},
"start_url": { "url": "http://127.0.0.1:3000" },
"actions": [
{
"step_id": "create-item",
"action": "request",
"method": "POST",
"path": "/api/items",
"query": [
{ "name": "notify", "value": { "source": "literal", "value": "false" } }
],
"headers": [
{
"name": "X-CSRFToken",
"value": { "source": "cookie", "name": "csrftoken" }
},
{
"name": "Authorization",
"value": {
"source": "local_storage",
"key": "auth",
"json_pointer": "/token"
},
"prefix": "Bearer "
}
],
"json": { "title": "Scratch item 1" },
"expected_status": [201],
"timeout_ms": 10000
}
]
}
```
- `method` is `GET`, `HEAD`, `POST`, `PUT`, `PATCH` or `DELETE`. `json` is the
body, sent with `Content-Type: application/json` unless a header sets one; a
`GET` or `HEAD` step cannot carry it.
- `path` starts with one `/` and must resolve to the start URL's origin, which
validation checks before the browser starts. When the step runs, the page must
be on that origin, checked again in the page as the request is sent; add a
`goto` step first if an earlier action left it. A query in `path` is sent as
written (a fragment is not sent), and `query` entries are appended after it
in order and may reference declared secrets.
- Request steps follow no redirects: a redirect answer fails the step, and
nothing is sent to the location it names, though a write the redirect
answered may already have been applied. `expected_status` cannot name a 3xx
status. Give the path the redirect names; with header values selected, the
capture records its `Location`.
- Header values are literals, declared secrets, a cookie the browser holds for
the request URL (`{"source": "cookie", "name"}`, HttpOnly cookies included,
whose value the page's scripts can then see while the request runs), or a
local storage entry on the start origin (`{"source": "local_storage",
"key"}`, with an optional `json_pointer` to a string or number in a stored
JSON value). `prefix` is sent before the value. A value read from the page is
redacted like a declared secret, as `[REDACTED:page:<step_id>.<header>]`,
from records made after the step reads it; a response recorded earlier, such
as the login response that returned a token, keeps the value. A value read
again keeps its first marker, and a value shorter than 8 characters is not
redacted, since redacting it would rewrite ids, ports and hosts throughout
the capture. A value is redacted as stored and as `fetch` sends it, without
surrounding spaces and tabs. A missing cookie, key or pointer fails the step.
Reading a page value raises the estimated cost of redacting every later
body, so the bodies kept within `max_network_body_allocation_bytes` (1 MiB
by default) get smaller: after a 32-character `X-CSRFToken` value, a body over
about 47 KiB is kept as `unavailable`. Raise `capture.limits` when the
application's responses are larger.
- Validation refuses a header name given twice in a step, header names a
page's `fetch` cannot set, such as `Cookie`, `Origin`, `Host` or `Sec-*`, and
`User-Agent`, which Chrome drops; it refuses line breaks and other ASCII
control characters in literal values and prefixes, and the step fails before sending
when a secret or page value holds them.
- The step completes when the whole response has been read. With
`expected_status`, any other status fails the step and the later actions are
recorded as `cancelled`; without it, any response completes the step. A
network error fails the step. `timeout_ms` covers reading the header values
and the request: a step whose time runs out, or that is cancelled, before
its request is sent sends nothing, but a request already sent can still reach
the application after the step fails.
An absolute deadline also covers browser evaluation queued behind page work.
A temporary route checks cancellation again at network dispatch and is removed
when the request evaluation settles. A page controlled by a service worker
is refused because its fetch can bypass that dispatch gate; use the default
blocked service-worker policy for request steps.
The request runs in the page's main world, so an application that wraps
`fetch` sees it, and it goes through the same network observation as other
page requests: select network content (bodies or header values) to record it.
Captures with request steps state in `limitations` that the steps sent their
declared requests through the page's fetch. A scenario without request steps
is unchanged.
## Network evidence
`capture.events: ["network"]` retains request, response, completion, failure,
@@ -355,6 +465,17 @@ and unfinished-request metadata. Select content independently:
All three options default to `false`. Any selected network content also enables
network metadata observation; listing `network` in `events` is then optional.
Each observed request object receives a capture-scoped `transaction_id`.
Each `request` event carries `observed_at`, the wall-clock time REA observed the
request start. Playwright reports the browser's own start only once a request
ends, so each `request-finished` or `request-failed` event carries `started_at`,
the browser's wall-clock start, and `elapsed_ms`, its time from that start to
the end of the response, when the browser reports them. The environment views
time scenario records from these, as HAR entries are timed: the browser's start
where it reported one, else `observed_at`, which trails it by up to tens of
milliseconds while REA reads other responses, and a duration of 0 where the
browser reported none. Captures made before these fields existed lack them and
are read in record order. REA versions before them refuse captures that carry
them. Scenario comparisons ignore all three (`ignore_network_request_timing`).
Requests sharing a URL remain distinct. Redirects link to the preceding
transaction only when it was observed; a null predecessor does not establish
that no earlier hop existed. Request events require both transaction fields;
@@ -452,8 +573,8 @@ and compared as an unordered collection after caller rules apply, because
concurrent responses arrive in any order and can land in a later step; a
redirect retains the complete predecessor ancestry, including each hop's own
request, response outcome, and selected network content. Each transaction's
capture-local IDs, sequence/index fields, and content source-sequence anchor
are removed. Caller rules normalize retained strings before hashing. The
capture-local IDs, sequence/index fields, content source-sequence anchor and
request times (`observed_at`, `started_at`, `elapsed_ms`) are removed. Caller rules normalize retained strings before hashing. The
`preserve_redirect_transaction_ancestry` built-in rule fingerprints each hop
as canonical SHA-256 of `{ events, redirected_from }`: `events` is the sorted
list of its normalized event SHA-256 digests, and `redirected_from` is its
@@ -474,7 +595,10 @@ scope/flags, value fingerprints, and duplicate entries even when redaction or
literal rules make names collide. Captured storage arrays retain their original
order. The `compare_storage_entries_as_unordered_collection` built-in rule
participates in the normalization commitment; new storage comparison digests
therefore differ from earlier order-sensitive comparisons.
therefore differ from earlier order-sensitive comparisons. The
`ignore_network_request_timing` built-in rule likewise joins the commitment, so
normalization digests differ from comparisons made before it, though captures
with and without request times compare alike.
Each aligned step reports changed, unchanged, or unknown. Changed and unknown
action, screenshot, DOM, accessibility, URL, history, storage, and event
+78 -1
View File
@@ -236,6 +236,16 @@ Formats 2, 3 and 4 retain header, directory and integrity facts. Compact deliver
keeps complete Evidence in the requested bundle. See [Godot PCK](godot-pck.md)
for embedded packs, batch queries and unknown encrypted coverage.
Inspect a Node.js SEA carrier without running it:
```sh
rea inspect-artifact ./application --json
rea extract-artifact ./application --json
```
See [Node.js SEA](node-sea.md) for supported layouts, script and asset paths,
fuse observations, and the real-fixture verification lane.
Inspect local model declarations without loading weights:
```bash
@@ -321,6 +331,22 @@ The workflow reports first-line metadata, process/exception/termination facts,
threads, usedImages, lossless integer registers, and documented crash classes
when `bug_type` is `"309"`. Other DiagnosticReports types return no crash
result. See [Apple IPS crash reports](apple-ips.md).
Inspect Linux kernel module metadata without executing the module or requiring
`modinfo`:
```bash
rea inspect-kernel-module /absolute/path/to/module.ko --json
```
The workflow reports ordered `.modinfo` keys (duplicate `alias` and `parm`
entries preserved), `__versions` symbol CRCs, presence of
`.gnu.linkonce.this_module` and `__ksymtab` sections, observed `.init.text`
and `.exit.text` sizes, and an appended signature trailer. `struct module` is
not decoded and the signature is not validated. Other ELF types are not
applicable. gzip, xz, and zstd containers are unsupported and name
`external_decompression` as the next probe: decompress with an external
utility, then inspect the decompressed file. See
[Linux kernel modules](kernel-modules.md).
Inspect a CPython 3.7–3.13 `.pyc` header and nested marshal code objects
without executing bytecode or installing Python:
@@ -375,7 +401,7 @@ Run `rea --help` or a command's `--help` for arguments and output options.
The [generated catalog](mcp-contracts.md#generated-catalog) lists all CLI commands and MCP
tools. Provider-specific target support is described in the
[native guide](native-investigation.md), [DOS guide](ghidra-dos.md),
[raw binary guide](raw-binary.md), and
[raw binary guide](raw-binary.md), [GameCube DOL guide](ghidra-dol.md), and
[Windows Ghidra guide](windows-ghidra-p0.md).
## Choose a provider
@@ -746,6 +772,31 @@ like a blank `MCP_HOST`. The bound URL is written to stderr as one JSON log line
[agent environments](agent-environments.md) for what the state and replay
return and do not establish.
## Build an environment bundle
`rea environment-build <capture> [dir] [--name <name>] [--template-rule <rule> ...]
[--format-rules-only] [--package-tarball <path>] [--capture-timeout-ms <ms>] [--dry-run]` builds the bundle
`environment-export` writes from one recorded capture: a `.har` file, which it
inspects as `inspect-web-network-capture` does, or capture Evidence JSON from
`inspect-web-network-capture` (HAR) or `capture-browser-scenario`. It projects
the environment manifest from the in-memory Evidence with the opt-in rules
`after-uid-literal`, `opaque-token`, `sibling-variance` and `prefix-variance`
unless `--template-rule` or `--format-rules-only` selects others, prepares the
state, and writes the bundle; every file is byte-identical to the three-step
path's when that path lists the same rules in the views' order and exports
under the build's name. A loopback or IP-literal capture is named after its file unless `--name`
is given. It prints one JSON report: the capture and manifest identities, the
state, a `coverage` summary of which tools answer from the state, which replay
and why each write is withheld with a hint, the files, warnings and the next
command. `--dry-run` writes nothing and may omit the directory.
```sh
rea environment-build grafana.har ./grafana --json
```
See [agent environments](agent-environments.md#build-a-bundle-in-one-step)
for the report, the hints and what the bundle ships.
## Export an environment bundle
`rea environment-export <manifest.json> --evidence <capture.json> <dir>
@@ -921,6 +972,10 @@ server resources. It preserves a primary listener diagnostic, or a
tool count and Evidence identifiers. The successful `stopped` document is
returned only after cleanup is confirmed. The same in-process server owner
retains failed stages for retry; the CLI exposes no separate retry command.
`rea environment-build` is one-shot too: it prints its report and exits `0`,
or prints the refusing stage's structured error and exits `1`, writing nothing
when the refusal comes before the bundle is written. SIGINT cancels it with a
`cancelled` error; a cancellation before the bundle stage writes nothing.
`rea environment-export` is one-shot: it prints the written files and exits
`0`, or prints a structured `invalid_request` error and exits `1` for the same
pair refusals, a non-empty or file-occupied directory, a missing package
@@ -997,3 +1052,25 @@ Counting retains the existing bounded formatted-string allocation after capacity
admission; omit the flag to stream large JSON documents without materializing
the complete formatted text. Exhausted token windows display an empty range at
the total token count and have no next offset.
### `inspect-devicetree`
Inspect an explicit local FDT v16/v17 blob without execution or an external tool.
Returns header, reservations, node/property tree, derived standard values and
selected phandle references. See [devicetree inspection](devicetree.md) for
limits and retained-view pagination.
```bash
rea inspect-devicetree /artifacts/board.dtb --json
rea inspect-devicetree /artifacts/board.dtb --detail summary --evidence_output /artifacts/board-evidence.json --json
```
### PE TLS and load configuration
```sh
rea inspect-pe-load-config /absolute/path/to/application.exe --json
```
Reports TLS callbacks and size-covered load-config fields without execution.
Use `--max-callbacks` and `--max-file-bytes` to set read budgets. See
[PE TLS and load configuration](pe-load-config.md).
+105
View File
@@ -0,0 +1,105 @@
# Flattened devicetree inspection
`inspect_devicetree` / `rea inspect-devicetree` reads a local DTB without an
engine, target execution or external tools. It supports FDT versions 16 and 17;
other versions return `unsupported_target`. Corrupt recognized blobs return
`invalid_request`, and exhausted budgets return `resource_constraint` without
partial success.
```bash
rea inspect-devicetree /artifacts/board.dtb --json
rea inspect-devicetree /artifacts/board.dtb --detail summary --evidence_output /artifacts/board-evidence.json --json
```
The complete Evidence includes the artifact SHA-256, validated header fields,
64-bit memory reservations as exact hexadecimal strings, and a flat node tree
with explicit parent paths. Each property retains its file offset, byte length,
and up to 256 raw bytes as hex. `truncated` qualifies the preview only; parsing
coverage includes the entire declared tree. Version 16 has no declared structure
size, so `header.size_dt_struct` is null.
Derived fields carry `confidence: derived`: standard ASCII string lists, scalar
phandles and cell counts, and `reg` tuples using the parent's `#address-cells`
and `#size-cells` (defaults 2 and 1). Zero-cell components are null (omitted), distinct from an encoded zero. Cell
counts above four, invalid counts and incomplete tuples remain undecoded with a
limitation. Addresses are bus-local; no `ranges` translation is attempted.
Invalid or ambiguous strings remain raw, rather than being guessed.
The summary is labeled derived and includes root compatible/model, chosen
bootargs/stdout-path and aliases. An undecodable alias retains its name with a
null path; it is not silently treated as absent. Single-cell `interrupt-parent`, `remote-endpoint` and `msi-parent`
references resolve through phandle declarations. Missing or duplicate targets
have null target paths. A node whose `phandle` and `linux,phandle` disagree
adds a limitation, and references to either value stay unresolved. A present
`phandle` that is not one cell also adds a limitation; `linux,phandle` cannot
resolve that node when the canonical declaration is malformed. Binding-specific
reference arrays and overlays are not resolved; an empty reference list does not
prove absence of hardware links.
MCP `detail: summary` retains complete Evidence in the session. Follow its
`parent_evidence_id` with `inspect_analysis_view`:
```json
{
"source": { "kind": "retained-evidence", "evidence_id": "ev:<digest>" },
"view": {
"kind": "devicetree",
"collection": "properties",
"path": "/serial@1000",
"offset": 0,
"limit": 32
}
}
```
Collections are `nodes` (identity and property counts), `properties` (requires an
exact node path), `reservations`, and `references`. Pages use retained parsed
facts and do not reopen the file. Each page carries fixed-size context
(artifact, header, coverage and counts) rather than the summary strings, so a
large `bootargs` value does not repeat on every page. Source indices are scoped to the selected
collection and parent Evidence; property indices are scoped to their node.
| Input budget | Default | Ceiling |
| ---------------- | ------: | ------: |
| `max_file_bytes` | 4 MiB | 16 MiB |
| `max_nodes` | 8,192 | 65,536 |
| `max_properties` | 32,768 | 262,144 |
| `max_depth` | 64 | 256 |
Decoded names, paths, values and row overhead share a fixed 32 MiB representation
allowance, charged before expansion. It has no runtime override.
The reservation count also uses `max_properties`. CLI flags use kebab case,
for example `--max-nodes 16384`. View pages contain at most 256 rows.
## Verification
Synthetic tests cover v16/v17, malformed headers/blocks/names/tokens, resource
limits, derived values, and CLI/MCP delivery including retained pages after
removing the input. The optional real lane requires **dtc 1.8.1**:
```bash
npm run build:cached
node scripts/verify/firmware/devicetree.mjs
```
It compiles a DTS with reserved-memory, chosen, aliases and phandles, compares
REA's node paths against `dtc -I dtb -O dts`, and checks the derived report.
`REA_DTC_PATH` selects the verification executable; it is not runtime setup.
The dtc 1.8.1 lane ran on macOS arm64 with Node 26.5.0. Its seven node paths
matched the decompiled tree. The report included `model: "REA DTB verification"`,
`bootargs: "console=ttyS0"`, alias `serial0 -> /serial@1000`, reservation
`0x80000000 + 0x1000`, and a resolved reference from `/consumer` to
`/serial@1000`. The deliberately minimal remote-endpoint fixture produces dtc
graph-binding warnings; this lane verifies FDT structure and selected property
derivations, not hardware-binding validity.
A repeat run of that complete compile/decompile/CLI verification took 2.13 s
elapsed, 1.52 s user CPU and 0.27 s system CPU under `/usr/bin/time -l`. This is a
small fixture with warm filesystem caches, not a cold-start or large-board
benchmark. Peak process-family memory and whole-workflow token usage were not
measured; the sandbox denied the memory-reporting system query. No pre-feature
equivalent REA workflow exists for comparison. The pinned Node 24.22.1/npm
11.16.0 toolchain was unavailable; dependency installation used the local cache
with Node 26.5.0/npm 12.2.0 instead.
+5
View File
@@ -30,6 +30,11 @@ compression type leaves section names unknown and adds a limitation; note bytes
remain independently examined. Each `entries[]` item reports owner, type,
descriptor SHA-256, and a hex preview for descriptors of 64 bytes or less.
A section name also stays null when its offset is outside the declared string
table or its bytes lack a NUL terminator. A limitation explains that missing
name. Note coverage remains complete when all declared note tables and entries
were examined; an unavailable section name does not erase those note facts.
For GNU notes, `gnu.build_id_hex`, `gnu.abi_tag`, or `gnu.gold_version` are
filled when the descriptor matches that type. Other owners keep `gnu` null.
+7
View File
@@ -91,6 +91,13 @@ nothing, and `fields: []` returns only `source_index`. Offset/limit page matchin
rows in original order (defaults 0/64). Unfiltered pages slice the retained
collection; queries do not decode the file again.
For `chunks`, `names` matches the four-character IFF chunk tag (for example
`"AtU8"`, `"Code"`, `"ExpT"`); available `fields` are `index`, `name`,
`location`, and `payload`. For `lambdas`, `names` matches the resolved function
name from the atom table; `null` names (unresolved atom indices) are never
selected by a name filter. For `atoms`, `names` matches the decoded atom name
string.
`beam` keeps the module, form/Code bounds, original source coverage, and table
counts. A null count means no table was decoded; zero means a decoded empty
table. Missing tables and unresolved atom names remain explicit, and an empty
+7
View File
@@ -285,3 +285,10 @@ REA_FIRMWARE_VERIFY_GHIDRA=1 npm run verify:firmware
`scripts/rea.mjs` to verify the packaged CLI/MCP workflow. Real coverage here is
generated gzip/USTAR/ext4 with a Linux x64 ELF child; SquashFS, UBIFS, proprietary
firmware, other hosts and architectures remain unverified.
## Inspect extracted devicetrees
Use [`inspect_devicetree`](devicetree.md) / `rea inspect-devicetree` on an
extracted DTB for its node/property tree, reservations, hardware summary and
selected phandle relationships. FDT v16/v17 inspection is portable and requires
no external tool or disassembler.
+129
View File
@@ -0,0 +1,129 @@
# GameCube DOL analysis through Ghidra
This lane imports 32-bit big-endian PowerPC GameCube/Wii DOL images through
Ghidra. It does not execute the image. DOL is a headerless format with no magic
number, so REA never detects it: the caller must select it explicitly.
Only the cached MEM1 window `0x80000000..0x81800000` is admitted. Wii MEM2
and uncached aliases are outside this lane.
Not covered: small-data anchors (`r2`/`r13`), REL, RPX, and IDA or Hopper
support. See [Not supported yet](#not-supported-yet).
## Select the format
CLI:
```bash
rea inspect /absolute/path/to/main.dol --target-format gamecube-dol --provider ghidra --json
rea function /absolute/path/to/main.dol 0x80003130 --target-format gamecube-dol --provider ghidra --json
```
MCP, with the existing `open_binary` tool:
```json
{
"path": "/absolute/path/to/main.dol",
"provider_id": "ghidra",
"format": "gamecube-dol"
}
```
The architecture comes from the DOL header (`powerpc`). Do not pass
`architecture` with `gamecube-dol`; it is rejected. `entry_point` is optional,
and if given it must equal the header entry.
Omitting `format` never imports a file as a DOL. Detection does not run for DOL.
## What the import does
REA reads the 256-byte DOL header at admission. It checks every section's file
range and load address. It records BSS/data overlaps and computes the exact
uninitialized BSS gaps once. Malformed headers are refused before Ghidra starts.
Ghidra then runs in two steps:
1. The standard `BinaryLoader` maps the whole file at base `0x0`, with
`-processor` and `-cspec` taken from the language selection below.
2. The pre-script `bridge/ghidra/ReaGhidraPrepareDol.java` reads a section file
that REA wrote from the parsed header, with versioned collection counts. It creates one initialized block per
used text and data section at its load address, with file bytes from the
section's file offset. The whole-file block is removed before the section
blocks are created. Text is read-execute and data is read-write. It sets the
entry point and labels the entry
`__start`.
Sections that the header marks unused are not mapped. The 256-byte header and
any bytes between sections are not mapped either.
## BSS
A DOL has one BSS range, and it is not always disjoint from the data sections.
The domain parser computes the parts of BSS that no data section covers. Both
the pre-script and load-image attestation consume those parsed ranges. The
pre-script creates uninitialized read-write blocks only in those gaps. Where BSS overlaps a data slot, that slot
keeps its file bytes. Overlapping slot indexes are reported by the parser.
## Bound language
The default language is `PowerPC:BE:32:default` with compiler spec `default`,
which ships with Ghidra. The load-image attestation checks the language Ghidra
reports, not only the requested one.
### Optional Gekko/Broadway override
`REA_GHIDRA_LANGUAGE_ID=PowerPC:BE:32:Gekko_Broadway` selects the Gekko/Broadway
variant. The language is not in stock Ghidra. It comes from the GameCubeLoader
extension, by Cuyler, licensed under Apache-2.0. The extension ships
`ppc_gekko_broadway.ldefs` under its `data/languages` directory and must be
installed in the Ghidra installation's `Ghidra/Extensions` directory. The stock
lane never requires it.
Any other language id must start with `PowerPC:BE:32:`. Other languages are
refused with a specific constraint.
## Paired-single limitation
The stock `PowerPC:BE:32:default` language does not decode the paired-single
instructions that Gekko and Broadway add. Those words decode as bad or undefined
instructions. Use the Gekko override to decode them.
## Verification
The source-owned fixture is built from `tests/conformance/c/ppc.c`,
`tests/conformance/c/dol.c`, `tests/conformance/ghidra/ppc-probe.S` and the
linker script `tests/conformance/ghidra/dol-fixture.ld`. It is converted to a DOL
by `tests/conformance/ghidra/dol-fixture.mjs`. Its text is at `0x80003100`, and
its single BSS range overlaps data slot 1 and leaves a gap. The fixture is
never executed. The real GameCube or Wii game images are never used.
```bash
GHIDRA_INSTALL_DIR=/absolute/path/to/ghidra npm run verify:ghidra:dol
```
The lane needs the same toolchain as the PowerPC ELF lane: `REA_PPC_CLANG`,
`REA_PPC_READELF` and LLD. An independent DOL header reader and GNU readelf
(run on the source ELF) check the section table, file bytes, entry and BSS gap
before Ghidra starts. The checks never use REA's parser.
After import, the lane checks the load-image attestation (`verified`), the
bound language, the `__start` entry procedure, a direct call to the leaf
function, decompilation, and bytes at the data, tail, marker and BSS-gap
addresses. It also checks CLI identity.
The Gekko pass runs only when `PowerPC:BE:32:Gekko_Broadway` is in the Ghidra
installation's language catalog, the same catalog REA uses to admit
`REA_GHIDRA_LANGUAGE_ID`. A language that exists only in a user extension under
`~/Library/ghidra` is not admitted, so the lane prints
`skipped: PowerPC:BE:32:Gekko_Broadway (not in the Ghidra installation catalog that REA admits); stock lane still verified`
and still succeeds on the stock checks.
The ELF lane (`npm run verify:ghidra:ppc`) is separate. See
[PowerPC ELF analysis through Ghidra](/ghidra-ppc).
## Not supported yet
- Small-data anchors: `r2` and `r13` are not seeded from the DOL header.
- REL (relocatable modules) and RPX (Wii U) are not admitted.
- IDA and Hopper refuse `gamecube-dol`. Ghidra is the only provider.
- Disc images (ISO/GCM) are not supported. Pass an extracted DOL file.
+12 -7
View File
@@ -29,13 +29,18 @@ point using the available structural bytes and actual file length. It reports
those DOS facts separately from Windows-header evidence. A recognized PE
signature remains PE; recognized NE, LE, and LX headers are explicitly
unsupported. A nonzero Windows-header pointer without a recognized signature
remains an unresolved candidate, even when the DOS structure validates:
automatic target selection fails closed and reports both observations.
REA does not infer DOS intent from padding or infer a damaged Windows header
from the pointer alone. Inventory classification is a bounded-prefix format
hint; it returns `unknown` for that candidate or when its prefix cannot
establish the format. That result is distinct from complete DOS validation and
does not decide target admission.
usually remains an unresolved candidate, even when the DOS structure validates:
automatic target selection fails closed and reports both observations. When a
nonempty relocation table ends inside a complete DOS header, the remaining
header bytes belong to DOS padding; a nonzero dword there is not a Windows
declaration. An unread signature beyond the current probe is requested before
that padding rule applies. This avoids manufacturing a Windows header from
original DOS padding while preserving rejection of damaged Windows
declarations.
Inventory classification is a bounded-prefix format hint; it returns `unknown`
for an unresolved candidate or when its prefix cannot establish the format.
That result is distinct from complete DOS validation and does not decide target
admission.
Ghidra imports a private target snapshot with `MzLoader`, language
`x86:LE:16:Real Mode`, and compiler specification `default`. These settings,
+13 -4
View File
@@ -24,7 +24,8 @@ specific unsupported constraint rather than guessing a compatible profile.
A second admission covers recordless legacy firmware images
([#1620](https://github.com/morluto/rea/issues/1620)): ELF32 `ET_EXEC` headers
declaring `EF_MIPS_ARCH_32` or `EF_MIPS_ARCH_32R2` with an unspecified ABI
declaring `EF_MIPS_ARCH_1` (MIPS-I, a zero ISA field), `EF_MIPS_ARCH_32` or
`EF_MIPS_ARCH_32R2` with an unspecified ABI
field, `EF_MIPS_ABI2` clear, and an _inspected_ absence of any ABI flags
record (a target whose record was merely not observed stays refused). For
these images o32 is an analysis assumption derived from the ELF32/ET_EXEC
@@ -40,9 +41,17 @@ refusal. A header that declares an ABI field but carries no record is still
refused. Two further boundaries apply: an inspection that could not examine
every ABI declaration source (for example a carved ELF whose section table
extends past the captured bytes without a program-header record) does not
establish the recordless admission, and an `EF_MIPS_ARCH_32` header that
_declares_ an ABI is refused rather than relabeled, because the declared-ABI
lane is verified for `EF_MIPS_ARCH_32R2` headers only.
establish the recordless admission, and an `EF_MIPS_ARCH_1` or
`EF_MIPS_ARCH_32` header that _declares_ an ABI is refused rather than
relabeled, because the declared-ABI lane is verified for
`EF_MIPS_ARCH_32R2` headers only. The recordless MIPS-I lane pins the same
generic 32-bit language, which also decodes later MIPS32 instructions; the
session states that conformance to the MIPS-I subset is not established from
the header alone, and names the committed language when the caller selected a
variant instead of the pin. A committed profile is bound to the current
target's lane and captured ELF metadata before startup; reusing a profile
resolved for the other recordless lane is refused rather than exporting
attribution for a target it did not describe.
Explicit `EF_MIPS_FP64` and `EF_MIPS_NAN2008` declarations are refused. Their
absence is insufficient: Clang can emit identical `e_flags` for FP32, FP64 and
+2 -1
View File
@@ -3,7 +3,8 @@
This lane extends native ELF analysis to 32-bit big-endian PowerPC; it does
not execute PowerPC code. It is the first milestone of
[#1741](https://github.com/morluto/rea/issues/1741). GameCube/Wii DOL
executables, PPC64 and other PowerPC variants are separate follow-ups.
executables are covered by [GameCube DOL analysis](ghidra-dol.md). PPC64 and other
PowerPC variants remain separate follow-ups.
## Admission boundary
+12
View File
@@ -36,6 +36,18 @@ provider stays bound to the target; runtime failure never selects another
provider automatically. The contracts below describe Ghidra observations,
not equivalence with another engine's pseudocode or analysis database.
## Request payload ceiling
Ghidra requests use `REA_BRIDGE_MAX_REQUEST_BYTES`: default 16 MiB, with a
configurable range of 7,464–536,870,887 bytes. REA measures UTF-8 JSON, including
escaping, before allocating the serialized request or sending it. The framing
newline is excluded. An oversized request returns `resource_constraint` with
the effective setting, its source, and the exact payload byte count.
Set the value in the environment that starts REA, then restart REA to apply it.
This setting is enforced by the REA client; the Java bridge has no corresponding
request ceiling.
## Admitted inventory semantics
| Concern | Ghidra contract |
+5 -4
View File
@@ -62,10 +62,11 @@ Collections are `sections`, `images`, `types`, `methods`, `fields`,
`generic_method_instances` and `generic_method_issues`. Every returned row includes
its source `index`, scoped by artifact SHA-256 and collection. All collections
accept batch `indices` and `fields`. Exact `names` filtering is supported for
`sections`, `images`, `types`, `methods`, `fields`, `properties`, `events` and
`parameters`; native `return_types`, `field_types` and `parameter_types` rows
are selected by source indices (or `method_indices` for `parameter_types`),
not rendered names. Types also accept `image_indices`, `namespaces` and
`sections`, `images`, `types`, `methods`, `fields`, `properties`, `events`,
`parameters`, `return_types`, `field_types`, `parameter_types` and
`generic_method_instances`. Native type names match the retained rendered display value exactly; every same-name row
keeps its source index, and an unavailable (`null`) name never matches a string.
Types also accept `image_indices`, `namespaces` and
`declaring_types`; methods, fields, properties and events accept
`declaring_types`. Parameters accept exact `method_indices`, so a batch of
method indices retrieves their parameters without reading the entire parameter
+14 -13
View File
@@ -47,19 +47,20 @@ For runtime instrumentation, see the [Frida guide](./frida-instrumentation.md).
## Choose a guide
| Task | Guides |
| --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Identify a desktop installation | [Runtime candidates, evidence and follow-up files](desktop-applications.md) |
| Inspect native functions, references, or types | [Native binaries](native-investigation.md), [Go metadata](go-binaries.md), [Rust dependencies](rust-binaries.md), [CPython pyc files](python-pyc.md), [PE certificates](pe-certificates.md), [PE imports](pe-imports.md), [ELF notes](elf-notes.md), [Apple IPS](apple-ips.md), [Erlang BEAM](erlang-beam.md), [offline ELF layout](binary-diagnostics.md), [recorded crashes](recorded-crashes.md) |
| Select a native provider or import format | [Ghidra semantics](ghidra-provider.md), [Windows](windows-ghidra-p0.md), [DOS](ghidra-dos.md), [raw bytes](raw-binary.md), [MIPS](ghidra-mips.md), [PSP](ghidra-psp.md), [NativeAOT](ghidra-nativeaot.md), [IDA](ida-provider.md), [Kuna](kuna-provider.md), [JEB](jeb-analysis.md) |
| Analyze JavaScript or Electron artifacts | [Static analysis](javascript-artifact-reconstruction.md), [tracing and comparisons](javascript-application-workflows.md), [application graph](javascript-application-graph.md), [source recovery](javascript-recovery.md) |
| Compare static JavaScript with runtime observations | [Runtime attribution](javascript-runtime-observation.md), [reconciliation](javascript-runtime-reconciliation.md), [Electron observation](electron-observation.md), [Electron fuses](electron-fuses.md) |
| Inspect assemblies, packages, or firmware | [Godot PCK](godot-pck.md), [.NET](managed-code-analysis.md), [Python packages](python-packaged-applications.md), [Android](android-analysis.md), [APK signatures](apk-signature.md), [Apktool resources](apktool-resource-analysis.md), [ADB acquisition](adb-device-analysis.md), [Apple](apple-application-analysis.md), [Harmony](harmony-analysis.md), [firmware](firmware-analysis.md), [WASM](wasm-artifacts.md), [EVM](evm-bytecode.md) |
| Observe or compare a browser scenario | [CDP observation](browser-observation.md), [scenarios](browser-scenario-contract.md), [runtime evidence](web-runtime.md), [historical captures](web-network-captures.md) |
| Serve recorded behavior as an agent environment | [Agent environments](agent-environments.md) |
| Trace captured website source | [Source locations](web-source-location.md), [module traces](website-module-trace.md), [script export](website-script-export.md) |
| Capture and compare a command | [Process capture](process-capture.md) |
| Use CLI/MCP and retain evidence | [CLI](cli.md), [MCP contracts](mcp-contracts.md), [agent prompts](mcp-prompts.md), [obligation ledgers](reconstruction-obligation-ledgers.md) |
| Task | Guides |
| --------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Identify a desktop installation | [Runtime candidates, evidence and follow-up files](desktop-applications.md) |
| Inspect native functions, references, or types | [Native binaries](native-investigation.md), [Go metadata](go-binaries.md), [Rust dependencies](rust-binaries.md), [CPython pyc files](python-pyc.md), [PE certificates](pe-certificates.md), [PE imports](pe-imports.md), [PE TLS/load config](pe-load-config.md), [ELF notes](elf-notes.md), [Linux kernel modules](kernel-modules.md), [Apple IPS](apple-ips.md), [Erlang BEAM](erlang-beam.md), [offline ELF layout](binary-diagnostics.md), [recorded crashes](recorded-crashes.md) |
| Inspect native functions, references, or types | [Native binaries](native-investigation.md), [Go metadata](go-binaries.md), [Rust dependencies](rust-binaries.md), [CPython pyc files](python-pyc.md), [PE certificates](pe-certificates.md), [PE imports](pe-imports.md), [PE TLS/load config](pe-load-config.md), [ELF notes](elf-notes.md), [Apple IPS](apple-ips.md), [Erlang BEAM](erlang-beam.md), [offline ELF layout](binary-diagnostics.md), [recorded crashes](recorded-crashes.md) |
| Select a native provider or import format | [Ghidra semantics](ghidra-provider.md), [Windows](windows-ghidra-p0.md), [DOS](ghidra-dos.md), [raw bytes](raw-binary.md), [GameCube DOL](ghidra-dol.md), [MIPS](ghidra-mips.md), [PSP](ghidra-psp.md), [NativeAOT](ghidra-nativeaot.md), [IDA](ida-provider.md), [Kuna](kuna-provider.md), [JEB](jeb-analysis.md) |
| Analyze JavaScript or Electron artifacts | [Static analysis](javascript-artifact-reconstruction.md), [tracing and comparisons](javascript-application-workflows.md), [application graph](javascript-application-graph.md), [source recovery](javascript-recovery.md) |
| Compare static JavaScript with runtime observations | [Runtime attribution](javascript-runtime-observation.md), [reconciliation](javascript-runtime-reconciliation.md), [Electron observation](electron-observation.md), [Electron fuses](electron-fuses.md) |
| Inspect assemblies, packages, or firmware | [Godot PCK](godot-pck.md), [.NET](managed-code-analysis.md), [Python packages](python-packaged-applications.md), [Android](android-analysis.md), [APK signatures](apk-signature.md), [Apktool resources](apktool-resource-analysis.md), [ADB acquisition](adb-device-analysis.md), [Apple](apple-application-analysis.md), [Harmony](harmony-analysis.md), [firmware](firmware-analysis.md), [WASM](wasm-artifacts.md), [EVM](evm-bytecode.md) |
| Observe or compare a browser scenario | [CDP observation](browser-observation.md), [scenarios](browser-scenario-contract.md), [runtime evidence](web-runtime.md), [historical captures](web-network-captures.md) |
| Serve recorded behavior as an agent environment | [Agent environments](agent-environments.md) |
| Trace captured website source | [Source locations](web-source-location.md), [module traces](website-module-trace.md), [script export](website-script-export.md) |
| Capture and compare a command | [Process capture](process-capture.md) |
| Use CLI/MCP and retain evidence | [CLI](cli.md), [MCP contracts](mcp-contracts.md), [agent prompts](mcp-prompts.md), [obligation ledgers](reconstruction-obligation-ledgers.md) |
For development, use [testing](testing.md), [client verification](client-verification.md),
[tool design](tool-design.md), [admission limits and recovery](design/limits-and-recovery.md),
+7 -3
View File
@@ -997,9 +997,13 @@ Target admission preserves recognized PE/NE/LE/LX signatures separately from
reader layout support. A Windows header overlapping the first 64 DOS-header
bytes remains an unsupported layout in this release; it is not reported as an
invalid offset. Tiny PE files with short optional headers have additional
unverified loader requirements. Unconfirmed nonzero DOS padding remains an
unresolved Windows-header candidate, with independently validated DOS facts in
the target error diagnostics; those facts do not prove a Windows header absent.
unverified loader requirements. A nonzero Windows-header pointer without a
recognized signature remains an unresolved candidate, with independently
validated DOS facts in the target error diagnostics; those facts do not prove a
Windows header absent. When a nonempty relocation table ends inside a complete
DOS header, the remaining header bytes are DOS padding, so a nonzero dword
there is not a Windows declaration. An unread signature beyond the current
probe is requested before that padding rule applies.
On Linux and macOS, `annotate_native_function` also edits a function name and/or
entry comments atomically and returns refreshed analysis, and `set_address_name`
and `set_addresses_names` name a function entry or any other mapped address
+16 -4
View File
@@ -448,8 +448,10 @@ uncertainty rather than pretending to execute later writes. References
handed back by local functions, methods, constructors, async functions, and
generators also retain escape uncertainty, including when the call is read-only;
this does not establish that a mutation occurred. Aliases
and shared children in spread and rest copies retain that uncertainty; copied
primitive slots and unrelated containing properties remain known. Object rest
and shared children in spread and rest copies retain that uncertainty. Exposing
the copy alone does not invalidate copied primitive slots or unrelated containing
properties. Dynamic child writes such as `copy[key].value = 2` remain conservative
about which original slot was selected. Object rest
excludes consumed keys, array rest excludes the consumed prefix for known
arrays, and later explicit data properties and methods replace earlier object
spread references. A known array prefix does not expose values from a following
@@ -466,6 +468,11 @@ Receiver projections follow known data fields initialized from `this` or earlier
self-referencing fields; static fields retain their declaring class identity.
An alias cycle that repeatedly selects deeper children retains escape uncertainty
at its recovered roots rather than claiming a fixed number of loop iterations.
Equivalent member reads in a repeating loop share unknown ordering instead of
creating a distinct capture for every branch history. Straight-line captures and
once-only loop inputs retain their own selection sites. When unobserved suffixes
are widened, their exact observed prefixes stay separate so unrelated siblings
remain known.
Writes through an object getter's returned receiver retain that uncertainty
when the object is an array element, nested inside mixed object/array
@@ -510,8 +517,13 @@ escape uncertainty. External prototype changes and unsupported runtime protocols
remain unknown rather than supplying exact post-invocation values.
Identifier references and closure captures share Babel syntax-role facts plus
explicit binding/write roles; noncomputed declaration keys and import/meta names
are not lexical reads. Provenance-only expression-depth omissions enter the same typed source coverage
as value-evaluation bounds and remain visible in retained application Evidence.
are not lexical reads. TypeScript type positions, including `typeof` type queries
and computed keys in erased signatures, do not evaluate JavaScript bindings or
create runtime calls or captures. Decorators on declared class fields can still
run when the class is defined, and a decorated declared or abstract field can
evaluate its computed key. Provenance-only expression-depth omissions enter
the same typed source coverage as value-evaluation bounds and remain visible in
retained application Evidence.
Immediate synchronous function results also share the loader reference owner.
An expression arrow returning the retained CommonJS `require` can preserve that
loader identity. Possible loader results with opaque alternatives or block
+174
View File
@@ -0,0 +1,174 @@
# Linux kernel modules
`inspect_kernel_module` / `inspect-kernel-module` reads metadata from one
selected local ELF32/ELF64 relocatable object (`.ko`) without executing it.
That metadata is the `.modinfo` key/value strings, the `__versions` symbol
CRCs, the presence of `.gnu.linkonce.this_module` and `__ksymtab*` sections,
the observed file sizes of `.init.text` and `.exit.text`, and an optional
appended signature trailer. It does not require a Linux host or the `modinfo`
program.
```bash
rea inspect-kernel-module /absolute/path/to/module.ko --json
```
The CLI resolves relative paths against the caller's working directory. MCP
requires an absolute filesystem path and preserves that exact selection:
```json
{
"name": "inspect_kernel_module",
"arguments": { "path": "/artifacts/demo.ko" }
}
```
## Observations
The inline Evidence result contains the artifact path, SHA-256, and byte
length. `applicability` is `applicable` only for `ET_REL`. Other ELF types are
`not_applicable`: header facts are filled and `.modinfo`, `__versions`, and
the signature trailer are not examined.
`.modinfo` entries stay in file order. Duplicate keys such as `alias` and
`parm` are preserved. Each entry keeps its key, value, file range, and
SHA-256. Values are the bytes after the first `=`. NUL padding between
strings is skipped. Keys and values use a byte-preserving Latin1 projection:
each source byte becomes the same-numbered Unicode code point. They are not
decoded as UTF-8. `raw_sha256` hashes the original entry bytes, including its
terminating NUL when present; `raw_hex` shows those bytes when they fit the
inline limit.
`__versions` entries are `struct modversion_info`: a 64-byte record whose CRC
is an `unsigned long` and whose name occupies the remaining bytes. ELF32
stores a 32-bit CRC and a 60-byte name. ELF64 stores a 64-bit unsigned long
and a 56-byte name. CRCs are zero-padded lowercase hex, 8 or 16 characters,
not a JavaScript number. The layout is reported from the ELF class even when
the section is absent. Names use the same byte-preserving Latin1 projection
as `.modinfo` keys and values. Only this classic `__versions` layout is
decoded. `versions.status: "absent"` means no `__versions` section was found
when section names were complete; it does not establish the absence of version
metadata in `__version_ext_names` and `__version_ext_crcs`, which Linux uses
with `CONFIG_EXTENDED_MODVERSIONS` and this workflow does not decode.
`.gnu.linkonce.this_module` is reported as present, absent, or unknown.
`layout` is always `unknown` because `struct module` changes with the kernel
version and is not decoded. File-backed `__ksymtab*` sections are listed in
section-header order by name and file range; their records are not decoded.
Sections that are `SHT_NOBITS` or leave the file are counted in one limitation
without their names. `.init.text` and `.exit.text` report the
observed file-backed size. A compressed payload reports the stored size and a
limitation that the decoded size is unknown.
The signature trailer is the 27-byte magic `~Module signature appended~\n`
preceded by a 12-byte `module_signature` (`algo`, `hash`, `id_type`,
`signer_len`, `key_id_len`, three pad bytes, and big-endian `sig_len`). When
those parameter fields are zero and `id_type` is PKCS#7 (`2`), the preceding
`sig_len` bytes are the PKCS#7 range and its SHA-256. The range is not parsed
and no certificate, signer, or digest-coverage claim is made. An absent
trailer is observed absence, not proof the module is unsigned.
When section names are incomplete, a missing named section is `unknown`
rather than `absent`. An empty file-backed section is present and empty.
## Limits and failures
The selected file is limited to 64 MiB by default (512 MiB maximum). At most
8192 `.modinfo` and `__versions` entries plus listed `__ksymtab*` sections are
examined by default (65536 maximum); `coverage.examined_entries` counts all
three. Exceeding that budget fails with no partial success. Keys longer
than 256 bytes and values longer than 4096 bytes stay malformed per entry;
the field that fits is still copied. A `__ksymtab*` section name longer than
256 bytes keeps its range with `name: null` and `ksymtab_name_exceeds_limit`. Inline raw hex stops at 4096 bytes and
the SHA-256 remains.
A `.modinfo` span with no trailing NUL is `unterminated_modinfo_entry` and
earlier entries are kept. A string with no `=` is `modinfo_missing_separator`.
An empty key is `modinfo_empty_key`. A `__versions` name with no NUL is
`version_name_unterminated`. A `__versions` section whose size is not a
multiple of 64 bytes is closed with `versions_size_not_multiple` and no
entries. `SHT_NOBITS` or a declared range that leaves the file is
`modinfo_section_not_file_backed` or `versions_section_not_file_backed`.
`SHF_COMPRESSED` payloads are not inflated
(`modinfo_section_compressed`, `versions_section_compressed`).
An appended magic with fewer than 12 preceding bytes is
`signature_trailer_truncated`. A `sig_len` that does not leave at least one
module byte is `signature_length_inconsistent`. Nonzero `algo`, `hash`,
signer, key-id, or pad fields for PKCS#7 are `signature_parameters_nonzero`.
A parameter block whose `id_type` is not PKCS#7 is
`signature_id_type_not_pkcs7` and is unsupported rather than a validated
signature.
gzip (`1f 8b`), xz (`fd 37 7a 58 5a 00`), and zstd (`28 b5 2f fd`) whole-file
containers are `unsupported`. The result names `external_decompression` as the
next probe and does not claim the payload is a kernel module. This is an external
step, not a REA operation: neither this tool nor `extract_artifact` decodes these
containers. Use an external gzip, xz, or zstd utility to decompress into a separate
file, then call `inspect_kernel_module` on that file to inspect the `ET_REL` image.
Malformed ELF headers return no partial kernel-module metadata. This workflow
does not execute the module or validate an appended signature.
## Format references
The record layout follows Linux v6.12's
[`modversion_info`](https://github.com/torvalds/linux/blob/v6.12/include/linux/module.h)
and [`MAX_PARAM_PREFIX_LEN`](https://github.com/torvalds/linux/blob/v6.12/include/linux/moduleparam.h).
The [Linux kbuild module versioning guide](https://docs.kernel.org/kbuild/modules.html#module-versioning)
describes the separate extended version sections.
Trailer fields and acceptance rules follow
[`module_signature`](https://github.com/torvalds/linux/blob/v6.12/include/linux/module_signature.h)
and [`mod_check_sig`](https://github.com/torvalds/linux/blob/v6.12/kernel/module_signature.c).
These references establish the decoded layout, not runtime verification.
## Real-module verification lane
Run from a built checkout with Docker available. This downloads a Debian kernel
package into a disposable container, extracts a filesystem module, decompresses
it when necessary, and inspects it with REA. It never installs or loads that
kernel module. The temporary output is outside the repository; record the
printed package version and module digest with results.
```bash
module_lane=$(mktemp -d)
docker run --rm -i -v "$module_lane:/out" debian:stable bash -se <<'CONTAINER'
apt-get update -qq
apt-get install -y --no-install-recommends xz-utils zstd ca-certificates
cd /tmp
arch=$(dpkg --print-architecture)
package=$(apt-cache depends "linux-image-$arch" | awk '/Depends: linux-image-[0-9]/{print $2; exit}')
test -n "$package"
apt-get download "$package"
dpkg-deb -f ./*.deb Package Version > /out/package.txt
dpkg-deb -x ./*.deb /tmp/kernel
module=$(find /tmp/kernel -type f -path '*/kernel/fs/*' \( -name '*.ko' -o -name '*.ko.xz' -o -name '*.ko.zst' -o -name '*.ko.gz' \) | sort | head -1)
test -n "$module"
case "$module" in
*.xz) xz -dc "$module" > /out/module.ko ;;
*.zst) zstd -dc "$module" > /out/module.ko ;;
*.gz) gzip -dc "$module" > /out/module.ko ;;
*) cp "$module" /out/module.ko ;;
esac
cat /out/package.txt
sha256sum /out/module.ko
CONTAINER
node scripts/rea.mjs inspect-kernel-module "$module_lane/module.ko" --json > "$module_lane/rea.json"
python3 - "$module_lane" <<'PY'
import json, pathlib, sys
root = pathlib.Path(sys.argv[1])
report = json.loads((root / 'rea.json').read_text())['normalized_result']
entries = report['modinfo']['entries']
assert entries and all(entry['status'] == 'observed' for entry in entries)
keys = [entry['key'] for entry in entries]
for key in ('license', 'vermagic', 'name'):
assert key in keys, key
print('Observed .modinfo entries:', len(entries), 'distinct keys:', len(set(keys)))
print('Artifact:', report['artifact'])
PY
```
`parm` and `parmtype` are retained separately by REA. Synthetic tests cover
their generic key/value representation and all four ELF class/byte-order
combinations. A successful real lane verifies only the selected module and
printed fields; it does not validate certificates. This lane has not run in the
sandbox used for implementation because access to the Docker socket was denied.
+10
View File
@@ -511,6 +511,16 @@ to the linked Mach-O. This classification requires both the recognized native
refusal and an independently parsed dSYM Mach header; other tool failures keep
their failure outcome.
Observed `dyld_info` diagnostics include `unknown mh.filetype 4` and
`unknown filetype 4` for `MH_CORE`, and the corresponding `3` spellings for
`MH_FVMLIB`. These refusals report `unsupported_target` when the independently
parsed Mach header confirms the reported file type. The error retains the exact
native diagnostic and recommends a linked Mach-O executable or relocatable
object. This classifies the selected target's refusal; it does not establish
support for core-dump analysis, historical FVMLIB layouts, or unobserved native
diagnostic spellings. Unknown or contradictory header values and signalled
commands remain failures.
`inspect_macho.selected_architecture` identifies the exact selected lipo slice
and its `container_file_offset` in the input file. A thin Mach-O has a known
offset of zero; an unknown universal-slice offset stays null. The Evidence
+91
View File
@@ -0,0 +1,91 @@
# Node.js single executable applications
REA inventories injected Node.js SEA blobs through `inspect-artifact` /
`inspect_artifact` and materializes their contents through `extract-artifact` /
`extract_artifact`. It never runs the application or deserializes V8 snapshots
or code cache.
```sh
rea inspect-artifact ./application --json
rea inspect-artifact ./application --detail summary --evidence_output evidence.json --json
rea extract-artifact ./application --json
```
For MCP, open the carrier with `open_binary`, then call `inspect_artifact`.
Summary delivery retains the complete Evidence: use `parent_evidence_id` with
`inspect_analysis_view` to request particular inventory paths and fields.
Extraction returns a fresh `output_root`; analyze that extracted directory with
`analyze_javascript_application`. Snapshot-only applications have no recovered
main script and receive no JavaScript next probe. Neither does a main script
whose code path lacks a JavaScript source suffix, such as `main`, because the
analyzer selects extracted files by suffix.
## Recognition and evidence
The container format is `node-sea`. REA locates Mach-O sections named
`__NODE_SEA_BLOB` in `NODE_SEA` or `__NODE_SEA`, ELF notes owned by
`NODE_SEA_BLOB`, and PE RCDATA resources named `NODE_SEA_BLOB`. PE resource
locations come from the shared PE resource parser. ELF program-header
`PT_NOTE` segments are authoritative, as at runtime; `SHT_NOTE` sections are
read only when no program-header table is readable, because injectors can
leave stale section entries. A PE without a `NODE_SEA_BLOB` resource name is
left to native classification. Universal Mach-O carriers
with different slice blobs are rejected as ambiguous; identical blobs are
inventoried once. Native carrier bytes keep their content hash and native
classification while the root occurrence represents the SEA container.
Root `format_metadata` preserves raw flags, decoded flags, code path, blob
location, exec arguments and fuse observations. Children retain their role and
original `source_path`. The main script uses its declared code path; snapshots
and code caches use `__node_sea__/snapshot` and `__node_sea__/code-cache`.
Assets retain their names. Extraction applies the shared path-containment and
collision rules, rejects unsafe names and never overwrites existing files.
The fuse scan independently records `set`, `unset`, `absent`, or `conflicting`.
An unset fuse with a present blob is reported as a limitation. These are static
sentinel observations, not proof of runtime activation: sentinel-like text in
script or asset bytes also participates in the scan.
## Supported layout and limits
The decoder follows the 64-bit little-endian layout in Node
[v24.10.0 node_sea.cc](https://github.com/nodejs/node/blob/v24.10.0/src/node_sea.cc)
and [node_sea.h](https://github.com/nodejs/node/blob/v24.10.0/src/node_sea.h),
including the exec-argument extension byte. Older layouts without that byte,
32-bit length fields, big-endian blobs and future flags are not supported.
Carrier byte order is independent of blob byte order.
Malformed magic, truncation, unknown flags/extensions, invalid UTF-8, duplicate
asset names and conflicting blob declarations fail closed with named reasons.
Zero padding after a complete blob is allowed. The blob read ceiling is 256 MiB;
PE carriers also use the shared 512 MiB input, 65,536 directory-entry and 8 MiB
resource-metadata ceilings. The decoder admits at most 100,000 assets whose
names total at most 8 MiB, and 4,096 exec arguments totaling at most 1 MiB of
text. These are fixed resource bounds, not Node format limits, checked before
decoding names or creating records; a hit fails as a limit with no runtime
override. Asset and argument decoding, padding scans, and inventory construction
yield in bounded batches and honor caller cancellation. Asset count and name
budgets bound retained metadata independently of stored member body sizes. The PE
name probe reads headers from the first 64 KiB of the carrier; a PE whose
headers extend beyond that is left to native classification. Snapshot and
code-cache bytes remain opaque.
## Verification
Synthetic fixtures cover Mach-O, ELF and PE inventory/extraction, malformed
blobs, collisions and fuse contradictions. CLI and MCP tests exercise the same
artifact provider and advertised schemas.
The optional real ELF lane uses Docker's official Node v24.10.0 image and pinned
postject to build a fixture, then verifies inventory and extracted script/asset
bytes without running the injected application:
```sh
npm run verify:node-sea
```
This requires Docker and network access to fetch the producer image and
postject. It creates and removes its own temporary fixture directory. A real
macOS Mach-O or Windows PE run is separate coverage; synthetic carriers do not
establish real-platform verification. No performance improvement is claimed;
whole-task tokens, CPU, memory, and cold/warm timings have not been measured.
+55
View File
@@ -0,0 +1,55 @@
# PE TLS callbacks and load configuration
Use `inspect_pe_load_config` to locate declared TLS callbacks and inspect the load
configuration of an explicit local PE32 or PE32+ image. No disassembler, Windows
host, or target execution is required.
```sh
rea inspect-pe-load-config /absolute/path/to/application.exe --json
rea inspect-pe-load-config /absolute/path/to/application.exe --max-callbacks 256 --json
```
MCP accepts `{ "path": "/absolute/path/to/application.exe" }`. CLI paths may be
relative to the current directory; MCP paths must be absolute.
The complete Evidence includes the artifact path, SHA-256, directory file ranges,
and callback-array file ranges. `tls` reports the raw data range, index address,
zero-fill size, characteristics and null-terminated callback array. Callback VAs
are hexadecimal strings to preserve 64-bit precision. RVA, mapped file offset,
and section are derived locations; unknown mappings are `null`.
`load_config` reports fields only when its declared `Size` covers the entire
field. MSVC x86 images often declare a 64-byte load-config data directory while
the structure's own `Size` is larger; like the Windows loader, REA reads `Size`
bytes when they are file-backed in one run, and reports both
`directory.size` and `size`. `structure_location` is the file range of the
`Size` bytes REA checked; Evidence locations include it beside the declared
directory range when the two differ. Unavailable fields are `null`; a covered zero pointer remains `"0x0"`.
The report includes the security cookie, PE32 SafeSEH table/count, CFG pointers
and table/count, address-taken IAT and long-jump tables/counts, EH continuation
table/count, and XFG check pointer. CHPE and dynamic relocation pointers are raw
values. The dynamic relocation table offset (DWORD) and section (WORD) are
reported separately when `Size` covers them, including zero values; REA does
not infer a VA from that pair or decode the target and mitigation-table entries.
Guard flags retain the raw DWORD, recognized names, unknown bits, and the CFG
entry stride. Mitigation claims derived from those flags carry
`evidence_kind: "inferred"`; they do not prove runtime enforcement. The image is
never executed. Missing directories are observed absence of those declarations,
not proof that no callback or mitigation exists by another mechanism.
The default read budget is 64 MiB (`max_file_bytes`, ceiling 512 MiB). The default
callback budget is 64 (`max_callbacks`, ceiling 4096). An exact-budget array is
accepted if its next slot is null. Truncated, ambiguous or unmapped directory and
callback-array storage fails closed, as does budget exhaustion; no partial
success is returned. An unmapped callback **target** retains its VA/RVA with an
unknown file offset. Load-config versions beyond the supported fields remain
undecoded.
Layout references: [Microsoft PE format](https://learn.microsoft.com/en-us/windows/win32/debug/pe-format),
[IMAGE_LOAD_CONFIG_DIRECTORY32](https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_load_config_directory32),
and [IMAGE_LOAD_CONFIG_DIRECTORY64](https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_load_config_directory64).
For a real cross-compiled fixture, run `npm run verify:pe:load-config` with Zig
on PATH (or `REA_ZIG` set to its path). The script builds PE32 and PE32+ samples
and inspects them through the public CLI without running them.
+3
View File
@@ -1,5 +1,8 @@
# Headerless raw-binary analysis with Ghidra
Headerless images are covered here. GameCube/Wii DOL images have a header and
use their own format selection; see [GameCube DOL analysis](ghidra-dol.md).
REA admits a headerless executable when the caller supplies the CPU family and
the address where file offset 0 should be mapped. That interpretation is not
inferred from a `.bin` suffix or from unrecognized bytes.
+2 -1
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · **العربية** · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md) · [Română](README_ro.md)
# REA: هندسة عكسية لأي شيء
@@ -158,6 +158,7 @@ npx rea-agents@latest setup
| شيفرة EVM البايتية | محددات التوجيه، وإزاحات البايتات، والمعاملات المستنتجة، وقابلية تغيير الحالة | مدخل محلي يحمل بايتات خامًا أو تمثيلًا سداسيًا عشريًا على Linux x64؛ [دليل 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) |
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
| أعطال Linux المسجّلة | سجلات note الخام، ومسجّلات المعالج/الإشارات لكل خيط مسجّل، وترشيحات اختيارية لخرائط الذاكرة | pwntools يوفّره المستدعي على Linux x64؛ GDB/pwndbg اختياريان؛ [الأعطال المسجّلة](../recorded-crashes.md) |
+2 -1
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · **Deutsch** · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md) · [Română](README_ro.md)
# REA: Alles reverse-engineeren
@@ -158,6 +158,7 @@ REA benötigt Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) oder 26+ (>=26.12.1) so
| 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](../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) |
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
| Aufgezeichnete Linux-Abstürze | Unverarbeitete Note-Einträge, Register/Signale aller aufgezeichneten Threads und optionale Zuordnungskandidaten | Vom Aufrufer bereitgestelltes pwntools unter Linux x64; optional GDB/pwndbg; [aufgezeichnete Abstürze](../recorded-crashes.md) |
+2 -1
View File
@@ -1,6 +1,6 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md) · **Ελληνικά** · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md)
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md) · **Ελληνικά** · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md) · [Română](README_ro.md)
# REA: Reverse Engineer Anything
@@ -159,6 +159,7 @@ npx rea-agents@latest setup
| 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) |
| 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) |
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
| Αρχείο WASM | Έλεγχο εγκυρότητας, ενότητες, ακριβείς μορφές εισαγωγών/εξαγωγών σε WAT και αποκωδικοποιημένο WAT | WABT 1.0.42 που παρέχει ο χρήστης· `inspect_wasm_artifact` |
| Bytecode EVM | Selectors επιλογής συνάρτησης, μετατοπίσεις σε bytes, εκτιμώμενα ορίσματα και δυνατότητα μεταβολής της κατάστασης | Τοπική είσοδος σε ακατέργαστα bytes ή δεκαεξαδική μορφή σε Linux x64· [οδηγός EVM χωρίς εκτέλεση](../evm-bytecode.md) |
+2 -1
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · **Español** · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md) · [Română](README_ro.md)
# REA: Ingeniería inversa de cualquier cosa
@@ -158,6 +158,7 @@ REA requiere Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) o 26+ (>=26.12.1), adem
| 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](../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) |
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
| Fallos de Linux registrados | Registros note sin procesar, registros/señales de cada hilo registrado y posibles correspondencias de memoria opcionales | pwntools proporcionado por quien llama, en Linux x64; GDB/pwndbg opcionales; [fallos registrados](../recorded-crashes.md) |
+2 -1
View File
@@ -1,7 +1,7 @@
<div align="center" dir="rtl">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · **فارسی** · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md) · [Română](README_ro.md)
# REA: مهندسی معکوس هر چیزی
@@ -151,6 +151,7 @@ REA به Node.js 22.x (>=22.23.4)، 24.x (>=24.22.1) یا 26+ (>=26.12.1)، به
| 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) |
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
| کرش‌های ثبت‌شدهٔ لینوکس | یادداشت‌های خام، رجیسترها/سیگنال‌های تمام threadهای ثبت‌شده و گزینه‌های احتمالی نگاشت | pwntools تأمین‌شده توسط فراخواننده روی Linux x64؛ GDB/pwndbg اختیاری؛ [کرش‌های ثبت‌شده](../recorded-crashes.md) |
| نصب‌های دسکتاپ | نامزدهای زمان اجرا، فایل‌های شاهد، نشانه‌های مؤلفه‌ها و مراحل بعدی | بررسی محدود پوشه‌ها و سرآیندها;[برنامه‌های دسکتاپ](../desktop-applications.md) |
+2 -1
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · **Français** · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md) · [Română](README_ro.md)
# REA : rétro-ingénierie de tout
@@ -158,6 +158,7 @@ REA nécessite Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) ou 26+ (>=26.12.1), ai
| 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](../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) |
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
| Crashs Linux enregistrés | Notes brutes, registres et signaux de chaque thread enregistré, et correspondances de mappings facultatives | pwntools fourni par l'utilisateur sous Linux x64 ; GDB/pwndbg en option ; [crashs enregistrés](../recorded-crashes.md) |
+2 -1
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · **Bahasa Indonesia** · [Italiano](README_it.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md) · [Română](README_ro.md)
# REA: Rekayasa Balik Apa Pun
@@ -162,6 +162,7 @@ Alat tambahan dan dukungan sistem host bergantung pada target yang dianalisis:
| 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) |
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
| Crash Linux yang direkam | Catatan mentah, register/sinyal setiap thread yang direkam, dan kandidat pemetaan opsional | pwntools yang disediakan pemanggil; GDB/pwndbg opsional; [crash yang direkam](../recorded-crashes.md) |
| Instalasi desktop | Kandidat runtime, berkas pendukung, penanda komponen, dan langkah berikutnya | Pemeriksaan direktori dan header terbatas;[Aplikasi desktop](../desktop-applications.md) |
+2 -1
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · **Italiano**
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md) · [Română](README_ro.md)
# REA: Reverse Engineer Anything
@@ -158,6 +158,7 @@ REA richiede Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) o 26+ (>=26.12.1), oltre
| Bytecode EVM | Selettori di dispatch, offset dei byte, argomenti inferiti e mutabilità | Carrier raw/hex locale; [guida EVM offline](../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) |
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
| Crash Linux registrati | Note grezze, registri/segnai di ogni thread registrato e candidati mapping opzionali | pwntools fornito dal chiamante; GDB/pwndbg opzionale; [crash registrati](../recorded-crashes.md) |
+2 -1
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · **日本語** · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md) · [Română](README_ro.md)
# REA:あらゆるものをリバースエンジニアリング
@@ -159,6 +159,7 @@ REA には Node.js 22.x(>=22.23.4)、24.x(>=24.22.1)、または 26+ (>=
| 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) |
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
| 記録済みの Linux クラッシュ | 生の note レコード、記録された全スレッドのレジスター/シグナル、任意のマッピング候補 | Linux x64 で利用者が用意する pwntools;GDB/pwndbg は任意;[記録済みクラッシュ](../recorded-crashes.md) |
| デスクトップのインストール先 | ランタイム候補、根拠ファイル、コンポーネントの目印、次の調査手順 | 上限付きのディレクトリ・ヘッダー検査;[デスクトップアプリ](../desktop-applications.md) |
+2 -1
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · **한국어** · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md) · [Română](README_ro.md)
# REA: 무엇이든 리버스 엔지니어링
@@ -159,6 +159,7 @@ REA에는 Node.js 22.x(>=22.23.4), 24.x(>=24.22.1) 또는 26+ (>=26.12.1)와 npm
| 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) |
| 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) |
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
| WASM 아티팩트 | 유효성 검사, 섹션, import/export의 정확한 WAT 형식, 디코딩된 WAT | 호출자 제공 WABT 1.0.42; `inspect_wasm_artifact` |
| EVM 바이트코드 | 디스패치 선택자, 바이트 오프셋, 추론한 인자와 상태 변경 가능 여부 | Linux x64의 로컬 원시 바이트/16진수 입력 파일; [오프라인 EVM 가이드](../evm-bytecode.md) |
+2 -1
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md) · **മലയാളം** · [Roman Urdu](README_ur-Latn.md)
· [Ελληνικά](README_el.md) · **മലയാളം** · [Roman Urdu](README_ur-Latn.md) · [Română](README_ro.md)
# REA: എന്തും Reverse Engineer ചെയ്യുക
@@ -158,6 +158,7 @@ REA-യ്ക്ക് Node.js 22.x (>=22.23.4), 24.x (>=24.22.1), അല്ല
| 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) |
| 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) |
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
| WASM artifact | Validation, sections, കൃത്യമായ import/export WAT രൂപങ്ങൾ, decoded WAT | Caller നൽകുന്ന WABT 1.0.42; `inspect_wasm_artifact` |
| EVM bytecode | Dispatch selectors, byte offsets, അനുമാനിച്ച arguments, mutability | Linux x64-ൽ local raw/hex carrier; [offline EVM guide](../evm-bytecode.md) |
+2 -1
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · **Polski** · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md) · [Română](README_ro.md)
# REA: Reverse Engineer Anything
@@ -158,6 +158,7 @@ REA wymaga Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) lub 26+ (>=26.12.1) oraz n
| 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](../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) |
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
| Zarejestrowane awarie systemu Linux | Surowe rekordy note, rejestry/sygnały każdego zarejestrowanego wątku i opcjonalni kandydaci na mapowania | pwntools dostarczone przez użytkownika w systemie Linux x64; opcjonalnie GDB/pwndbg; [zarejestrowane awarie](../recorded-crashes.md) |
+2 -1
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · **Português (Brasil)** · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md) · [Română](README_ro.md)
# REA: Faça engenharia reversa de qualquer coisa
@@ -158,6 +158,7 @@ O REA requer Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) ou 26+ (>=26.12.1), alé
| 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](../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) |
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
| Falhas do Linux registradas | Registros note brutos, registradores/sinais de cada thread registrada e candidatos opcionais a mapeamentos | pwntools fornecido pelo chamador no Linux x64; GDB/pwndbg opcionais; [falhas registradas](../recorded-crashes.md) |
+442
View File
@@ -0,0 +1,442 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md) · **Română**
# REA: Reverse Engineer Anything
### Un singur server MCP pentru inginerie inversă: fișiere binare, aplicații și comportament în timpul execuției.
**Ai găsit o funcționalitate care îți place? Înțelege cum funcționează, până la nivel de cod binar.**
[![Versiunea npm](https://img.shields.io/npm/v/rea-agents?style=flat-square&color=cb3837)](https://www.npmjs.com/package/rea-agents)
[![Catalogul uneltelor MCP](https://img.shields.io/badge/MCP-tool_catalog-5c4ee5?style=flat-square)](../mcp-contracts.md#generated-catalog)
[![Cerințe Node.js](https://img.shields.io/badge/Node.js-requirements-339933?style=flat-square&logo=nodedotjs&logoColor=white)](#ce-poți-analiza)
[![skills.sh](https://skills.sh/b/morluto/rea?style=flat-square)](https://skills.sh/morluto/rea/reverse-engineer-anything)
[![Licența MIT](https://img.shields.io/badge/license-MIT-f4c430?style=flat-square)](../../LICENSE)
[![Discord](https://img.shields.io/discord/1556595354999332884?logo=discord&logoColor=white&label=Discord&color=5865F2)](https://discord.gg/GkcryMnJDM)
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&amp;utm_medium=badge&amp;utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
**[Site web](https://rea.tools/) · [Ghiduri](https://rea.tools/guides/) · [Studii de caz](https://rea.tools/showcase/)**
[Pornire rapidă](#pornire-rapidă) · [Cum funcționează REA](#cum-funcționează-rea) · [Ce poți analiza](#ce-poți-analiza) · [Studii de caz](#studii-de-caz) · [Întrebări frecvente](#întrebări-frecvente) · [Documentație](#documentație)
<code>npx rea-agents setup</code>
</div>
> [!IMPORTANT]
> Descarcă REA din [morluto/rea](https://github.com/morluto/rea),
> de pe [rea.tools](https://rea.tools/) sau prin [pachetul npm rea-agents](https://www.npmjs.com/package/rea-agents).
> Urmează linkurile de instalare și către versiunile publicate oferite de aceste surse oficiale.
> Un nume de repository asemănător sau un README copiat nu dovedește că descărcarea provine dintr-o sursă oficială.
<div align="center">
<br />
<img src="../assets/rea-hopper-analysis.png" alt="REA pornește componenta de legătură pentru analiză în Hopper în timp ce inspectează un fișier binar nativ" width="1200" />
<br /><br />
<table aria-label="Comunitatea REA">
<tr>
<td align="center" width="360">
<a href="https://discord.gg/GkcryMnJDM">
<img src="../assets/discord.svg" height="42" alt="Discord" /><br />
<strong>Alătură-te comunității de inginerie inversă</strong>
</a><br />
<sub>Discord · Întrebări și răspunsuri · Prezentări și demonstrații</sub>
</td>
</tr>
</table>
<br />
</div>
---
Ai văzut într-o aplicație o funcționalitate pe care ai vrea să o incluzi în propriul produs? Cere-i agentului tău să o investigheze cu REA. Poate inspecta aplicația fără codul ei sursă, poate explica modul în care funcționează acea funcționalitate, poate prezenta dovezile și poate construi o variantă pentru proiectul tău.
REA conectează agentul tău la unelte pentru inspectarea fișierelor binare native, a aplicațiilor JavaScript și Electron, a ansamblurilor .NET și a site-urilor web. Poți folosi aceleași unelte și din terminal. Analiza rulează local, iar rezultatele includ dovezile și limitările care stau la baza fiecărei concluzii.
Programul de configurare înregistrează REA în agentul tău și instalează instrucțiunile corespunzătoare pentru fluxul de lucru. Analiza nativă poate folosi o instalare existentă de Hopper, Ghidra sau IDA; programul de configurare poate instala opțional Hopper, cu aprobarea ta. Analiza statică JavaScript nu necesită un motor de analiză nativă.
> **[Vizitează site-ul REA](https://rea.tools/)** pentru instrucțiuni de configurare, ghiduri ilustrate și studii de caz reale.
## Pornire rapidă
Pe Linux, pornirea Ghidra, JADX și a uneltelor pentru firmware, precum și analiza
statică JavaScript/Electron necesită un `ps` compatibil cu procps în `PATH`-ul REA,
pentru a verifica dacă procesele îi aparțin. Pașii de remediere sunt în
`docs/installation.md`, la secțiunea „Linux owned-process prerequisite”.
### Configurează agentul
Cu Node.js și npm instalate, rulează:
```bash
npx rea-agents setup
```
Alege agenții, verifică modificările propuse și aprobă-le. Configurarea adaugă
serverul MCP REA și instrucțiunile corespunzătoare pentru fluxul de lucru,
creând copii de siguranță ale configurației existente. Repornește apoi agentul.
Programul de configurare acceptă Claude Code, Codex, Cursor, Gemini CLI, Grok Build și
[alți agenți](../installation.md#supported-agents). Consultă
[instalarea și configurarea](../installation.md) pentru configurarea motoarelor
de analiză și înregistrarea manuală a serverului MCP.
MCP folosește implicit scheme de intrare compacte, pentru a reduce spațiul ocupat
în context. Setează `REA_MCP_INPUT_SCHEMA_PROFILE=full` în înregistrarea MCP când
ai nevoie de îndrumările complete pentru câmpurile expuse și de detaliile
validării structurilor imbricate. Ambele profiluri păstrează toate uneltele și
validarea canonică a serverului. Consultă secțiunea „Compact input schema profile”
din ghidul contractelor MCP.
### Cere-i agentului
```text
Înțelege cum funcționează căutarea în aplicația Notes, arată-mi dovezile și
construiește o funcționalitate similară pentru proiectul meu.
```
Înlocuiește Notes cu aplicația vizată și precizează funcționalitatea pe care vrei să o înțelegi.
### Folosește terminalul
Inspectează directorul extras al unei aplicații JavaScript/Electron sau o arhivă ASAR:
```bash
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
```
Rezultatul include modulele, importurile, delimitările dintre componentele Electron
și dovezile aferente. Înlocuiește calea cu cea a aplicației vizate, de exemplu
`"D:/apps/example"` pe Windows.
Pentru a instala comanda `rea` și a o folosi în mod curent:
```bash
npm install --global rea-agents
rea --help
```
Pentru analiza nativă, configurează mai întâi un motor de analiză. Consultă
[ghidul CLI și Evidence](../cli.md) pentru comenzile de analiză nativă,
selectarea motorului, instantanee și utilizarea în scripturi.
### Actualizează REA
REA evoluează rapid, iar versiunile noi includ frecvent corecții de erori.
Menține instalarea la zi.
Pentru o interfață CLI instalată cu `npm install --global` sau `bun add --global`:
```bash
rea update
```
`rea --update` este un alias pentru acest flux de actualizare verificat și acceptă
aceleași opțiuni globale de ieșire. Folosește `rea update` când îl combini cu alte
opțiuni de control al comenzii.
Comenzile de actualizare păstrează cel mult 4 MiB în total din stdout și stderr.
Răspunsurile cu metadate prea mari sunt respinse după încheierea comenzii;
instalările cu ieșire detaliată continuă cu un volum limitat de date de diagnostic.
Afișarea progresului în terminal ține cont de ritmul în care acesta poate primi
date și așteaptă finalizarea operațiilor de scriere deja trimise.
Actualizările instalărilor globale cu Bun necesită Bun 1.2.17 sau o versiune mai nouă.
Dacă ai o versiune mai veche, actualizează Bun manual înainte de a rula `rea update`.
Pentru a actualiza înregistrările agenților și skill-ul, rulează comanda de
configurare afișată de programul de actualizare.
Dacă folosești `npx`, actualizează configurarea agentului cu:
```bash
npx rea-agents@latest setup
```
Verifică modificările de configurare și repornește agentul. Pentru comenzi CLI
ocazionale, folosește `npx rea-agents@latest` urmat de comandă. Cu Bun, folosește
`bunx rea-agents@latest` în loc de `npx rea-agents@latest`.
## Cum funcționează REA
Agentul tău apelează REA prin MCP pentru a inspecta ținta și a urmări codul relevant.
REA returnează constatările împreună cu dovezile aferente. Agentul le folosește
pentru a pune întrebări suplimentare, a explica comportamentul sau a scrie și
testa o implementare. Comenzile CLI folosesc aceleași fluxuri de lucru.
![Fluxul unei investigații REA: agentul întreabă despre o țintă locală, REA o inspectează și îi urmărește comportamentul cu ajutorul uneltelor de analiză, iar agentul folosește codul, referințele și necunoscutele returnate pentru a explica, implementa și testa.](../../website/public/assets/figures/rea-investigation-flow.svg)
[Deschide figura la dimensiunea originală](../../website/public/assets/figures/rea-investigation-flow.svg).
<a id="current-status"></a>
## Ce poți analiza
REA necesită Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) sau 26.12.1+, plus npm.
Uneltele suplimentare și platformele acceptate depind de ținta analizată:
| Țintă | Ce returnează REA | Cerințe și ghid |
| ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| Aplicații desktop instalate | Componente de execuție candidate, fișiere auxiliare, indicii despre componente și următoarele verificări | Inspectare integrată a directoarelor și antetelor, cu limite de resurse; [aplicații desktop](../desktop-applications.md) |
| Fișiere binare native | Pseudocod, cod de asamblare, șiruri de caractere, simboluri, apeluri și referințe | Hopper, Ghidra sau IDA; [analiză nativă](../native-investigation.md) |
| Structura ELF, offline | Secțiuni, segmente, simboluri și relocări originale, precum și indicii statice despre mecanismele de protecție | pwntools furnizat de utilizator, pe Linux x64; [diagnosticarea binarelor](../binary-diagnostics.md) |
| Fișiere binare Go | Versiunea compilatorului încorporată în fișier, modulele, înlocuirile și setările de compilare | Inspectare statică integrată a fișierelor ELF/PE/Mach-O cu o singură arhitectură; nu necesită instalarea Go |
| Obiecte de cod GPU | Ținte CUDA/HIP, parametri și resurse selectate ale kernelurilor GPU, textul PTX păstrat | Inspectare statică integrată a fișierelor ELF/PE/fatbin/CUBIN/PTX; [obiecte de cod GPU](../gpu-code-objects.md) |
| Fișiere de model | Metadate GGUF / safetensors, declarații de tensori și totaluri calculate din acestea | Inspectare integrată a antetelor; opțional, hash al întregului fișier; [fișiere de model](../model-artifacts.md) |
| Metadate IL2CPP | Imagini de ansambluri din global-metadata.dat al Unity, nume de tipuri și metode și intervale de indici | Decodare statică integrată a versiunilor 24, 27, 29, 31 și 39; nu necesită Unity |
| Godot PCK | Inventarul și extragerea arhivei, informații originale din antet și director și identificatori ai conținutului selectat | Suport static integrat pentru formatele 2, 3 și 4; nu necesită Godot |
| 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) |
| 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) |
| 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) |
| Module de kernel Linux | Chei .modinfo, CRC-uri din __versions, prezența this_module și structura semnăturii de la sfârșitul fișierului | Inspectare statică integrată a fișierelor ELF; [module de kernel](../kernel-modules.md) |
| Erlang BEAM | Blocuri IFF, atomi, exporturi, importuri, funcții lambda și antetul Code | Inspectare statică integrată a fișierelor BEAM; [Erlang BEAM](../erlang-beam.md) |
| Fișiere WASM | Validare, secțiuni, forme WAT exacte ale importurilor și exporturilor și cod WAT decodat | WABT 1.0.42 furnizat de utilizator; `inspect_wasm_artifact` |
| Bytecode EVM | Selectori de apel, poziții în octeți, argumente și mutabilitate deduse | Fișier local în format brut sau hexazecimal, pe Linux x64; [ghid EVM offline](../evm-bytecode.md) |
| Baze de date SQLite | Tabele, coloane, indecși, vizualizări, declanșatoare și rânduri selectate din tabele obișnuite | Instantaneu local al bazei de date/WAL; Node.js 24.x >=24.15 sau 26+, cu limite SQLite native |
| Erori fatale Linux înregistrate | Note brute, registrele și semnalele fiecărui fir înregistrat și, opțional, corespondențe candidate pentru mapările de memorie | pwntools furnizat de utilizator, pe Linux x64; opțional GDB/pwndbg; [erori fatale înregistrate](../recorded-crashes.md) |
| JavaScript / Electron | Module, importuri, hărți de cod sursă, rute, IPC și relații cu extensiile native | Node.js și npm; pe Linux: `ps` compatibil cu procps; [analiza aplicațiilor](../javascript-application-workflows.md) |
| Site-uri web | Structura paginilor, scripturi, observații de rețea și capturi de ecran solicitate | Un browser din familia Chrome; [analiză în browser](../browser-observation.md) |
| Capturi de rețea salvate | Cereri, răspunsuri, conținuturi expuse ale mesajelor și pozițiile lor în sursă | HAR sau SAZ din Fiddler Everywhere; mitmdump pe Linux pentru formatul nativ mitmproxy; [ghidul capturilor](../web-network-captures.md) |
| Ansambluri .NET | Metadate, instrucțiuni CIL, dependențe native declarate și comparații între compilări | Fără cerințe suplimentare (doar analiză statică); [ghidul codului gestionat](../managed-code-analysis.md) |
| APK-uri Android | Declarații din manifest, clase, metode decompilate și referințe | JADX fără interfață grafică și un JDK complet, pe Linux/macOS/Windows x64; [ghidul Android](../android-analysis.md) |
| Semnături APK | Semnatari v1/v2/v3/v3.1, amprente ale certificatelor, rotația cheilor și consistența internă a hash-urilor | Fără cerințe suplimentare (doar analiză statică); `inspect_apk_signature` |
| Dispozitive Android | Dispozitive, pachete, procese, jurnale, dumpuri, capturi de ecran și transferuri de APK-uri și fișiere | adb furnizat de utilizator (emulator sau dispozitiv fizic); [ghidul ADB](../adb-device-analysis.md) |
| Proiecte JEB | Unități de proiect analizate prin motor, hash-uri ale artefactelor și pseudocod pentru tipuri și metode | Un client JEB pornit de utilizator, care oferă un server MCP și este selectat prin REA_JEB_MCP_URL; [ghidul JEB](../jeb-analysis.md) |
| Resurse Android | Manifest decodat, tabele de șiruri de caractere, localizări și informații despre versiune | Program de lansare Apktool furnizat de utilizator; [ghidul resurselor](../apktool-resource-analysis.md) |
| Aplicații Flutter | Hash-ul instantaneului Dart, hash-uri ale bibliotecilor pentru fiecare ABI și proveniența compilării motorului | Fără cerințe suplimentare (parsare statică a APK-ului); [ghidul Flutter](../flutter-analysis.md) |
| Firmware | Regiuni, rezultate ale extragerii și transferul către analiza nativă | Binwalk / Unblob pe Linux; [ghidul firmware](../firmware-analysis.md) |
| Pachete de rețea înregistrate | Câmpuri selectate, valori repetate, filtre și reguli decode-as | Linux: TShark 4.x + prlimit; [ghidul capturilor](../packet-captures.md) |
| Rezumate ale traficului de rețea | Puncte terminale, DNS, TLS SNI, distribuția protocoalelor și cereri HTTP | tshark și capinfos furnizate de utilizator (verificate pe Linux); [ghidul pcap](../pcap-analysis.md) |
| Semnale logice înregistrate | Adnotări ale decodorului, intervale de eșantioane și date de diagnostic | Linux: sigrok-cli 0.7.2 + prlimit; [înregistrări de semnale](../signal-recordings.md) |
| Pachete și resurse | Inventare de fișiere, hash-uri, fișiere plist, structura pachetelor de aplicație Apple și resurse extrase | [Ghidul artefactelor și JavaScript](../javascript-artifact-reconstruction.md), [aplicații Apple](../apple-application-analysis.md) |
| Comportamentul proceselor | Ieșirea în terminal, interacțiuni, observații despre terminarea proceselor și sistemul de fișiere și comparații între rulări | Linux/macOS cu un PTY nativ; [capturarea proceselor](../process-capture.md) |
| Executabile PyInstaller | Membri CArchive, versiunea Python, scripturi de intrare și bytecode extras | Fără cerințe suplimentare (doar analiză statică); [aplicații Python împachetate](../python-packaged-applications.md) |
Inspectarea statică JavaScript și .NET citește fișierele furnizate fără a rula
aplicația. Capturarea în timpul execuției rulează ținta selectată sau interacționează
cu aceasta folosind permisiunile contului tău; fiecare ghid de capturare descrie
efectele acestor operații.
Pentru inspectarea offline a binarelor native, REA acceptă și GNU objdump și Rizin,
furnizate de utilizator. Consultă [ghidul uneltelor de inginerie inversă](../reverse-engineering-tools.md)
pentru configurare, comenzi CLI și limitările obiectelor Evidence. REA nu
instalează și nu actualizează niciunul dintre aceste două instrumente.
<a id="choosing-a-deep-analysis-provider"></a>
Formatele native și platformele acceptate diferă în funcție de motorul de analiză.
Consultă [configurarea Hopper și Ghidra](../installation.md#hopper),
[ghidul IDA](../ida-provider.md) (inclusiv o componentă opțională de legătură MCP nativă),
[ghidul decompilatorului Kuna](../kuna-provider.md) și
[suportul experimental Ghidra pe Windows](../windows-ghidra-p0.md).
Ghidra acceptă și [analiza DOS pe 16 biți](../ghidra-dos.md).
Pentru binare mari, mărește timpul maxim de pornire al motorului cu
`REA_GHIDRA_STARTUP_TIMEOUT_MS` sau `REA_HOPPER_STARTUP_TIMEOUT_MS`.
Pentru selectarea motorului, consultă [ghidul CLI](../cli.md#choose-a-provider).
Verifică [disponibilitatea în versiunile publicate](../installation.md#released-package-and-main)
pentru funcționalitățile adăugate după cea mai recentă versiune npm.
## Studii de caz
<a href="https://rea.tools/showcase/">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="../assets/rea-showcases-dark.svg" />
<img alt="Ilustrații pentru studiile de caz despre panoramarea sunetului în DX-Ball, legătura cu clipboardul în Notion și inelele de proiectile din TH04" src="../assets/rea-showcases.svg" />
</picture>
</a>
### DX-Ball: reconstruiește calculul panoramării sunetului
Urmărește un apel audio până la funcția auxiliară care transformă poziția în
panoramare stereo, inspectează instrucțiunile și transformă pseudocodul incomplet
în C. Reconstrucția trece 3.205 cazuri de test comparate cu originalul x86 și
reproduce toți cei 63 de octeți ai funcției compilate.
[Citește studiul de caz](https://rea.tools/showcase/dx-ball/) ·
[Repository-ul reconstrucției](https://github.com/N0zoM1z0/dx-ball)
### Notion: urmărește legătura Electron cu clipboardul
Găsește API-ul pentru clipboard din procesul de randare, urmărește-l prin preload
și IPC până în procesul principal și inspectează formatul datelor cu formatare din clipboard.
[Citește studiul de caz](https://rea.tools/showcase/notion/)
### TH04: recuperează calculul unui inel de proiectile DOS
Inspectează instrucțiunile pe 16 biți ale jocului original pentru PC-98,
recuperează calculele unghiurilor fixe și ale celor orientate spre țintă și
compară codul C++ reconstruit cu rezultatul compilatorului folosit la acea vreme.
[Citește studiul de caz](https://rea.tools/showcase/th04/) ·
[Repository-ul reconstrucției](https://github.com/N0zoM1z0/th04)
Dacă ai folosit REA pentru ceva interesant, ne-ar plăcea să vedem rezultatul.
Prezintă cazul într-un [issue](https://github.com/morluto/rea/issues) sau într-un
[pull request](https://github.com/morluto/rea/pulls), menționând ținta analizată,
întrebarea ta, cum te-a ajutat REA și ce ai descoperit.
## Întrebări frecvente
<details>
<summary><strong>Ce agenți pot folosi REA?</strong></summary>
Orice agent care acceptă servere MCP locale. Programul de configurare pregătește
[agenții acceptați](../installation.md#supported-agents); pentru ceilalți clienți
poți folosi [înregistrarea manuală MCP](../installation.md#mcp-registry).
</details>
<details>
<summary><strong>Am nevoie de Hopper, Ghidra sau IDA?</strong></summary>
Analiza nativă aprofundată folosește unul dintre aceste instrumente. Inspectarea
statică JavaScript și .NET funcționează fără un motor de analiză nativă.
Programul de configurare poate instala Hopper după aprobare; pentru Ghidra și IDA
folosește instalările existente. Consultă [configurarea motoarelor de analiză](../installation.md#hopper).
</details>
<details>
<summary><strong>Trebuie să pornesc mai întâi Hopper?</strong></summary>
REA pornește Hopper când o operație are nevoie de el. Pe macOS, un dialog afișat
la prima pornire îți poate cere să alegi modul demonstrativ sau să activezi licența.
Finalizează acel pas în aceeași sesiune de utilizator înainte de prima operație
CLI/MCP sau de prima sarcină nesupravegheată. Deschide Hopper manual dacă este nevoie.
Consultă [pornirea Hopper și depanarea](../installation.md#launcher-paths-and-troubleshooting).
</details>
<details>
<summary><strong>Ce face instalarea skill-ului de pe skills.sh?</strong></summary>
Skill-ul oferă agentului instrucțiuni de investigare. Folosește `npx rea-agents setup`
pentru a înregistra serverul MCP REA și a instala instrucțiunile corespunzătoare,
apoi repornește agentul. Consultă [instalarea doar a skill-ului](../installation.md#skill-only-installation).
</details>
<details>
<summary><strong>Ce cod returnează REA?</strong></summary>
Analiza nativă returnează pseudocod și cod de asamblare. Analiza JavaScript/Electron
recuperează modulele și relațiile dintre ele. Agentul tău folosește aceste
constatări pentru a scrie și testa o implementare;
[studiile de caz](#studii-de-caz) oferă exemple detaliate.
</details>
<details>
<summary><strong>REA îmi încarcă aplicația pe un server?</strong></summary>
REA analizează țintele local. Agentul tău primește rezultatele uneltelor, iar
furnizorul modelului său are propria politică privind datele.
</details>
<details>
<summary><strong>Ce fac dacă întâlnesc o eroare?</strong></summary>
Actualizează mai întâi; o versiune recentă ar putea să o fi remediat deja.
Pentru o interfață CLI instalată cu `npm install --global` sau `bun add --global`:
```bash
rea update
```
Pentru configurarea agentului prin `npx`:
```bash
npx rea-agents@latest setup
```
Dacă folosești un agent, finalizează [actualizarea configurării](#actualizează-rea)
și repornește-l. Reîncearcă aceeași sarcină. Dacă problema persistă,
[deschide un issue](https://github.com/morluto/rea/issues) și include versiunea REA,
tipul țintei analizate, pașii de reproducere și mesajele de eroare.
</details>
## Documentație
Începe cu [ghidurile practice](https://rea.tools/guides/) de pe site.
Pentru opțiunile exacte, cerințele preliminare și contractele rezultatelor:
- [Instalare și configurare](../installation.md): înregistrarea agenților, configurarea motoarelor de analiză, actualizări și dezinstalare.
- [Verificarea pregătirii și depanare](../installation.md#check-readiness-for-your-task): diagnosticarea unui agent sau motor de analiză.
- [CLI și Evidence](../cli.md): comenzi, selectarea motorului, instantanee, import/export și coduri de ieșire.
- [Contracte MCP](../mcp-contracts.md) și [prompturi pentru agenți](../mcp-prompts.md): rezultatele uneltelor, sesiuni și investigații ghidate.
- [Catalogul uneltelor](../mcp-contracts.md#generated-catalog): inventarul generat al uneltelor, motoarelor de analiză și comenzilor CLI.
- [Plan de dezvoltare](../roadmap.md): lucrări planificate și urmărirea progresului funcționalităților.
Raportează vulnerabilitățile conform [SECURITY.md](../../SECURITY.md).
## Istoricul stelelor
🎉 **300.000 de stele pe GitHub — vă mulțumim!**
Mulțumim tuturor celor care folosesc REA, raportează erori, propun funcționalități,
testează versiuni și contribuie cu remedieri.
<a href="https://www.star-history.com/?repos=morluto%2Frea&amp;type=date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=morluto/rea&amp;type=date&amp;theme=dark&amp;legend=top-left" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=morluto/rea&amp;type=date" />
<img alt="Istoricul stelelor REA pe GitHub" src="https://api.star-history.com/chart?repos=morluto/rea&amp;type=date" />
</picture>
</a>
## Precizări privind utilizarea
Consultă [DISCLAIMER.md](../../DISCLAIMER.md) pentru îndrumări privind utilizarea responsabilă.
Poziția proiectului față de utilizarea ilegală sau neautorizată se aplică tuturor
versiunilor REA, canalelor de distribuție și platformelor; fiecare copie își
păstrează licențele aplicabile.
REA este destinat cercetării legale, educației, interoperabilității, conservării
software-ului și analizei programelor pe care le deții sau pe care ai autorizarea
să le examinezi.
## Contribuții
Ne-ar bucura să contribui la REA! [Deschide un issue](https://github.com/morluto/rea/issues)
pentru a raporta o eroare sau a propune o funcționalitate ori
[trimite un pull request](https://github.com/morluto/rea/pulls) pentru a îmbunătăți
codul sau documentația.
Consultă [CONTRIBUTING.md](../../CONTRIBUTING.md) pentru pregătirea mediului de dezvoltare
și verificări, [ghidul de testare](../testing.md) pentru categoriile de verificări
și [diagrama arhitecturii](../architecture.mermaid) pentru anumite straturi și
relații de dependență.
## Licență
[MIT](../../LICENSE)
Codul scris în cadrul REA este licențiat sub MIT. Materialele terților își păstrează
propriile licențe și mențiuni privind drepturile de autor; consultă
[mențiunile privind terții](../../THIRD_PARTY_NOTICES.md) pentru inventarul
dependențelor, textele licențelor păstrate și condițiile aplicabile uneltelor externe.
**Trebuie să obții legal uneltele comerciale și să ai dreptul să le folosești**,
inclusiv decompilatoarele IDA Pro / Hex-Rays, Hopper și JEB, în condițiile
licențelor aplicabile. REA nu îți acordă nicio licență, cheie sau drept de activare
pentru aceste unelte. Versiunile de evaluare și demonstrative rămân supuse
condițiilor furnizorilor, inclusiv restricțiilor privind utilizatorii,
funcționalitățile, automatizarea și partajarea rezultatelor. Nu sunt autorizate
copiile piratate, cheile furate, partajarea neautorizată a cheilor sau ocolirea
restricțiilor de licențiere. Aceste îndrumări nu modifică licența MIT sau licențele terților.
+2 -1
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · **Русский** · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md) · [Română](README_ro.md)
# REA: Реверс-инжиниринг чего угодно
@@ -158,6 +158,7 @@ REA требует Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) или 26+ (>=26.1
| 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) |
| 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) |
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
| Записанные сбои Linux | Необработанные записи note, регистры/сигналы каждого записанного потока и необязательные кандидаты сопоставления с памятью | pwntools, предоставленный вызывающей стороной, в Linux x64; необязательные GDB/pwndbg; [записанные сбои](../recorded-crashes.md) |
+2 -1
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · **ไทย** · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md) · [Română](README_ro.md)
# REA: Reverse Engineer Anything
@@ -158,6 +158,7 @@ REA ต้องใช้ Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) หรื
| ไบต์โค้ด EVM | ตัวเลือกฟังก์ชันสำหรับการเรียก ออฟเซ็ตไบต์ อาร์กิวเมนต์และความสามารถในการเปลี่ยนสถานะที่อนุมานได้ | ไฟล์บนเครื่องที่มีไบต์ดิบหรือข้อมูลเลขฐานสิบหกบน Linux x64; [คู่มือ 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) |
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
| ข้อมูลการแครชของ Linux ที่บันทึกไว้ | ระเบียน note ดิบ รีจิสเตอร์/สัญญาณของทุกเธรดที่บันทึกไว้ และตัวเลือกการแมปเพิ่มเติม | pwntools ที่ผู้ใช้จัดเตรียมบน Linux x64; เลือกใช้ GDB/pwndbg เพิ่มเติมได้; [การแครชที่บันทึกไว้](../recorded-crashes.md) |
+2 -1
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · **Türkçe** · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md) · [Română](README_ro.md)
# REA: Her şeyi tersine mühendislikle inceleyin
@@ -157,6 +157,7 @@ REA; Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) veya 26+ (>=26.12.1) ve npm gere
| 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) |
| 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) |
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
| WASM artefaktı | Doğrulama, bölümler, tam import/export WAT biçimleri ve decode edilmiş WAT | Çağıranın sağladığı WABT 1.0.42; `inspect_wasm_artifact` |
| EVM bayt kodu | Yönlendirme seçicileri, bayt ofsetleri, çıkarımla belirlenen argümanlar ve durum değiştirilebilirliği | Linux x64 üzerinde yerel ham bayt/onaltılık girdi; [çevrimdışı EVM rehberi](../evm-bytecode.md) |
+2 -1
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · **Українська** · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md) · [Română](README_ro.md)
# REA: Реверс-інжиніринг будь-чого
@@ -158,6 +158,7 @@ REA потребує Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) або 26+ (>=26
| Байт-код EVM | Селектори диспетчеризації, зміщення байтів, виведені припущення про аргументи та змінюваність стану | Локальні необроблені байти або шістнадцяткове представлення у Linux x64; [посібник 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) |
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
| Записані аварійні завершення в Linux | Необроблені записи note, регістри/сигнали кожного записаного потоку й необов'язкові кандидати зіставлення пам'яті | pwntools, наданий стороною, що викликає інструмент, у Linux x64; необов'язкові GDB/pwndbg; [записані аварійні завершення](../recorded-crashes.md) |
+2 -1
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · **Roman Urdu**
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · **Roman Urdu** · [Română](README_ro.md)
# REA: Reverse Engineer Anything
@@ -198,6 +198,7 @@ Mazeed tools aur host support target par depend karte hain:
| 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) |
| 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) |
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
| Erlang BEAM | IFF chunks, atoms, exports, imports, lambdas aur Code header | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
| WASM artifact | Validation, sections, exact import/export WAT forms aur decoded WAT | Aapka diya hua WABT 1.0.42; `inspect_wasm_artifact` |
| EVM bytecode | Dispatch selectors, byte offsets, inferred arguments aur mutability | Linux x64 par local raw/hex carrier; [offline EVM guide](../evm-bytecode.md) |
+2 -1
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · **Tiếng Việt** · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md) · [Română](README_ro.md)
# REA: Kỹ thuật dịch ngược cho mọi thứ
@@ -158,6 +158,7 @@ REA yêu cầu Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) hoặc 26+ (>=26.12.1)
| 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) |
| 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) |
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
| Sự cố Linux đã ghi lại | Bản ghi note thô, thanh ghi/tín hiệu của mọi luồng được ghi lại và các ứng viên ánh xạ tùy chọn | pwntools do bên gọi cung cấp trên Linux x64; GDB/pwndbg tùy chọn; [sự cố đã ghi lại](../recorded-crashes.md) |
+2 -1
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · **繁體中文** · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md) · [Română](README_ro.md)
# REA:逆向一切
@@ -160,6 +160,7 @@ REA 需要 Node.js 22.x(>=22.23.4)、24.x(>=24.22.1)或 26+ (>=26.12.1)
| 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) |
| 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) |
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
| 已記錄的 Linux 崩潰 | 原始 note 記錄、每個已記錄執行緒的暫存器/訊號及可選的對映候選項 | Linux x64 上由呼叫方提供的 pwntools;可選的 GDB/pwndbg;[已記錄崩潰](../recorded-crashes.md) |
| 桌面安裝目錄 | 執行階段候選、支持檔案、元件標記與後續操作 | 有界的目錄與檔頭檢查;[桌面應用](../desktop-applications.md) |
| JavaScript / Electron | 模組、匯入、source map、路由、IPC 和原生附加元件關係 | Node.js 和 npm;[應用分析](../javascript-application-workflows.md); Linux: procps `ps` |
+2 -1
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · **简体中文** · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md) · [Roman Urdu](README_ur-Latn.md) · [Română](README_ro.md)
# REA:逆向一切
@@ -160,6 +160,7 @@ REA 需要 Node.js 22.x(>=22.23.4)、24.x(>=24.22.1)或 26+ (>=26.12.1)
| 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) |
| 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) |
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
| 已记录的 Linux 崩溃 | 原始 note 记录、每个已记录线程的寄存器/信号及可选的映射候选项 | Linux x64 上由调用方提供的 pwntools;可选的 GDB/pwndbg;[已记录崩溃](../recorded-crashes.md) |
| 桌面安装目录 | 运行时候选、支持文件、组件标记和后续操作 | 有界的目录与文件头检查;[桌面应用](../desktop-applications.md) |
| JavaScript / Electron | 模块、导入、source map、路由、IPC 和原生扩展关系 | Node.js 和 npm;[应用分析](../javascript-application-workflows.md); Linux: procps `ps` |
+8
View File
@@ -94,6 +94,14 @@ Captured stdout and stderr are retained up to a 16 MiB combined limit; `output_t
## Cutter bridge
Cutter commands use the local REA Python bridge and authenticated loopback messages.
Discovery reads the session descriptors that the plugin publishes in the bridge
directory. Set `REA_CUTTER_BRIDGE_DIR` in both REA's environment and the environment
that starts Cutter to select a custom directory. Both expand `~` and `~/` paths
against the current user's home, including `~\` paths on Windows. By default,
they use `%LOCALAPPDATA%\REA\CutterBridge` on Windows when `LOCALAPPDATA` is set,
and `~/.cache/rea/cutter-bridge` otherwise. The two processes must resolve the
same directory for discovery to find the published sessions.
The client and plugin must support the current v2 handshake; older peers are
rejected before dispatch because they cannot preserve cross-caller recovery proof.
The client checks the final encoded request against
@@ -0,0 +1,25 @@
# Plan
1. Add `environmentCoverageView.ts`: the request schema, the result fields, the
hint per write reason (`satisfies Record<WriteReason, string>`), a
summarizer over state views and write plans, and the projector, which
projects the environment-state view and summarizes it. Register the kind in
the view unions, the grouping dispatch and the view service (state
accumulator, inferred confidence).
2. Split the export's input read into `readEnvironmentExportFile` and
`environmentExportFile` (parse and seal bytes), both naming the calling
operation, and let the export take an already prepared state.
3. Add `environmentBuild.ts`: classify the capture, project the manifest from
the in-memory Evidence, serialize it as `--format json` writes it to a file,
authenticate the pair from those bytes, name the environment, prepare the
state as the export does, summarize coverage, scan credential names, then
plan (dry run) or export. `environmentBuildReport.ts` holds the scan and the
naming helpers.
4. Register `environment-build` in the CLI: inspect a HAR through the capture
service as `inspect-web-network-capture` does, or read Evidence once as the
export does, and log stages at info.
5. Switch `scripts/verify-agentenv-environment.mjs` to the build command.
6. Document the command, the report, the hints, what ships, and the view.
7. Tests: coverage summary and hint completeness, the view against the state
view, build equivalence with the three-step path in process and through the
CLI, dry run, naming, refusals, the credential scan, and the view over MCP.
@@ -0,0 +1,96 @@
# Build an environment in one step
Issue: https://github.com/morluto/rea/issues/4933
## Outcome
Building an agent environment bundle takes three commands and a hand-written
view request that embeds the whole capture inline, so large captures hit inline
input accounting. The views apply no opt-in template rules by default, while the
published capture-built environments all use the same four, and a local capture
is named after its origin host (`127-0-0-1-3105`). Nothing reports which tools
an environment serves from its state, or why a write tool is withheld.
`rea environment-build <capture> [directory]` builds the bundle in one offline
step and reports what it serves. A new `environment-coverage` analysis view
reports the same summary for any capture, so an agent can check a recording
before building.
## Requirements
- The capture is a `.har` file, inspected as `inspect-web-network-capture`
inspects it, or capture Evidence JSON from `inspect_web_network_capture` or
`capture_browser_scenario`; any other extension is refused before it is
read, and a HAR document saved as `.json` is refused with a hint.
- The manifest is projected from the in-memory capture as retained Evidence,
with the default rules `after-uid-literal`, `opaque-token`,
`sibling-variance` and `prefix-variance` unless `--template-rule` or
`--format-rules-only` selects others; rules apply in the views' order and a
repeated rule is refused, as the views refuse it.
- For the same capture path, every bundle file is byte-identical to what
`inspect-web-network-capture --format json`, `inspect-analysis-view --format
json` (the same rules in the views' order) and `environment-export` (the
build's name) write, Evidence ids included. The Evidence is serialized once
into one exactly sized buffer, whose bytes are both authenticated and shipped.
- The name is `--name`, else the manifest's, unless the origin is a loopback
name or IP literal: then the capture file's name, with a warning; a file name
that yields no portable bundle name is refused, asking for `--name`.
- The report carries the export's tool count, state, package source and files,
plus the capture and manifest identities, `coverage`, warnings and the next
command, shell-quoted. `coverage`
counts read tools answered from the state and replayed (per view reason), and
lists write tools as served or withheld with their reason, plan record, the
contradicting read and a hint; it is null when the state is unavailable.
- Every write reason has a hint; a reason without one fails to compile.
- Credential-named header names, query parameter names and JSON or form body
members that hold a value are reported as a count and the first 20 record or
event pointers, never values, and never refuse a build; the scan cannot
overflow the stack on deeply nested bodies. A body is read as a form
(`application/x-www-form-urlencoded`, by `URLSearchParams`) or as JSON (the
media types replay parses as JSON) by its recorded media type; a body with
none, or one naming neither, is JSON when it starts with `{` or `[` and a form
when it contains `=` and no whitespace, so plain text and empty bodies yield
no pointers. A browser scenario is scanned by event rather than by record:
every retained request and response network-content event's header values and
body, including a response whose request began before the browser was attached
and a transaction that cannot be attributed to one request and response, and
each transaction URL's query parameters once; its pointers name the event's
index in `events.items` (`event 4 response /token`). Each occurrence of a
repeated form field counts, as each repeated header or query parameter does. A
HAR request recorded with structured form parameters and no text, or the null
text mitmproxy's SaveHar records, is scanned by those parameters. Text bodies
over the 4 MiB scan limit, measured in UTF-8 bytes, including a response body
decoded only as a prefix, are not scanned; they are reported as a
`credential-scan-incomplete` warning with a count and the first 20 record or
event and side pointers (`record 3 response`, `event 4 response`), never
contents, and never refuse a build. Binary bodies hold no JSON or form text
and are neither scanned nor reported.
- The inspected HAR's Evidence and the projected manifest are admitted against
the live heap headroom for parsing and sealing before either is serialized, as
a capture Evidence file is before it is read; one that does not fit is refused
as `resource_constraint` before anything is written.
- An empty or NUL-containing capture, directory or package tarball argument is
refused as invalid input; it is never resolved to the working directory.
- Nothing is written before the bundle stage; `--dry-run` writes nothing,
reports the files a build would write and refuses the package tarballs a build
refuses, admitting, reading and hashing, and releasing the tarball as a build
does before copying it, so a tarball whose contents cannot be read is refused:
a close it cannot confirm is reported as incomplete cleanup that keeps its
owner. The directory must be absent or empty.
- SIGINT is checked between stages and after a dry run's last step: a build
cancelled before the bundle stage writes nothing, and one cancelled during the
write names the written bundle.
- The `environment-coverage` view summarizes the environment-state view with
the same rules, refuses what it refuses and is inferred. It does not apply the
serve-time catalog and state admission checks, and says so in a limitation.
- The `real-agentenv` lane builds its bundle with `environment-build`.
## Non-goals
- No change to the three existing commands' contracts, or to what any view
derives or any environment serves.
- No browser launch or network access: scenarios are recorded with
`capture-browser-scenario` first.
- No `--replace`, no refusal on credential names, and no rewriting of the
capture's absolute path, which Evidence identity includes.
- No MCP export tool; that is a separate change.
@@ -0,0 +1,13 @@
# Tasks
- [x] Record the outcome, requirements and non-goals.
- [x] Add the environment-coverage view and register it.
- [x] Split the export's input read and accept a prepared state.
- [x] Add the build module, its report helpers and the CLI command.
- [x] Switch the real-agentenv lane to the build command.
- [x] Document the command, the report, the hints and the view.
- [x] Add domain, service, CLI and MCP tests.
- [x] Run static, formatting, duplication, documentation and test gates.
- [x] Build the 50 published captures and compare them with the three-step path.
- [x] Build environments end to end on Linux VMs and run them with agents.
- [x] Address the independent review.
@@ -0,0 +1,113 @@
# Verification
Executed on macOS with Node v24.22.1 (the checkout's `.nvmrc`) on main `3d4f5565`, and on Ubuntu 24.04 VMs for the
end-to-end runs.
## Gates
- `npm run -s check:fast`, `npm run -s format:check`, `npx knip`, `npm run -s scan:todos`, `npm run -s docs:check`,
`npm run -s verify:test-discovery` and `npm run -s mcp-catalog:check`: passed.
- `npm run jscpd`: 3.52% duplicated lines over 4348 files, as on main (3.52% over 4341).
- `npx vitest run --project domain --project services`: 9537 passed, 6 skipped and 151 failed. The same 151 fail on main
`3d4f5565` in this environment (9513 passed there); none is new.
- `npm run -s test:prepare`, then `npm run -s test:focused --` the environment build, export, export-inputs and serve CLI
boundaries, the analysis-view CLI and MCP boundaries and the environment serve MCP boundary: 7 files, 67 passed and 1
failed, "projects portable artifact inventory after the original ZIP is removed", which fails the same way on main.
- `npm run verify:agentenv:environment` (the `real-agentenv` lane) passed on Ubuntu 24.04 with `agent-env 0.9.1334`.
## Equivalence and coverage over the published captures
Each of the 50 published capture-built environments' captures (capture Evidence JSON) and four scoped HAR recordings
(EspoCRM, Ghost, NocoDB, Sonarr) was built twice: once with `rea environment-build <capture> <dir> --json`, and once
with the three CLI steps, `inspect-web-network-capture --format json` (HAR only), `inspect-analysis-view` with an
`evidence-file` source and the four default rules, and `environment-export --name <the build's name>`. Every file of
every bundle was compared by SHA-256.
| Inputs | Bundles identical | State served | Read tools from state / replayed | Write tools served / withheld | Build / three steps |
| ------------------------- | ----------------- | ------------ | -------------------------------- | ----------------------------- | ------------------- |
| 50 capture Evidence files | 50 of 50 | 50 | 1,100 / 290 | 50 / 143 | 32.9 s / 59.2 s |
| 4 scoped HARs | 4 of 4 | 4 | 166 / 21 | 5 / 12 | 8.3 s / 12.4 s |
- The coverage of every capture equals an independent census of the environment-state view with the same rules (read
views by shape, write plans by verdict and reason).
- 43 of the 50 captures have a loopback origin and are named after their file (`grafana.capture.json` becomes `grafana`)
with a `name-from-file` warning. 20 carry a `credential-named-members` warning; the published captures were scrubbed by
their pipeline, whose placeholder strings still count.
- The withheld writes' reasons: no-state-target 86, contradicted 16, unconfirmed 14, no-observation 13,
unexplained-change 8, side-effect 4, body-unusable 1, incomplete-entities 1.
## End to end
Five environments last rebuilt on 10-23 were built again with this branch's packed CLI on disposable Ubuntu VMs, four
from their scoped HAR recordings and Metabase from its capture Evidence, with `rea environment-build <capture> <dir>
--name <app> --package-tarball <tarball>`. Each bundle then ran its smoke task, a retrieval eval generated from the
bundle's own manifest and capture, and the write eval from its recorded write spec, with two models through a LiteLLM
proxy (`fireworks_ai/gpt-oss-120b`, `fireworks_ai/glm-5p3-flash`), one run per task.
| App | Input | Served tools of manifest | Served writes (build report) | Smoke | Retrieval gpt-oss / GLM | Writes gpt-oss / GLM |
| -------- | -------- | ------------------------ | -------------------------------------------------- | ------ | ----------------------- | -------------------- |
| Ghost | HAR | 35 of 38 | 0 (3 no-state-target) | passed | 12/12 / 8/12 | 0/2 / 0/2 |
| Metabase | Evidence | 40 of 42 | 1 (create contradicted, update unexplained-change) | passed | 12/12 / 12/12 | 1/2 / 1/2 |
| EspoCRM | HAR | 85 of 88 | 0 (2 contradicted, 1 unexplained-change) | passed | 12/12 / 10/12 | 0/2 / 0/2 |
| NocoDB | HAR | 23 of 28 | 0 (4 no-state-target, 1 unconfirmed) | passed | 6/6 / 5/6 | 0/3 / 0/3 |
| Sonarr | HAR | 49 of 50 | 5 (1 side-effect) | passed | 12/12 / 12/12 | 3/3 / 3/3 |
- Every write task passed with both models when the build report listed its tool as served (Metabase's card delete and
Sonarr's release-profile create, update and delete), and failed when it listed it as withheld: 8 of 24 write episodes
passed, exactly the served ones. On main these apps' other writes are withheld; the published v0.2.0 versions were
built with the state-target and write-rule changes, which serve more of them.
- The retrieval misses are GLM answers on bundles byte-identical to the three-step path's; gpt-oss passed all 54.
- The `real-agentenv` lane, switched to `environment-build`, passed from a fresh checkout on Ubuntu with
`agent-env 0.9.1334` and the host's Docker: five public cases, the smoke task passed in 26.6 s, no new containers.
## Review
An independent review rebuilt the branch and tried to refute it. It confirmed byte-equality for pretty-printed,
CRLF and newline-free Evidence, Unicode HARs across output part boundaries, inline and file view sources, browser
scenario Evidence and relative or symlinked paths, and that the refactored export answers 16 success and refusal cases
exactly as main does. Its findings, all addressed:
- The credential scan recursed and a 4 KB body nested 5,000 deep crashed the build; it now walks with its own stack, and
also scans header names, query names and form bodies.
- SIGINT after the capture was read was ignored; the build now checks the signal between stages and writes nothing when
cancelled before the bundle stage.
- The coverage view does not apply the serve-time catalog and state admission checks; a limitation and the docs now say
so.
- A dry run accepted a tarball the build refuses; it now opens it for reading.
- Repeated `--template-rule` values were deduplicated; they are refused as the views refuse them. A HAR saved as `.json`
gets a hint, file-derived names follow the bundle name rules, `localhost.` is anonymous, and the next command is
shell-quoted.
- The `no-observation` hint now covers updates and deletes of entities the state never held; the docs no longer claim
the build launches no process, state the conditions of byte-equality, and name the headers inspection excludes; the
MCP tool description names the view.
## Review round on main `584120e8`
The branch merged main `584120e8`: the export's file read now returns permission refusals through `Result`, as main
does, under the build's operation name, and the `environment-coverage` sentence sits in main's `inspect_analysis_view`
description. Four automated review findings were fixed, each with a test that fails on the merge:
- A browser scenario's retained header values and the query parameters of its recorded URLs are scanned for credential
names as a HAR's are; the manifest and replay record projections are unchanged.
- `--dry-run --package-tarball` admits and releases the tarball through the export's own regular-file scope, so a close
it cannot confirm is retried and then reported as incomplete cleanup that keeps its owner.
- A dry run checks for cancellation after reading the tarball, before it reports a plan.
- Evidence is serialized into one exactly sized buffer instead of chunks joined by a copy. Those bytes are still parsed
and sealed before authentication, so authentication reads exactly what ships.
On macOS with Node v22.23.1: the static gates passed (`npm run jscpd`: 3.53%), `npx vitest run --project domain
--project services` passed (9753, 6 skipped), the environment boundary projects passed (102), and the focused build,
export, export-inputs, serve and analysis-view boundaries with tool schema validity passed (9 files, 96). Rebuilt over
the same 50 captures and four HARs, all 54 bundles are byte-identical to the three-step path's, with the same coverage,
served writes and warnings as before the round.
## Review round on main `3d84215b`
The branch merged main through `3d84215b`: #6946, which carries this builder, in `27a4e3986`, then #7008. Integrating
#6946 took main's event-based credential scan, which also answers the review finding that a scenario transaction with
duplicate responses or content events hid their credential names; a test that both content events of a transaction
recorded twice are scanned fails on `0895c89bc`. The integration had dropped `0895c89bc`'s tests for form text recorded
without a media type and its `body-unusable` hint; both are restored, and main's scan passes the tests.
Rebuilt at `05566419e` over the same 50 captures and four HARs, all 54 bundles are byte-identical to the three-step
path's, and their warnings and credential counts equal the previous round's.
@@ -0,0 +1,21 @@
# Plan
1. Add the `request` action to the scenario schema: methods, path, query,
headers with page value sources, JSON body and expected statuses; validate
the origin and the body against the start URL; include query and header
secrets in the declared-secret check.
2. Add `PlaywrightScenarioRequest.ts`: check the page's origin, build the URL,
resolve header values (admitting values read from the page to the scenario's
redaction), fetch in the page with a timeout and read the whole response,
then check the status. Dispatch it from the action runner with the start
origin, and add the limitation when a scenario has a request step.
3. Add a real-browser lane, `verify:browser:request`: a fixture item API that
requires a CSRF cookie header and a stored bearer token for writes, a write
session recorded through the CLI, the build's served writes, and a failing
expected status.
4. Add the `export_environment_bundle` contract, effects, CLI alias, catalog
family and handler: check the destination as other MCP output tools do,
resolve the capture bytes, and call the build with an exclusive destination.
5. Write the skill reference, route it from SKILL.md and install it with setup.
6. Document the request step, the MCP tool and recording writes.
7. Tests: request step unit tests, the MCP tool boundary, setup's skill files.
@@ -0,0 +1,83 @@
# Build an environment from a prompt
## Outcome
`rea environment-build` builds a bundle from one capture, but an agent asked to
"make an environment of this app" still cannot finish the job through REA's MCP
server. A browser scenario can only reach an application's API through the UI or
a `goto` GET, so recording the writes an environment serves means scripting every
form, and there is no MCP tool that writes a bundle. Nothing tells the agent how
to record a session the state can learn writes from, or when to stop recording.
A `request` scenario action sends one same-origin request through the page's own
`fetch`, so a scenario can sign in through the UI and then call the application's
API, writes included, with the session's cookies and the CSRF or bearer values
the application's own requests send. An `export_environment_bundle` MCP tool
builds the bundle from capture Evidence, as `environment-build` does. A skill
reference walks an agent from the prompt to a bundle: map, record, check
coverage, re-record by reason, export and hand off.
## Requirements
- A `request` step names a method (`GET`, `HEAD`, `POST`, `PUT`, `PATCH`,
`DELETE`), a path on the start URL's origin, ordered query entries, headers,
an optional JSON body and optional expected statuses. Validation refuses a
path that resolves to another origin, a body on `GET` or `HEAD`, a 3xx
expected status, header names a page's `fetch` cannot set or Chrome drops
(`User-Agent`), a header name given twice, line breaks or control characters
in literal values and prefixes, and a path the URL parser rejects, as a field
issue at the step's path.
- Header values are literals, declared secrets, a cookie the browser holds for
the request URL, or a local storage value on the start origin, optionally read
at a JSON pointer, with an optional prefix; local storage values are read only
while the page is on the start origin. Values read from the page are redacted
like declared secrets from records made after they are read, as stored and as
sent; a value read again keeps its first marker, a colliding label never
unredacts an earlier value, and values shorter than 8 characters are not
redacted.
- The request runs through the page's `fetch` with same-origin credentials, from
a page on the start origin (checked again in the page as it is sent), follows
no redirects, and completes when the response has been read to its end,
without keeping its body. A redirect, a status outside `expected_status`, a
network error or the step timeout fails the step; later steps are cancelled as
for other actions. The page sends only before the step's deadline, which it
receives as an absolute time, and within one second of being handed the
request; a busy page that starts later sends nothing, and the step hands the
request over again while it is neither cancelled nor out of time. A step
cancelled or out of time before the page starts its request sends nothing,
except a request the page starts within that second of being handed it. A
browser whose clock runs ahead of the host's fails the step. Its network
events are attributed to the step.
- Captures with request steps say so in a limitation. A scenario without one
validates, runs and records exactly as before.
- `export_environment_bundle` takes retained capture Evidence or a capture
Evidence file, an absolute output directory that does not exist and whose
parent does, and the build's name, template rules, package tarball and dry
run. It writes the bundle `environment-build` writes from the same Evidence
bytes (retained Evidence serialized as `--format json` writes it) and returns
the same report, validated by its output schema. It refuses an existing
directory, empty or not, a parentless or relative one, unknown Evidence, an
unnamed loopback capture and repeated rules, and writes nothing when it
refuses. Retained Evidence is refused for heap headroom before it is
serialized, as an Evidence file is before it is read. `environment-build` is
its CLI command.
- The skill reference `references/environments.md` is installed with the skill
and routed from SKILL.md. It covers disposable instances only, credentials
from environment variables, the recording rules the state needs, coverage, the
stop rule, the hand-off, and how to reach objects by id when the server
assigns ids: ids the caller chooses, a second scenario, or the UI.
- Coverage of a capture whose records are not all timed, which includes every
browser scenario capture, says the state learns no write clock.
- Stop rule: re-record a withheld write at most twice for a recording gap
(`no-state-target`, `no-observation`, `unconfirmed`, `incomplete-entities`),
once for `unexplained-change` and `no-call`, and stop at once for application
behaviour (`side-effect`, `contradicted`, `body-unusable`).
## Non-goals
- No cross-origin requests, form or multipart bodies, or arbitrary script
actions.
- No retroactive redaction of values a request step reads: a login response
recorded before the step keeps the token it returned, as captures do today.
- No application start, seed or reset, and no publishing of bundles.
- No change to what any view derives or any environment serves.
@@ -0,0 +1,11 @@
# Tasks
- [x] Record the outcome, requirements and non-goals.
- [x] Add the request action, its validation and its Playwright runner.
- [x] Add the real-browser request lane.
- [x] Add the export_environment_bundle tool.
- [x] Write and install the skill reference.
- [x] Document the request step, the tool and recording writes.
- [x] Run static, formatting, documentation and test gates, and the existing browser lanes.
- [x] Build environments from prompts with an agent against live applications on Linux VMs.
- [x] Address the two independent reviews.
@@ -0,0 +1,82 @@
# Verification
Executed on macOS with Node v24.22.1 at `feat/environment-prompt` 8dfd5a4d (stacked on the environment-build branch at
eeab0bda), and on Ubuntu 24.04 VMs for the end-to-end runs.
## Gates
- `npm run -s typecheck`, `lint`, `format:check`, `docs:check`, `mcp-catalog:check`, `verify:test-discovery` and
`npx knip`: passed.
- `npx vitest run --project domain --project services --project adapters`: 16,347 passed and 1 failed,
"stops Inspector target authorization after the current stat settles", which fails the same way on the base fce1aba8
in this environment (16,326 passed, 2 failed there); none is new.
- Boundaries: the bundle tool, analysis view, tool schema validity, environment-build CLI, setup skill and client
lifecycle suites (123 passed), and every browser and scenario MCP and CLI boundary suite (68 passed).
- Real Chrome: `verify:browser:request` (new: 22 request steps, the CSRF cookie and stored token authenticate seven
writes and never reach the Evidence, two queued writes are refused, the build serves the three write tools, an unexpected status fails the step and
cancels the rest, and a redirect to another origin is not followed, the other origin receiving nothing) and
`verify:browser:network` passed. `verify:browser` stops at "Real Chrome stable capture identities did not
reconcile", an `inspect_web_page` comparison this change does not touch, and fails the same way on the base.
- `verify:browser:request` runs each CLI call under the shared package verifier command owner, which stops the CLI and
the browser it launched on timeout, output overflow or failure before the lane's private root is removed, and reports
a failed stop with the root retained; `browserRequestVerifierOwnership.test.ts` proves a still-running CLI's
separately grouped browser is stopped before the root goes.
Scenarios without request steps are unchanged: the action union gains a member, the secrets refactor compiles the same
patterns, the request-step limitation appears only with request steps, and the browser suites and the network lane pass.
## End to end: environments from a prompt
Five self-hosted applications were brought up fresh and seeded on four disposable VMs, loopback only. Claude Code 2.1.310
(`claude-sonnet-5-5` through a LiteLLM proxy) got this branch's packed CLI, registered with `rea setup --client
claude_code` (the MCP server and the skill), and one prompt per application: build an agent environment of the app at
this URL, cover these entities, the instance is disposable but leave the seeded data as found, sign in as this user, the
password is in `APP_PASSWORD`, Chromium is here, write the bundle there. The prompt did not mention the skill. The agent
could use the REA tools, the skill and file tools; Bash and web tools were disabled.
Each bundle was then rebuilt with `environment-build` from its own `evidence.json` and this branch's tarball (the
registry's `rea-agents@6.7.0` has no `environment-serve`); `manifest.json` and `evidence.json` were byte-identical to
the agent's in all five. It ran its smoke task, a retrieval eval generated from its manifest and capture, and the
application's recorded write eval with `fireworks_ai/gpt-oss-120b` and `fireworks_ai/glm-5p3-flash`, one run per task.
| App | Agent run | Recordings | Records | Tools (state / replayed reads) | Served writes | Smoke | Retrieval gpt-oss / GLM | Writes gpt-oss / GLM |
| -------- | --------- | ---------- | ------- | ------------------------------ | ------------- | ------ | ----------------------- | -------------------- |
| Linkding | 182 s | 9 | 66 | 8 (7 / 1) | 0 | passed | 9/9 / 8/9 | 0/3 / 0/3 |
| Grocy | 253 s | 7 | 154 | 22 (16 / 0) | 6 | passed | 10/11 / 10/11 | 3/3 / 3/3 |
| Homebox | 391 s | 9 | 268 | 13 (11 / 2) | 0 | passed | 9/9 / 9/9 | 0/2 / 0/2 |
| Directus | 202 s | 7 | 100 | 25 (16 / 4) | 5 | passed | 12/12 / 11/12 | 2/2 / 2/2 \* |
| Grafana | 143 s | 4 | 100 | 15 (12 / 2) | 1 | passed | 5/5 / 5/5 | 2/2 / 2/2 |
\* The Directus write eval checks that the state holds exactly the provisions the scripted recording read (2); the
agent's recording read all 5, so its count checks fail although every field check passes (0/2 with both models). With
the counts relative to the state's own provisions it passes 2/2 with both.
- Write tasks passed exactly when the build served their tool, with both models.
- The skill loaded unprompted in every run. All five agents signed in through the form with the declared secret,
recorded the API with request steps, checked coverage, stopped by the withheld reasons and exported with
`export_environment_bundle`; each reported the credential-named warning pointers, and none published anything.
- The scripted recordings the published environments were built from, built with this branch, read more (11, 39, 21,
31 and 35 state-backed reads) and serve 7, 3, 2, 2 and 0 writes; the prompted recordings serve 0, 6, 0, 5 and 1.
- Linkding's and Homebox's updates are withheld because a browser scenario carries no request times: the state learns
no clock, so an update the application stamps with a modification time is `unexplained-change`. Linkding's scripted
HAR serves the same PATCH with a clock rule. The coverage view now says so; recording request times on scenario
network events would lift it. Linkding derives each bookmark's archive URL from its creation time, so its create is
`contradicted` in both recordings.
Earlier rounds: in the first, two agents hung on steps without `timeout_ms` and most guessed the scenario schema, so the
reference now starts from a whole scenario with a timeout on every step; the second (same five apps, before the review
fixes) served 1, 6, 0, 5 and 0 writes with the same eval pattern.
## Review
Two independent reviews tried to refute the change.
- The first found a 302 that took a request step to another origin, page values left unredacted under a repeated or
colliding header label, short page values that rewrote ids and hosts across the capture, `User-Agent` accepted but
dropped by Chrome, a request that could still be sent after its step timed out, an origin check separated from the
send, a literal query re-encoded, line breaks accepted in header values, and CLI wording in MCP refusals. All are
fixed and tested; redirects are now checked in real Chrome.
- The second confirmed those fixes and found query entries mishandled after a path ending in `?` or `#`, 3xx expected
statuses that can never match, and padded stored values escaping redaction (fixed), plus the body cost of reading a
page value, short values kept in plain text and the MCP dry run checking its directory (documented). In the
prompted captures, only static assets lost bodies to the size limits.
@@ -24,7 +24,7 @@ the line recorded, from the collection's initial entities, explains it:
nothing else matched); an empty term matches everything, and a term that matched no field leaves the parameter inert
until another line's term does;
- `sort` by the field the value names, with `-` or an `asc`/`desc` suffix, and `direction` for a separate `asc`/`desc`
parameter: numbers by value, text ignoring case, null last ascending, ties (case-only differences included) in state
(or `ascending`/`descending`) parameter: numbers by value, text ignoring case, null last ascending, ties (case-only differences included) in state
order; an empty selection needs no sort;
- `limit`, `offset` and `page` (counted from 1 or 0 only when the line distinguishes them) for paging; zero or a negative limit
lists every match;
@@ -39,8 +39,11 @@ Query totals retain their numeric or canonical decimal-string type. Unknown or d
`state.source` distinguishes a computed response from recording replay; `replay.exact_match` describes only the
retained source envelope and can be false for a correctly computed new query.
Parameter names decide which roles are tried (`sort` for `orderBy`, `search` for `q`), and any parameter may be a
filter or inert. No two parameters of one call take the same `sort`, `direction` or paging role: the answer would
Parameter names decide which roles are tried (`sort` for `orderBy`, `search` for `q`), and any other parameter may be a
filter or inert. A name asking for an order in any common spelling (`_sort`, `sort[]`, `sort[0][field]`, `sorters[...]`,
`sort-column`, `sortKey`, `orderBy`) is only a `sort`; a direction name (`_order`, `sortDirection`, `dir`) given a direction
is only a `direction`, and given anything else is an ordinary parameter; `reverse` is never inert unless false. The same
vocabulary decides which lines set a collection's initial order (see the state view). No two parameters of one call take the same `sort`, `direction` or paging role: the answer would
depend on which applied last, and serving supplies a call's values in name order rather than as recorded, so such a
combination explains no line and such a call replays its recording. A line with more than 12 query parameters is left
unexplained, which bounds the search; names such as `__proto__` are kept as the query's own members. The lines' roles
@@ -48,8 +51,9 @@ merge: lines that disagree drop the parameter, inert values gather, search field
named like its parameter (`shelf`, or `shelf_id`, for `shelf`) can match by coincidence, so it stands only when lines
giving the parameter two different values agree on it; otherwise the parameter takes no role. The merged query keeps the
lines it reproduces, envelope and fields included; any other line keeps its recorded members. The view's `query` names
the roles, those lines, and the envelope members that count every match before paging (`totals`); a `number`-of-items
member such as `numberOfElements` counts the page.
the roles, those lines, and the envelope members that count every match before paging (`totals`), top-level or one
level down in a paging object (`/pageInfo/totalRows`); a `number`-of-items member such as `numberOfElements` counts the
page.
## Serving and the write gate
@@ -61,7 +61,8 @@ change.
- No new browser action: scenarios still cover what the UI requests plus GET
reads through `goto`. A fetch or script action would be a new effect and
needs its own scope decision.
needs its own scope decision. (The `request` action was later scoped and
added by [environment-prompt](../environment-prompt/spec.md).)
- No change to what the scenario redacts. Values the application returns in
response bodies are retained, as they are in HAR captures.
- The api-surface view keeps HAR parents only.
@@ -0,0 +1,22 @@
# Plan
1. Schema: optional `observed_at` (ISO date-time) on `request` events, and
`started_at` (ISO date-time) and `elapsed_ms` (non-negative number) on
`request-finished` and `request-failed` events.
2. Collector: stamp `observed_at` when a request is observed and copy the
browser's `timing().startTime` and `responseEnd` into `started_at` and
`elapsed_ms` when it reports them, admitting the strings with the other
metadata.
3. Records: index each transaction's single finished or failed start and
duration and give each attributable record `startedDateTime` and `time`;
add a limitation when any record is timed.
4. Comparison: drop both fields from network evidence before fingerprinting and
commit the rule in the built-in list and the comparison limitation.
5. Docs: the scenario contract's network evidence and the environment docs'
browser scenario section.
6. Tests: the collector stamps both fields, records take them, the state learns
the HAR's clock and verdicts from a timed scenario and none from an untimed
or partly untimed one, a request that never ended or ended twice falls back
to its observation, comparisons ignore the times (also against an untimed
capture), and the real-Chrome network lane checks every request is observed
and every finished request carries the browser's start and duration.
@@ -0,0 +1,44 @@
# Time browser scenario records
## Outcome
Browser scenario network events record no times, so every record a scenario
projects is untimed. The environment-state view then orders records by
position, detects no overlapping requests and learns no write clock: an update
whose response carries a modification time the application sets is withheld as
`unexplained-change` however it is recorded, while a HAR of the same session
serves it with a clock rule.
Scenario network events now record when each request was observed and how long
the browser took, and scenario records carry them as HAR entries do, so a
scenario capture learns the clock a HAR does.
## Requirements
- Each `request` event carries `observed_at`, the ISO wall-clock time the
provider observed the request start; Playwright reports the browser's start
only once a request ends.
- Each `request-finished` and `request-failed` event carries `started_at`, the
browser's wall-clock start (`timing().startTime > 0`), and `elapsed_ms`, its
time from that start to the end of the response (`timing().responseEnd >= 0`),
when the browser reports them.
- The fields are optional in the schema, so Evidence captured before them still
parses and is read exactly as before. REA versions before them refuse
Evidence that carries them.
- Scenario records take `startedDateTime` from the transaction's single
finished or failed event's `started_at`, else from `observed_at`, and `time`
from its `elapsed_ms` (0 without one), and say so in a limitation; a capture
whose requests are all timed gets the recorded-start-time timeline and a
write clock.
- Scenario comparisons ignore both fields, as a committed built-in rule
(`ignore_network_request_timing`).
- The added metadata is charged to the event budgets as other metadata is.
## Non-goals
- No change to HAR captures, to how the state uses times, or to what a
record's timing means for overlap or clock rules.
- No retroactive timing of captures made before this change.
- No browser start for a request that never ended: its record starts at
`observed_at`, which can trail the browser's start by tens of milliseconds
while the provider reads other responses, with no duration.
@@ -0,0 +1,13 @@
# Tasks
- [x] Record the outcome, requirements and non-goals.
- [x] Add the optional timing fields to scenario network events.
- [x] Stamp them in the Playwright network collector.
- [x] Time scenario records from them.
- [x] Leave them out of scenario comparisons.
- [x] Document the fields and the timed records.
- [x] Add unit, view and real-browser checks.
- [x] Run the gates.
- [x] Re-record stale scenario environments and compare them with their published versions.
- [x] Build prompt-recorded environments with and without request times.
- [x] Address the independent review.
@@ -0,0 +1,73 @@
# Verification
Executed on macOS with Node v24.22.1 at `feat/scenario-request-timing` b92a11b6 (on main d87e73f2), and on Ubuntu 24.04
VMs with snap Chromium 155 for the recordings.
## Gates
- `npm run -s typecheck`, `lint`, `format:check`, `docs:check`, `mcp-catalog:check`, `verify:test-discovery`,
`scan:todos` and `npx knip`: passed.
- `npx vitest run --project domain --project services --project adapters`: 16,380 passed and 1 failed, "stops Inspector
target authorization after the current stat settles", which fails the same way on main in this environment.
- Browser and scenario MCP and CLI boundaries, tool schema validity and the analysis-view MCP boundary: passed.
- `npm run -s verify:browser:network` in real Chrome: every one of the 10 requests carries `observed_at`, and every
finished request carries the browser's `started_at` and `elapsed_ms` (9 of 10; the tenth is the fixture's deliberately
unfinished stream).
- Captures made before this change parse and project exactly as before: the published Pinry (568 requests), PhotoPrism
(277) and Lychee (134) captures give byte-identical manifest and state views, Evidence ids and digests included, on
main and on this branch.
## The same capture with and without request times
Each capture below was recorded with this branch, then built twice: as recorded, and with its request times removed and
the Evidence sealed again as the provider seals it. The difference is this change alone.
| Capture | Records | Timeline without / with times | Writes served without / with times |
| ---------------------------------------------- | ------- | ----------------------------- | ---------------------------------- |
| Linkding, agent-recorded from a prompt | 60 | record order / start time | 1 / 4 |
| Linkding, agent-recorded from a prompt (again) | 65 | record order / start time | 3 / 3 |
| Homebox, agent-recorded from a prompt | 263 | record order / start time | 2 / 3 |
| Homebox, agent-recorded from a prompt (again) | 275 | record order / start time | 2 / 2 |
| Pinry, its published scenario re-recorded | 568 | record order / start time | 0 / 0 (1 clock rule learned) |
| PhotoPrism, its published scenario re-recorded | 273 | record order / start time | 2 / 2 (2 clock rules learned) |
- With times, Linkding's bookmark and bundle edits and Homebox's tag edit are served: their responses carry a
modification time the application sets, which only a clock explains. PhotoPrism's served album create now stamps
`CreatedAt` and `UpdatedAt` from the clock instead of replaying the recorded values. No capture served fewer writes.
- The second Linkding and Homebox recordings gain nothing because their gaps are elsewhere (`unconfirmed` and `contradicted`
writes): an agent's recording choices move coverage more than timing does.
- The prompt-built recordings used a local, unpushed build of this branch merged with the prompt PR (#6946), which adds
the `request` scenario action they need. Claude Code (`claude-sonnet-5-5`) recorded each app from a one-paragraph
prompt, as in that PR's end-to-end check.
- The Linkding bundle with four served writes passes all three of the application's write tasks with
`fireworks_ai/gpt-oss-120b` and `fireworks_ai/glm-5p3-flash` (with the tasks' entity counts taken relative to the
bundle's own state, which holds active and archived bookmarks as two types); the earlier prompt-built Linkding bundles,
recorded without times, passed none. The Homebox bundle passes both of its write tasks with gpt-oss and one with GLM
(GLM deleted the wrong item). The final build's prompt-recorded bundles start and answer retrieval: Linkding
passed its smoke task and 8/8 and 7/8 retrieval tasks, Homebox its smoke task and 3/3 and 3/3.
## Stale published environments
Pinry and PhotoPrism are the oldest published environments recorded as browser scenarios (version 0.1.0, replay only).
Both were re-recorded with this branch from their published scenarios on fresh seeded instances and rebuilt with the
publishing pipeline:
| App | Published 0.1.0 | Rebuilt with this branch |
| ---------- | ------------------------------------------- | ---------------------------------------------------------------------------------- |
| Pinry | 12 tools, replay, retrieval 6/6 and 6/6 | 12 tools, state and clock, no writes served; retrieval 6/6 and 4/6 |
| PhotoPrism | 13 tools, replay, retrieval 10/10 and 10/10 | 14 tools, state and clock, album create and rename served; retrieval 8/10 and 5/10 |
Neither improves, so neither was republished. The retrieval drops do not come from timing: the timed and untimed states
are identical in every view these tasks read. PhotoPrism's album list is answered from the state in name order while
the recorded response that generated the task listed another album first; Pinry's two GLM misses are tool calls
rejected for passing `limit` and `offset` as numbers, under the same tool schemas the published version had.
## Review
An independent review rebuilt the branch and tried to refute it. It confirmed byte-identical views for earlier captures,
that comparisons with and without times compare alike, that each record's duration comes only from its own
transaction, and that both fields are charged to the event budgets. Its findings are addressed: records now start at the
browser's own start time rather than the provider's observation, which trailed it by up to 36 ms while other responses
were read; the docs give the fallback's lag, that earlier REA versions refuse captures carrying the fields, and that the
comparison's normalization digest changes; and tests cover the fallbacks, partly untimed captures and comparisons with
untimed captures.
@@ -45,8 +45,10 @@ tool, view kind, provider, network activity or write effect.
value. Objects at the root or in one member keep precedence. `at` and
`replaceAt` read and replace exactly these forms besides the root and one
member, so the gate and renderers run unchanged.
- An entity's state order is its earliest sighting on the timeline and it cites
its records in timeline order, whichever plan observed it first.
- An entity's state order is its place on the first whole line its collection's
application order lists, else its earliest sighting on the timeline (see
the state view); it cites its records in timeline order, whichever plan
observed it first.
- Each tool description reads `METHOD template; <returns>; recorded statuses
…; replays retained records […]; evidence ev_…` after the untrusted-catalog
warning. `<returns>` describes one level deep the first cited `2xx` record
@@ -81,6 +81,15 @@ later increments.
the state holds (APIs that omit empty or false members), no view of the type
requires it.
Never treat an envelope that is itself an entity as a list.
- A top-level UUID or clock member that differs on every repeat of a path's
successful GET lines with no write between, and across the capture differed
on at least two repeats and never stayed the same, is minted per response
there (a request id). A view names it in `per_response_members` only when
every concrete path the view answers independently proves it minted. A
collection's proof does not establish that its item paths mint the same
member. The shared envelope compares without these common members, and
serving keeps their recorded values; an unproved envelope difference stays
recorded rather than borrowing another path's value.
- Merge observations into one entity per collection and identity: item views'
latest values win over list views', differing views keep their own values,
and a value that changed between repeats of one request line with no recorded
@@ -97,9 +106,30 @@ later increments.
under the view's separator, with its own fraction digits padded with zeros to
the view's width and none dropped; an epoch spelling is computed from the
instant.
- An item or list view names in `sorted_arrays` the fields it lists as string
arrays in code-point order whatever order they were written in (tags kept as
a set). Only the write gate learns it. A field is a candidate when every
initial entity shows it as a sorted string array or null, one with two
distinct strings, and no read the gate counts has listed it unsorted with two
distinct strings. It is learned from a counted read that fails with the
stored order and passes with the field sorted, and only when that read lists
a written array in an order that is neither the written one nor values the
initial state held first, then the new ones as written (an application
ordering by tag id or creation shows that order too). The stored value keeps
the written order, so a write's own answer echoes it; every view of the type
for which the field is still a candidate sorts it as well.
- A collection's initial order is the order of the first line that lists every
entity any initial read shows in it and names no sort, direction (`sort[]`,
`_sort`, `sort-column`, `sortKey`, `orderBy` and like spellings) or random
parameter (`random`, `rand`, `shuffle`, `seed`); a collection without such a
line keeps its entities' earliest sightings.
- A list has `all` membership only when no item-total envelope member (`total`,
`count` and their compounds, not page counts) differs from the number of
entities it lists and no `has_more`, `next` or next-page token is set.
`count` and their compounds, not page counts), at the top level or in a
top-level object beside the list such as `pageInfo.totalRows`, differs from
the number of entities it lists and no `has_more`, `next` or next-page token
is set. Such a member that equals the number listed is a counter; a nested
one is named by its pointer (`/pageInfo/totalRows`), and members beside it
such as `isLastPage` stay as recorded.
- Keep an item or list plan only when it reproduces each request line's latest
initial recording from the state, comparing JSON values without regard to
member order; otherwise record the tool with a reason. Parse bodies only when
@@ -84,6 +84,43 @@ same root regenerates values on every read.
| `document` | `PUT`, `PATCH` or `POST` to a document view's path | that document |
| `none` | anything else | reason `no-state-target` |
**Item actions.** A `POST`, `PUT` or `PATCH` to an item path plus one literal
segment (`/{type}/{id}/archive`) is an `update` of the entity the item path
names, recorded as `address: {by: "action", segment}`, when the segment names
no collection (no state type ends in it, and it is not `delete`, `remove` or
`destroy`) and every successful call answered nothing or that entity: never a
201, and an object holding the id at the type's id field where an item read of
the type shows its entity, with every field that read always shows. A call
answering anything else made a sub-resource, as a comment `POST` does, and
stays `none`. An action's unsent change of a scalar that no other rule
explains takes the value its observation showed, a `set` rule applied on every
call (`archived: true`); a changed object, list or instant has no rule, so the
plan is `unexplained-change`. An action is served only when it sets something
and a recorded call found every field it sets already at that value, since one
change cannot tell a set from a toggle; otherwise it stays `unconfirmed`. Its
call may send no body. An action is observed in its own response, or in a
later item read only while no other tool's modelled write to the entity awaits
a check: that read then checks the other write, and the action's call is one
the gate does not model.
**Bulk writes.** A `DELETE`, `PUT` or `PATCH` at a type's
`collection_template`, or a `POST` to it plus `delete`, `remove` or `destroy`,
names its entities in its body: an array of ids of one JSON kind, or of
objects carrying the type's `id_field`, at the root or as the body's one array
member (`{"IDs": […], "Read": true}`, `[{"Id": 9}]`). Every successful call
must name them the same way, recorded as `address: {by: "ids", member, kind}`
or `{by: "elements", member}`. A path whose last segment is the type's
singular or plural (`/messages` for `/message`) writes that type. An `ids`
member applies the body's other members to every id, an element its own
members; a root id array updates nothing. Each named entity gets the effect as
an item call would, checked by the same reads; one the state does not hold
leaves the call unmodelled there. A bulk update's observation must show the
members the call sent. Serving answers any ids only when the exemplar's
response fits: no body, a body naming none of the recorded ids and holding no
number equal to how many it named (a count), or an array with one element per
id, each showing it as the first does; otherwise the reason is
`body-unusable`.
A `POST` whose path gives it no target is a `create` into the type its returned
id is read under: its exemplar's response names an id (an identity member of the
body or of an object member, or a scalar body or sole member of four characters
@@ -114,7 +151,18 @@ the exemplar.
- **Request body.** The exemplar's request body must be a JSON object that
could enter the state: a JSON-like media type, complete UTF-8 text within
256 KiB, exact numbers, no `__proto__` member. Otherwise the reason is
`body-unusable`. A `DELETE` takes no body.
`body-unusable`. A `DELETE` takes no body. A create's or update's body
whose only member wraps one object in a one-element array (Ghost's
`{"posts":[{...}]}`) is that object, and the plan records the member as
`body.envelope`: serving takes only a body wrapped that way.
- **Query.** Serving takes only the query parameters the exemplar sent, with
its values, since they may change what the body does (Ghost reads `html`
only with `?source=html`): a parameter a call leaves out takes the recorded
value, as the recording sent it, and a call with another value or parameter
is refused, a bulk call included. The gate treats a later recorded call sent
with another query, or wrapped otherwise, as a write its plan does not
explain: it brings the entity to what the call recorded, without
attribution, and a bulk call leaves the entities it names unmodelled.
- **The written entity.** The entity is observed in the exemplar's response,
located by its identity value at the root, in one member, or in a
one-element array, as item views locate it. When the response does not carry
@@ -143,33 +191,64 @@ identity the client sends is an echo:
classes.
6. **Clock.** A timestamp within 10 minutes of the exemplar's start time. The
format is kept: ISO-8601 with its separator, fraction digits and zone, or
epoch seconds or milliseconds.
epoch seconds or milliseconds. An object whose every clock lies between 1
second before the start time (a whole second's truncation) and 2 seconds
after it, in one format, is a nested clock when the type's other entities
hold that field as objects agreeing with it in every other member and
holding other clocks: served, those members take the write's clock and the
others are kept. A reference to another entity, which differs by entity or
was made before the request, is not one.
7. **Sequence.** An integer one greater than the largest value of the field in
the collection, else in the type. The integer may be written as `-`-separated
digit groups of one width of at least two digits (an asset number `000-005`;
not a date, version or address); a served value keeps the recorded value's
groups and zero padding, and digits that outgrow them lead. A later call must
record the value spelled that way.
8. **Unique.** A UUID-shaped string that no other entity of the type shares.
8. **Unique.** A UUID-shaped string that no other entity of the type shares,
or an opaque token (16 to 64 letters, digits, `_` or `-`, with letters and
digits both) that no other field of the entity repeats, where every other
entity of the type holds a token of the same length and none this one.
Serving generates a token of the same length, character classes and
separator positions.
9. **Template.** A string containing a generated value, or else a copied
value, as a delimited token (for example a URL that ends in the new id or in
a slug the call sent under another name). It is rebuilt around the new
value. An echoed value is not a token, nor is a copy of one: it may recur in
unrelated text. A copied member whose value also sits in text no template
rebuilds around it (beside another source in one template, or in a constant)
is accepted only with its recorded value.
unrelated text. A copied or echoed member whose value also sits in text no
template rebuilds around it (beside another source in one template, or in a
constant) is accepted only with its recorded value.
10. **Constant.** Otherwise the exemplar's value.
When a constant, or the text a template keeps, contains a path value of the
exemplar's call as a delimited token, the create is served only for calls
naming the exemplar's collection.
A constant may also derive from a member without holding its text, when the
constant is named after the member: with a reference's `Id` or `_id` left off,
one name's letters and digits begin the other's (`parentId` and `parentName`,
`dateEndDate` and `dateEnd`), three or more of them. Such a member is accepted
only with its recorded value when it names another entity the copy holds (by
its id, among the types whose paths hold the member's name when any does; a
value shorter than 8 characters only under a member named as a reference, such
as `parentId` or `parent_id`) whose own string of 8 or more characters the
constant repeats under another name, neither name reading as a reference; or
when it sends a date or time that the constant's instant lies within two days
of (an end date stored as a time in another zone).
**Body members of a create.** A member is either accepted or not:
- **Accepted.** An echoed member, a member copied whole into a field of another
name and stored under none of its own, or the identity field's value. A
member copied whole must have the JSON type of the field it is copied into.
Every member the exemplar sent is required.
- **Accepted only as recorded.** An echoed member naming the object the entity
nests under the field it refers to (`collection_id` and a `collection`
object), whose other members serving would keep as recorded; and a state
word other fields go with: a lowercase word several entities share, where
every entity in another state holds a field the new one leaves null, or the
reverse (Ghost's `status: "draft"` beside `published_at: null`), unless the
tool's recorded calls sent other values of it. Updates take a member naming
a nested object only as recorded too.
- **Not accepted.** A member the application stored with a different value
(transformed), or one it did not show. A call that sends such a member is a
tool error naming the accepted members.
@@ -191,15 +270,46 @@ as sent in another letter case is the sent value, not a fresh one. A member a la
which the plan's observation does not show, keeps the JSON type that call stored
it as (the body's `kinds`), as an exemplar's member keeps the observation's.
`PUT` requires every member the exemplar sent; `PATCH` and `POST` require at
least one. A field the exemplar's update changed without sending it is
explained as one of:
least one. A sent clock the call left as it was, stored in whole seconds and
naming the second the call ran in, is a `held-clock` the application stamps
anew (Ghost's `updated_at`, which a client sends back as it read it), when it is
the only such member the call sent: a call must send it as the entity holds it,
or it is refused, and it is served as the write's clock. An unsent clock the
call left as it was, stored in whole seconds and naming the second the call ran
in, cannot show whether the update stamps it, unless it names when the entity
was made (`createdAt`, `row_created_timestamp`): the plan stays `unconfirmed`
until another call shows it stamped, and that call's clock rule then holds
without the first call contradicting it. A field the exemplar's update changed
without sending it is explained as one of:
- clock;
- a nested clock: an object the entity held whose only changes are clocks
within 10 minutes of the start time, in one format, where the type's other
entities hold that field as objects agreeing in every other member. The gate
stores the object the first later read of the entity shows when it differs
from the write's only in those members, each within 2 seconds, as Metabase
answers an update with one instant and stores another. Serving refuses it on
an entity holding no such object;
- an increment by one;
- a slug of an echoed member;
- `unique`: a UUID in place of another UUID, that no other entity of the type
holds and the call sent nowhere in its body (a version the application assigns
on every update);
- a slug of an echoed member, judged before a token, since a renamed slug is
spelled like one;
- `unique`: a UUID in place of another UUID, or an opaque token in place of
another of the same length, that no other entity of the type holds and the
call sent nowhere in its body (a version the application assigns on every
update);
- `stamp`: the acting user on a field named `modified…by`, `updated…by`,
`edited…by` or `changed…by` (or `last…`), stored on every call, when the
entity already held the value under its `created…by` counterpart
(`modifiedByName` and `createdByName`), and the call sent no member naming
the same thing (`modifiedById`). The other `…by` field naming the same user
(`modifiedById` beside `modifiedByName`) is stamped with it when the call
showed it at its own `created…by` counterpart's value, even where the entity
lacked it before;
- `derived`: when the call sent exactly one member the entity stores nowhere,
a value showing that member's text, markup aside (at least 8 characters): a
field Ghost derives from the `html` it was sent. The member is accepted only
with its recorded value, and a call sending it is served only on an entity
holding the value the recorded call found (`from`), else refused;
- `renewed`: an array or object whose only changes are UUIDs replaced by ones
that neither it nor another entity of the type held, as child rows an update
rewrites are. The rule names the pointers it renewed at (`*` for any array
@@ -210,7 +320,7 @@ explained as one of:
A field the entity lacked before is a change too when the view that observed
the update had shown the entity, so a field the application fills in on update
follows its rule. Any other change is `unexplained-change`. An increment or a slug applies to
follows its rule. Any other change is `unexplained-change`. An increment, a slug, a stamp or a derived value applies to
every call that could take it, and `unique` and `renewed` to every call, so the
plan is also `unexplained-change` when such a rule would give a call of the
tool another value than the one it recorded, a recorded call kept a `unique` or
@@ -247,7 +357,13 @@ and the state is left unchanged.
collection's order: sorted by the identity field or a clock field (ascending
or descending), `first` or `last`. The rule is the first candidate consistent
with both the initial `all`-membership recordings and the position a later
recorded list shows. With no evidence, the rule is `last`.
recorded list shows. With no evidence, the rule is `last`. A sort by a
random identity (version 4 UUIDs, or generated ids every creation clock lists
in another order) is a candidate only after `last`: the recorded order by it is
a coincidence until a list shows a create placed by it; time-ordered ids
(ObjectId, ULID, TSID, version 7 UUIDs) keep it. When the sorts the recorded
order admits would place the created entity differently, none is claimed
before `last` until a later list settles it.
## The gate
@@ -308,7 +424,8 @@ call that serving would not apply, such as a create outside its tied
collection, keeps its evidence.
**Reads.** A read is checked when the served environment would answer it
from the state:
from the state (an item, list or document read compares without the view's
`per_response_members`):
- an item read of a state-backed item view, of the entity serving would name:
in a held collection the read's path matches, each segment verbatim or either
@@ -372,12 +489,40 @@ refuses to answer with.
does not excuse a newer write the list shows undone. An item read checks the writes an unmodelled
write did follow only on the members they sent, and a list with such a write
is not checked again: the unmodelled write may explain the rest.
- A failed item read with no such unmodelled write contradicts the writes that
changed the whole entity (its create or delete). When one of them is
exercised, each exercised update is checked instead on the fields its effect
holds, and confirmed when those show as the copy holds them and its latest
call's response shows every other field it and the read both show (but
volatile ones and those other updates wrote) as the read does: serving
answers an update from the copy, so a response that shows the entity
otherwise would be answered wrongly. A field the copy lacks that the read and
an exercised update's response both show, with the same value other than
null, is that update's change: an update whose effect does not hold it is
neither confirmed nor contradicted by the read, and when such changes are
the read's whole difference the entity's own writer is not contradicted.
When the read differs on a field the copy holds that no exercised update
wrote, any of the writes may have caused it, and each is contradicted.
- A failed item read whose field newly embeds an entity a create made (an
object in the field, or within it, naming it by its id and by another of its
strings of 3 or more characters, 8 or more when the id is shorter than 8),
which the copy's value lacks, contradicts that create: serving would not show
the change it made there.
- A mismatch on an entity or collection that no write changed contradicts,
as `side-effect`, the modelled writes since that previous check whose paths
are segment-prefix related to the read's path.
are segment-prefix related to the read's path. When a refused write's
recorded outcome since then left the entity lacking a field every recording
of its view carried, the create that made it is contradicted too: an outcome
merges only what its answer showed.
- After a failed item check, the copy takes the read's recorded values (or
drops the entity on a recorded absence). A list whose check failed is not
checked again.
- A failed item or list read the gate counts passes when the view lists some
string-array fields sorted and its order rules out the written one and the
order the values were first held in (see `sorted_arrays` in the state view);
the type's views keep the fewest such fields the read needs, and the read
checks the writes it exercises. A counted read listing such a field unsorted
rules it out for its view.
- A list that fails only for where one create placed its entity passes when
another of that create's insert candidates reproduces it, which then
becomes the plan's rule, unless an earlier list check relied on the rule
@@ -410,7 +555,20 @@ served. Their descriptions say the tool:
- applies its effect to the served state;
- names its target type;
- lists its required and accepted body members, quoted and sampled within the
description budget as response fields are.
description budget as response fields are, the members it accepts only with
their recorded value, with that value when its JSON text is 120 characters
or less, and the envelope a body wraps its object in;
- names the query its recording sent.
The input schema says the same: each query parameter the recording sent is
optional, defaulting at execution to its recorded value, with a nonempty value
as the only `enum` member; an empty value is described and enforced at execution
because some clients reject empty literals. A body that wraps its
object requires that member, an array of one object; and a member accepted
only with its recorded value carries that value as `const` when it is short
and neither a boolean nor an empty string (some clients reject these literals), else a description
saying it must equal the recorded value. A refused member's message names the
recorded value the same way.
The discovery budget charges them. `--replay-only` and the replay fallback are
unchanged.
@@ -493,7 +651,18 @@ covers its arguments; otherwise it keeps the recorded page members.
**Provenance.** Write answers carry `state: {source: "state", view: "create" |
"update" | "delete", type, collection, id}`, or `{source: "state", view:
"document-write", path}`.
"document-write", path}`. A bulk answer adds `ids`, every entity it wrote in
body order, `id` naming the first.
**Bulk and action calls.** A served bulk tool takes its body as recorded (an
array of objects or of ids of the recorded kind, or an object), a `DELETE`
included, and `environment-compare` sends a probe's body to it; every id must
name an entity the state holds, else the call is refused and the state is
unchanged. A bulk
update with a `unique` or `renewed` rule writes one entity per call, since one
call draws those values once. A bulk write changes or removes its entities in
one step, so a call the state cannot hold changes none of them. A served item action's path names its entity
before the action segment.
**Data plane and export.**
@@ -0,0 +1,14 @@
# Implementation plan
1. **Blame and the constant guard.** A module attributes a failed item read to the writes whose fields it disagrees on,
and create explanation pins members a constant derives from.
2. **Update rules.** A module holds the stamp, derived, held-clock, nested-clock, token and envelope rules; write
plans record the recorded query and pinned members, and serving and the gate apply them alike.
3. **View order.** A module orders collections by whole order-free lines, another learns sorted string arrays from
read-backs, and the list query and the initial order share one order vocabulary.
4. **Bulk writes and item actions.** A module addresses writes at a collection or an action path; the gate applies
them per entity and serving answers them atomically.
5. **Per-response members.** A module finds members a path mints anew and leaves them out of that path's comparisons.
6. **Nested counters.** List facts and rendering read and write totals through JSON pointers.
7. **Tests and docs.** Each step's tests fail on the step before; the state-view, state-writes, list-queries and
state-coverage specs and the environment docs describe the new behaviour.
@@ -0,0 +1,88 @@
# Serve writes by what their recordings show
Issue: https://github.com/morluto/rea/issues/4933
## Outcome
After state targets, 30 write tools across the 50 published capture-built environments had a state target but were
refused (contradicted, an unexplained change, or a side effect), and 59 still had none. A diagnosis traced each
refused tool to the fields that differed between the gate's state and the recorded read, and named seven shared
causes. Seven of the 30 are refusals REA should keep (asynchronous jobs, rendered markup, reverse embeds, appended
relation rows), and six need only a better recording. This change addresses the causes REA can fix:
- **Blame too broad.** A failed read contradicted every write it exercised, whatever fields differed.
- **Unsent update changes.** An update changing a field it did not send had no rule unless the change was a top-level
clock, unique value, slug, renewal or increment.
- **View order.** Collections took their initial order from the earliest sighting, and arrays a view lists sorted were
compared in written order.
- **Targets at a collection or an action path.** Bulk writes naming entities in the body, and actions at
`/{type}/{id}/{verb}`, had no target.
- **Per-response members.** A UUID or clock minted on every response made a view's envelope vary.
- **Nested counters.** A list total inside a paging object (`pageInfo.totalRows`) stayed at its recorded value.
## Behaviour
- **Blame.** A failed item read with no interfering unmodelled write contradicts the creates and deletes it exercised.
It contradicts an update only on a field that update wrote or its response showed, and a field the state lacks that
the read and the update's response both show is the update's change. A read that embeds a created entity elsewhere
and the state does not contradicts the create.
- **Constant guard.** A create member is accepted only with its recorded value when a constant derives from it: its
echoed text inside the constant, a referenced entity's string, or an instant beside a sent date, matched by related
field names. Only successful calls sent with the exemplar's query show that a member may vary.
- **Update rules.** An update's unsent change can be a modified-by stamp beside a created-by counterpart (with its id
sibling), a value derived from the one sent member stored nowhere (accepted only while the target holds the recorded
before-value), or a sent clock other than a creation clock left in the call's own second, which every call must then
send with the value the entity holds. An unsent clock left in the call's own second keeps the update unconfirmed until
another call shows it stamped. Objects whose clocks moved with the request are nested clocks, stored from the
read-back on creates and updates and refused once any call left the object as it was; opaque tokens of a type's own
length are generated; an enveloped body is learned as its one object. A write takes only its recorded query, and a
call sent with another neither confirms nor teaches its plan: a create or update only brings the state to what it
recorded, and a delete, document or bulk call leaves its target unknown. An echoed reference to a nested object or a
state word paired with other fields stays at its recorded value. The served catalog names these values.
- **View order.** A collection's initial order comes from the first list line that shows every entity its type's
sightings do and applies no order. A view renders a string array sorted only after a counted read-back shows an
order that neither the written order nor existing-values-first explains, among the entities that read covers in its
own collection. A later read contradicting a learned sort makes that view recorded and removes its earlier
write confirmations and saved overrides; types and entities with no surviving state view leave the snapshot.
The read's formerly sorted fields are compared as string multisets, so inconsistent ordering supplies no new
proof and does not hide real value mismatches or contradict an independent view's valid write confirmation.
Retired views keep a comparison-only representation to check later reads for value contradictions; they never
regain serving authority or confirm writes through those checks.
Sort and direction parameters share one vocabulary and are never learned as inert. A create placed by disagreeing
sorts or a random id falls back to `last`.
- **Bulk writes.** A write at a type's collection whose body names entities by id, as an id array or objects carrying
the id field, deletes or updates each one, all or none; serving refuses it unless every id is held. Body ids are taken
as written, and a call naming one entity twice is refused. Each entity's generated values come from that entity,
sharing the call's clock and counters. A delete requires the members its exemplar sent besides the ids, with their
recorded values. A recorded response is served when it names none of the ids and counts nothing, or is an array with
one element per entity the body names, in the body's order, each that entity with the first element's members or its
id spelled as the first spells its own; serving renders each element from the entity the call wrote, and any other
response naming the ids refuses the tool. Comparison translates a bulk body's ids with the mapping of the collection
the write targets.
- **Item actions.** A write to an item path plus a literal segment that names no collection, whose calls answer
nothing or the entity (never a 201, and with every field its item read always shows), updates that entity; a scalar
it changes without sending is set on every call. It is served only once a call found every field it sets already at
that value, so a toggle is never served. An action never takes a read another write to the entity still needs.
- **Per-response members.** A top-level member whose value is a UUID or a clock is minted per response on a path when
it differs on every repeat of that path's successful GET lines with no write between, and across the capture it
differed on at least two repeats and never stayed the same. A shared view leaves it out only when every concrete
path it answers independently proves it minted; a collection's proof never transfers to its item paths. Item
envelopes and the write gate's item, list and document reads compare without these common proved members.
The view names it in `per_response_members`, and serving keeps the recorded value.
- **Nested counters.** An item total in a top-level object beside the list is a counter on the same evidence as a
top-level one, rendered from the state through its pointer. A top-level member whose name starts with `/` is named by
its pointer (`/~1total`).
## Acceptance
- Over the 50 published captures, served write tools rise and none is lost except where the evidence cannot show the
write's own effect; write tools with no state target fall.
- Read calls that state serving answers keep matching their recordings, before the first write and across sessions.
- Every newly served write answers a call other than the recorded one correctly, or refuses it.
## Not included
- Soft deletes, appended relation rows, rendered markup and reverse embeds; creates completed from later reads.
- Templating literal ids by value (an opt-in rule served two deletes whose targets came from unserved writes wrongly).
- Identity aliases and resource names; per-response values that differ as instants.
- A replayed write leaves the state as it was, so a served write on an entity only a replayed write made refuses.
@@ -0,0 +1,14 @@
# Tasks
- [x] Blame a failed read on the writes whose fields it disagrees on, and pin members constants derive from.
- [x] Explain unsent update changes, nested clocks, tokens and envelopes by what the recording shows.
- [x] Order collections by whole lines and sort only arrays a read-back proves sorted.
- [x] Serve bulk writes at a collection and target item actions.
- [x] Set aside response members a path mints anew on every read.
- [x] Count a list total nested in a paging object.
- [x] Take a created entity's nested clocks from its read-back.
- [x] Confirm no update whose clocks or fields the evidence cannot show it writing.
- [x] Show pinned query and body values in the served catalog, and apply bulk writes atomically.
- [x] Add tests that fail on the step before each change.
- [x] Measure the 50 published captures, the served smoke and recorded-read serving after each step.
- [x] Run static, formatting, duplication, documentation and test gates.
@@ -0,0 +1,180 @@
# Verification
Executed on macOS with Node v22.23.1, one change at a time on the state-targets branch, over the 50 published
capture-built environments (each app's latest capture). The served smoke rebuilds each manifest with the tree under
test and applies every served write tool. The serving check answers every recorded read made before the capture's
first write through state serving and compares it with the recording; it reads no calls from the five
browser-scenario captures. The session replay answers every recorded call in order, writes included, through state
serving, reading records with REA's own capture parser, and compares the reads; it covers all 50 captures.
## Gates
- `npm run -s check:fast`, `npm run -s format:check`, `npx knip`, `npm run -s scan:todos`, `npm run -s docs:check`,
`npm run -s verify:test-discovery` and `npm run -s mcp-catalog:check`: passed.
- `npm run jscpd`: 3.57% duplicated lines over 4235 files.
- `npx vitest run --project domain --project services`: 9545 passed, 6 pre-existing skips. The 10 failures (JavaScript
Map endpoints, a returned invocation's alias graph, Frida cleanup) fail the same way on main.
- `npm run -s test:prepare`, then `npm run -s test:focused --` the environment serve and export CLI boundaries, the
serve and served-writes MCP boundaries, the analysis-view CLI and MCP boundaries, the compact input schema boundary
and tool schema validity: 8 files, 91 passed.
## Measurements
| Step | Served | No state target | State-backed read views | Reads before first write | Session reads |
| ---------------------------- | ------ | --------------- | ----------------------- | ------------------------ | -------------- |
| state targets | 62 | 59 | 1,151 | 2,085 of 2,155 | 2,764 of 3,022 |
| blame and constant guard | 63 | 59 | 1,151 | 2,085 of 2,155 | 2,764 of 3,022 |
| update rules | 68 | 59 | 1,151 | 2,085 of 2,155 | 2,763 of 3,022 |
| view order | 69 | 59 | 1,151 | 2,085 of 2,155 | 2,763 of 3,022 |
| bulk writes and item actions | 71 | 48 | 1,151 | 2,085 of 2,155 | 2,763 of 3,022 |
| per-response members | 71 | 46 | 1,153 | 2,087 of 2,162 | 2,766 of 3,031 |
| nested counters | 71 | 46 | 1,153 | 2,087 of 2,162 | 2,766 of 3,031 |
| created nested clocks | 71 | 46 | 1,153 | 2,087 of 2,162 | 2,766 of 3,031 |
| unshown writes refused | 69 | 46 | 1,153 | 2,087 of 2,162 | 2,766 of 3,031 |
| catalog values, atomic bulk | 69 | 46 | 1,153 | 2,087 of 2,162 | 2,766 of 3,031 |
| review fixes | 69 | 46 | 1,153 | 2,087 of 2,162 | 2,766 of 3,031 |
| scoped envelopes, bulk rows | 69 | 48 | 1,151 | 2,085 of 2,155 | 2,763 of 3,022 |
All 187 write tools are counted at every step, and the served smoke applies every served write tool.
- **Scoped envelopes, bulk rows:** a shared envelope masks only members minted on every path, so Vault's identity entity
and group item views, which matched one of four and one of three recorded reads from the state, replay their
recordings, and Vault's entity update and delete lose their state target. Serving bulk responses that list each
written entity changes no capture's verdict.
- **Review fixes:** literal bulk ids, per-entity bulk values, pinned bulk delete members, comparison ids per
collection, echoed creation clocks, required held clocks, nested clocks checked against every call, learned sorts
kept open to contrary reads and limited to the read's own entities, one query check per recorded call, guards
relaxed only by successful calls with the exemplar's query, and slash-named counters. No tool's verdict and no
session read changed; each fix has a test that fails without it.
- **Newly served:** Ghost's post create and update, Metabase's card create and update, n8n's workflow update,
Navidrome's playlist create, and the NocoDB and Mailpit bulk deletes.
- **No longer served:** Gitea's issue update. Its create, update and read share one second, so the evidence cannot show
the update stamping `updated_at`, and serving it left the clock as it was.
- **Refused that were briefly served on this branch:** EspoCRM's task update, for the same reason, with `modifiedById`
absent from its create response.
- **Session reads:** one read is lost at Ghost #74, a 404 right after the served create whose delete the recording
lacks; Vault #80 answers 404 after an unserved create, as a served read on an entity only a replayed write made does.
- Ghost's create and update accept only the recorded html, and Metabase's create only the recorded collection,
display, type and query; the served catalog shows those values.
Every change had an independent review that rebuilt it and tried to refute it; the corrections they found are part of
the change. A final review of the whole branch found one wrongly served update (EspoCRM) and the hidden pinned values,
both fixed.
## Cost
Over the 50 captures, one Node v24.22.1 process per sweep builds the environment-state and environment-manifest views
of every capture (macOS arm64, warm medians of three runs; the cold first run is within 2% of them):
| Tree | Wall | CPU | Peak RSS | Environment-state view | Its results |
| ----------------------- | ------ | ------ | -------- | ---------------------- | ----------- |
| main `d87e73f2` | 2.61 s | 3.41 s | 516 MiB | 1,791 ms | 3.69 MB |
| state targets (`#6385`) | 2.96 s | 3.82 s | 540 MiB | 2,093 ms | 3.84 MB |
| this branch | 3.06 s | 4.07 s | 601 MiB | 2,224 ms | 3.85 MB |
The write rules add 0.10 s wall, 0.25 s CPU and 61 MiB of peak memory to the sweep while serving 7 more write tools.
Agent tokens were not measured; the views' results grow by 0.3%.
## Not included
- Templating literal ids by value: an opt-in rule served Grafana's folder delete and Vault's policy delete, but both
targets came from unserved writes, so serving them answered their own recorded calls wrongly.
- Completing a created entity from a later read: it served Bookstack's page create wrongly.
- Soft deletes, appended relation rows, rendered markup, reverse embeds, identity aliases and resource names.
## Linux integration and review corrections, 2026-10-24
The following is separate from the historical 50-capture measurements above.
Node 24.22.1, baseline `3ce311757`, reviewed implementation `352c0278d`:
- 1,236 selected source tests passed. Typecheck, built lint/module boundaries,
format, knip, metadata, architecture guards and documentation checks passed.
- Seven review regression checks failed before correction and passed after it.
Earlier structured update values now retain canonical equality digests within
the history budget. A shared envelope masks only members independently proved
minted on every concrete path. Empty advertised literals are widened while
execution enforces their values, and pinned queries are optional so their
execution defaults can run. Numeric and string bulk id arrays stay distinct.
- Nineteen compiled SDK/schema checks passed, including real HTTP MCP discovery,
empty fixed values, omitted empty and nonempty queries, and unchanged state
after refused writes. Live Gemini API execution was not performed.
- Four public datasets were downloaded at immutable Hugging Face revisions:
NocoDB `d0f7e040`, Ghost `6d5b2d1c`, Metabase `fa2aa0fd`, and Mailpit
`416b29be`, under `earakely-scale/*Env-REA`. Capture hashes, full calls,
independent recorded JSON comparisons, runtime identities and resource
receipts were retained locally. These were recorded real applications;
the original applications were not re-recorded in this verification.
| Capture | Eligible write tools before / after | Initial reads matching | Session reads matching |
| -------- | ----------------------------------- | ---------------------- | ---------------------- |
| NocoDB | 0 / 3 | 20 / 20 | 80 / 92 |
| Ghost | 1 / 1 | 19 / 20 | 72 / 88 |
| Metabase | 1 / 2 | 19 / 22 | 99 / 122 |
| Mailpit | 0 / 1 | 51 / 52 | 51 / 56 |
Every previously matching read remains matching, record by record. Real CLI
listeners and the MCP SDK reproduce the shared application's outcomes. The
feature session applies six NocoDB mutations, one Ghost deletion and one
Metabase update; the baseline applies only the Ghost deletion. A `state`
answer with view `missing` is not counted as an applied write. Mailpit exchange
65 is refused because its target was introduced only by an unserved create.
Eligibility does not establish that every recorded write can run on the initial
state, and the remaining read mismatches are preserved coverage gaps.
Three alternating fresh producer sweeps over all four captures had median
wall/CPU/cgroup peak of 5.360 s / 4.263 s / 327.3 MiB before and
4.960 s / 4.357 s / 323.1 MiB after. Ranges overlap. One complete SDK session
per app/revision, 403 versus 413 calls, took 20.905 s / 19.039 s / 508.7 MiB
before and 23.426 s / 19.396 s / 462.8 MiB after; these are single observations
with different write coverage, not a speed or memory improvement claim.
Catalog estimates (`cl100k_base`) were 8,630 / 9,408 tokens for NocoDB,
11,355 / 11,355 for Ghost, 12,162 / 12,648 for Metabase and 2,955 / 3,138
for Mailpit. Arguments, instructions and text replies were also counted in
retained receipts; actual model usage, prompts and client rendering were not
measured. No token saving is claimed.
Processes were fresh, OS caches warm or uncontrolled, on a shared Linux host.
Each task used one CPU, a 3 GiB early stop / 4 GiB hard limit and zero swap.
All eight recorded-data task groups exited zero, verified payload cleanup,
and observed no OOM or limit events. Payload-exit counters were verified;
final whole-scope counters were unavailable. Downloads, builds and runtime
identity preparation are excluded from task measurements. The current
four-capture verification does not requalify the earlier 50-capture totals.
### Bulk-response follow-up qualification
The follow-up at `f4bc513dd` classifies every returned bulk element and checks
all recorded calls with the shared serving renderer. Nested parsed-body leases
retain their enclosing scratch charge. Literal JSON IDs never pass through a
synthetic URL. The author's subsequent bulk regressions and measurement note
were retained when integrating `a319ba1af`; their historical census does not
measure this follow-up.
A fresh four-capture producer sweep and real CLI/MCP replay retained every
previously matching read (109/114 initial, 302/358 session). NocoDB, Ghost and
Metabase plans and complete SDK calls equal `9942863b2`. Mailpit's text/plain
`ok` deletion acknowledgement is not parsed by the state JSON owner, so its
contents remain unknown and its write tool is now withheld as `body-unusable`.
The prior session already refused this deletion for a missing target; no
previously applied mutation was lost. Plain-text acknowledgement support remains
unverified rather than treating unknown contents as empty. The three other
captures retain the same eight applied mutations.
This qualification-only sweep took 5.857 s wall / 5.413 s CPU / 329.9 MiB peak;
the complete SDK replay took 23.264 s / 22.298 s / 474.0 MiB. These single
observations are not pooled with the earlier performance cohort. Payload cleanup
and payload-exit resource counters were verified, with no OOM/limit events;
final whole-scope counters and actual model token usage remain unmeasured.
## Review round on main `3d84215b`, 2026-10-24
Three automated review findings were fixed in `d0198389f` and `7c25ad4dc`: a create compares the other calls' request
bodies one at a time inside their parsing reservations, an echoed reference to a nested object stays pinned when the
plan cannot rebuild the object, and a sorted read view a later read contradicts is recorded and loses the
confirmations it gave. On macOS with Node v24.22.1 at `05566419e`, the static gates passed, `npx vitest run --project
domain --project services` passed (10,164, 6 skipped; the reference-lifetimes file whose worker crashes on main was
excluded) and the focused build, export, serve and analysis-view boundaries passed (10 files, 93). Over the same 50
captures every write plan, session replay and served read equals `0895c89bc`'s: 187 write tools with 68 served, 68 of
68 smoke calls, 1,151 views, and 2,085 of 2,155 initial and 2,763 of 3,022 session reads matching.
+40 -17
View File
@@ -72,6 +72,16 @@ preparation use that entrypoint; a raw Turbo compiler task without matching
identity fails instead of publishing an ambiguous successful cache entry.
A missing binary on a supported host also fails without silently falling back.
Development verifiers load their shared command, terminal and file owners from
source using Node's in-process TypeScript transformation. A single process-lifetime
loader handles this checkout's `src/` and `scripts/lib/` graphs, resolving missing
relative `.js`/`.mjs` imports to their `.ts`/`.mts` source. Existing JavaScript and
modules outside those roots keep native resolution and failures. Node's module
cache shares source identities within the verifier process; transformed output
is not cached across Node versions. Restart the verifier after source edits.
This avoids a cold-cache compiler subprocess surviving command cleanup; the
live process-lineage checks remain required.
The Rust compiler defaults to at most two checker threads. For `--noEmit` checks,
`--maxMemory 3G` also sets a soft memory target; emit builds do not use that target.
Explicit compiler arguments can override the defaults. These controls do not
@@ -838,16 +848,17 @@ installation (`application.java.min` through `application.java.max`). Current
12.1 releases require JDK 21 or newer and set no maximum. The lanes below still
prove behavior on the verified Ghidra 12.1.4 and JDK 21 build.
| Ghidra lane | Supported runner/target | Additional local tools |
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| `npm run verify:ghidra` | Linux x64/arm64 ELF or macOS x64/arm64 Mach-O | Host C compiler, Ghidra 12.1.4, and full JDK 21 |
| `npm run verify:ghidra:seeds` | POSIX; raw x86 fixture; seed ordering, BSS labels, analyzer readback, not-applicable and unknown analyzer names | Ghidra 12.1.4 and full JDK 21 |
| `npm run verify:ghidra:swift` | macOS x64/arm64 Mach-O | Host Swift compiler, Ghidra 12.1.4, and full JDK 21 |
| `npm run verify:ghidra:riscv` | Linux/macOS x64/arm64 host; source-owned RV32/RV64 ELF through CLI and MCP | RISC-V Clang, LLD, LLVM readelf, Ghidra 12.1.4, and full JDK 21 |
| `npm run verify:ghidra:switch` | Linux x64 ELF; GCC/Clang optimized and stripped switch fixtures | GCC, Clang, GNU nm/objdump/strip, Ghidra 12.1.4, and full JDK 21 |
| `npm run verify:ghidra:aarch64-jump-table` | Any supported Ghidra host; AArch64 ELF; signed/unsigned byte/halfword tables; host ARM64 Mach-O | Clang with AArch64 target support, Ghidra 12.1.4, and full JDK 21 |
| `npm run verify:ghidra:cross-format` | Any supported Ghidra host; also analyzes AArch64 ELF, x86-64 PE, and x86-64 Mach-O | `clang`, LLD, and `lld-link` in addition to host-lane prerequisites |
| `npm run verify:ghidra:windows` | Controlled Windows x64 with native x86-64 PE; `-- --x86` selects native x86 PE | Ghidra 12.1.4, full JDK 21, and the matching native artifact |
| Ghidra lane | Supported runner/target | Additional local tools |
| ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| `npm run verify:ghidra` | Linux x64/arm64 ELF or macOS x64/arm64 Mach-O | Host C compiler, Ghidra 12.1.4, and full JDK 21 |
| `npm run verify:ghidra:seeds` | POSIX; raw x86 fixture; seed ordering, BSS labels, analyzer readback, not-applicable and unknown analyzer names | Ghidra 12.1.4 and full JDK 21 |
| `npm run verify:ghidra:swift` | macOS x64/arm64 Mach-O | Host Swift compiler, Ghidra 12.1.4, and full JDK 21 |
| `npm run verify:ghidra:riscv` | Linux/macOS x64/arm64 host; source-owned RV32/RV64 ELF through CLI and MCP | RISC-V Clang, LLD, LLVM readelf, Ghidra 12.1.4, and full JDK 21 |
| `npm run verify:ghidra:switch` | Linux x64 ELF; GCC/Clang optimized and stripped switch fixtures | GCC, Clang, GNU nm/objdump/strip, Ghidra 12.1.4, and full JDK 21 |
| `npm run verify:ghidra:aarch64-jump-table` | Any supported Ghidra host; AArch64 ELF; signed/unsigned byte/halfword tables; host ARM64 Mach-O | Clang with AArch64 target support, Ghidra 12.1.4, and full JDK 21 |
| `npm run verify:ghidra:cross-format` | Any supported Ghidra host; also analyzes AArch64 ELF, x86-64 PE, and x86-64 Mach-O | `clang`, LLD, and `lld-link` in addition to host-lane prerequisites |
| `npm run verify:ghidra:windows` | Controlled Windows x64 with native x86-64 PE; `-- --x86` selects native x86 PE | Ghidra 12.1.4, full JDK 21, and the matching native artifact |
| `npm run verify:ghidra:dol` | Any supported Ghidra host; source-owned PowerPC GameCube DOL fixture built from a linker script (never executed) | Clang with PowerPC target, LLD, GNU readelf, Ghidra 12.1.4, and full JDK 21 |
The optional Swift lane compiles a source-owned fixture and checks modern and
provider-demangled procedure names through real stdio MCP, with and without the
@@ -1002,6 +1013,16 @@ application execution is required. The lane compares real CLI/MCP package,
class search, class inventory, method decompilation and incoming references.
See [Android analysis](android-analysis.md) for boundaries and resource budgets.
The **Real Android metadata bridge** workflow compiles the Java bridge and runs
its metadata regressions against the same checksum-pinned engine and public APK.
It runs on branch pushes affecting its bridge, fixture or engine inputs, and can
also be dispatched manually or called by another workflow. It uses the runner's
JDK with two active processors and a 512 MiB heap; it needs no npm dependency
installation. This lane checks exact raw/display class selectors and the existing
cold-metadata regressions. It does not establish CLI/MCP parity or application
execution behavior. The broader `verify:android` lane checks CLI/MCP parity;
neither lane runs the APK.
`verify:jeb` requires a caller-started JEB client serving MCP at
`REA_JEB_MCP_URL` (default `http://127.0.0.1:8425/mcp`) with a project
already open, and exercises shared application/provider client inspection, unit listing coverage,
@@ -1924,13 +1945,15 @@ would remain after the pause, the verifier waits 90 seconds and runs once more,
recording `attempts: 2` and `retried_for` in the receipt. Any other failure
ends the lane at once.
The verifier writes the Grafana-shaped HAR the view tests own, inspects it and
projects the `environment-manifest` view with all three template rules through
the built CLI, packs the checkout with `npm pack`, exports the bundle with
`--package-tarball` so the image installs this checkout rather than the
registry package, and runs `agent-env run` on the bundle. It checks that the
export reports the planned file set with the inputs copied byte-exact and a
bundle served from the derived state, which serves fewer tools than the
The verifier writes the Grafana-shaped HAR the view tests own, packs the
checkout with `npm pack`, builds the bundle from the HAR with
`rea environment-build --package-tarball` through the built CLI, the command
users run, so the image installs this checkout rather than the registry
package, and runs `agent-env run` on the bundle. It checks that the build
reports the 28-record capture, the default template rules, the planned file set
with the shipped manifest naming the reported capture, a coverage summary whose
read and served write tools add up to the served tool count, and a bundle
served from the derived state, which serves fewer tools than the
manifest because write tools the capture did not confirm are not served, that the runner exits `0` with
`tasks/smoke.json` reported as passed and the verifier's description naming
that served tool count, and that no container exists
+5
View File
@@ -53,6 +53,11 @@ satisfy. A plan without execution does not establish runtime behavior.
- Use strict inputs with explicit target identity and required fields. Preserve
meaningful target, action, capture, and output choices; remove ignored options
and repeated permission declarations.
One-shot CLI provider inspections retain the caller's exact target spelling
in Evidence `parameters.input_path`; the subject keeps the canonical inspected
file identity. Keep request provenance separate from provider arguments.
Snapshot entries bind Evidence parameters to provider queries, so changes to
snapshot-backed request provenance must preserve that cache contract.
- Return the requested facts inline with artifact/provider identity, source
locations, Evidence references, coverage, and relevant confidence and
limitations. Use supported selection, summaries or views when they answer
@@ -0,0 +1,359 @@
# Android manifest components verification, 2026-10-24
## Scope and revisions
PR #5025 adds a static component inventory to `inspect_android_package`, using
the namespace-aware decoded manifest already retained by the JADX adapter.
Activities, aliases, services, receivers and providers retain declarations,
qualified class names where established, export declarations/defaults,
permission provenance, intent actions/categories, service types, authorities
and alias targets. These facts do not establish runtime registration, class
existence, merged-manifest acceptance or APK authenticity.
The maintenance comparison pins baseline
`e3802c16491b772118ccdfa195c6b51c3a337bdd` (contributor head
`c822b00c4755e584ee081eb3e7587735b630e249` integrated with main) and candidate
`fde65b1d47c00c189e003c54ad2a2c1fc93b443a`. Both contain the feature;
this comparison checks the maintenance changes rather than estimating the
cost of answering the same question without the feature. Later main
integration is outside these measurements.
## Corrections and automated coverage
Alias target matching indexes valid spellings once for preceding activities.
It now adds a bare abbreviation only for a class directly inside the manifest
package. `sub.Target` is already a fully qualified name and cannot match
`com.example.sub.Target`; `.sub.Target` and the complete qualified name can.
The contributor had already removed repeated package expansion in
`c822b00c`; that performance correction is not attributed to this follow-up.
The existing 900 KiB package / 511 alias regression remains covered.
Older Evidence without this inventory now receives a fresh unavailable
inventory per parse. Mutating one parsed default cannot affect another parse.
Before the corrections, the new identity/default cases had 6 passes and 3
failures (two false alias matches and one shared default). Afterward all 50
component cases passed. The broader selected component/JADX/SDK-summary set
passed all 83 cases, and `npm run check:fast` passed on the measured candidate.
After integrating main `21a20306b4887ff8e6b614b6ae34a387cfbdf1d8` in
`cdc2829131045d85692cf4bbf0acb12f36f65af7`, the same 83 cases passed again.
`check:fast`, `docs:check` and test discovery (2,333 files, each once) also passed.
The parser bounds declarations and retained name/permission/authority copies
before expansion. Truncation, unknown export rules, unresolved names and
unsupported permission overrides remain explicit. The existing Android
summary includes the structured inventory and retains the complete manifest
for paging; no second inventory parser or new view implementation was added.
## Real engine and public APK
Linux x86-64, Node 24.22.1 / npm 11.16.0, existing full OpenJDK 17.0.20.1,
JADX headless MCP 0.7.1, one analysis worker, 512 MiB JVM heap and
`ActiveProcessorCount=2`. The task process family was restricted to CPUs 0/1.
The APK was read as an inert artifact and was never installed or executed.
- [Appium ApiDemos 6.0.18](https://github.com/appium/android-apidemos/releases/tag/v6.0.18),
source revision `3e716ee04ef3390284bfab761e28da8c15c08143`, Apache-2.0:
`ApiDemos-debug.apk`, 6,521,217 bytes, SHA-256
`a9eecf37b26cd084855c530db81c2bb1b91f4c1b095a04f47aa7c20e2791f686`.
- [JADX headless MCP 0.7.1](https://github.com/1013503897/jadx-headless-mcp/releases/tag/v0.7.1),
source revision `5844800a486d2248fa949b74c9896285f5b54de2`, JAR SHA-256
`6e5eacf500b64292bfb73c49797c1958f6ee44646e43e868039ae7feb573ff75`.
The engine reported package `io.appium.android.apis`, target SDK 33,
2,876 classes and 869 resources. The complete 152,541-code-unit manifest
produced 355 components: 331 activities, 11 services, 7 receivers, 4 providers
and 2 aliases. Component coverage was complete, with no projection limitations.
APK signature verification remained explicitly `not_performed`.
An independent Python ElementTree projection checked every component field,
declaration order, namespace attribute, class-name qualification, permission
source, export/default, intent value, authority and preceding alias target
against the retained XML on all six measured CLI tasks. It also rehashed the
APK. This oracle checks REA's projection of JADX-decoded XML; it does not
independently validate binary XML decoding or Android runtime behavior.
Interpretation was checked against Android's
[activity](https://developer.android.com/guide/topics/manifest/activity-element),
[alias](https://developer.android.com/guide/topics/manifest/activity-alias-element),
[service](https://developer.android.com/guide/topics/manifest/service-element),
[receiver](https://developer.android.com/guide/topics/manifest/receiver-element)
and [provider](https://developer.android.com/guide/topics/manifest/provider-element)
documentation.
An actual stdio MCP client launched the same engine, validated the advertised
input/output schemas with Ajv2020, checked every structured summary fact,
reconstructed the exact manifest in three 70,000-code-unit pages, made a warm
complete call, and re-read the retained summary. All normalized facts and
provenance matched CLI. Raw engine snapshots and Evidence IDs were not equated:
they truthfully retain session-specific private APK paths, Java map key order,
load duration and the commands actually run. The warm complete request does
not repeat the earlier load command. This client explicitly allowed a 64 MiB
receive buffer; default client-buffer compatibility was not established.
## Completed-task costs
Three alternating before/after pairs ran
`rea inspect-android-package <same APK> --json`. Each task launched a fresh CLI
and JVM. OS/JAR caches were already warm; cold disk and first bridge compilation
were not isolated. Full normalized results, target identity and provenance
were equal; raw per-run engine records were retained without rewriting them.
| Cost | Before median (range) | After median (range) |
| ------------------------------------ | -------------------------- | -------------------------- |
| Estimated task tokens | 135,958 (same in all runs) | 135,958 (same in all runs) |
| Elapsed seconds | 16.538 (14.666–37.511) | 23.008 (17.756–24.777) |
| CPU seconds | 18.811 (18.034–19.461) | 18.191 (17.741–18.426) |
| Sampled peak process-family RSS, MiB | 1,074.9 (989.7–1,087.9) | 1,100.4 (1,074.1–1,123.6) |
The elapsed and CPU ranges overlap; these runs establish no speed improvement.
The candidate's median sampled memory was slightly higher. Tokens use tokenx
1.3.0 estimates, not model billing: all complete CLI stdout plus the invocation
(135,926 + 32 tokens). Discovery, prompts, client parsing and envelopes are
outside this known CLI task.
The actual MCP verification task took 132.448 seconds, 40.767 CPU seconds and
1,820.8 MiB sampled peak family RSS, including handshake, full discovery,
schema validation, summary, three manifest pages, complete call, retained
summary and shutdown. An earlier verification attempt used an incorrect
cross-session raw-Evidence equality assertion and failed after 36.131 seconds;
that diagnostic attempt is not counted as a successful workflow.
For the successful MCP task, compact JSON payloads estimate 47,583 tokens for
the summary versus 135,759 for the warm complete Evidence. These are individual
payload estimates, not a measured whole-agent saving; the 222-tool discovery,
envelopes, repeated text/structured representations and earlier attempt are
not included in that payload comparison. CLI and MCP transports have different
costs and are not interchangeable baselines.
CPU comes from waited child-process accounting. RSS samples descendants every
50 ms, may count shared pages more than once and may miss short-lived children;
it is not a kernel-enforced process-family maximum. No Android device,
split APK, signature-verification, native-library or runtime coverage is claimed.
Issue #4448's broader enabled/process, metadata, URI-grant and location requests
remain open.
## SDK omission and XML namespace follow-up
The subsequent review fixes are pinned in
`dfdba0fac79e07949b12299c85a1a2f3e9c2f115`.
Android's [uses-sdk contract](https://developer.android.com/guide/topics/manifest/uses-sdk-element)
defaults an omitted target to the minimum SDK and an omitted minimum to 1.
The component XML owner now combines the bridge's declaration values with
attribute-presence facts from the already parsed document. A present-empty
attribute or an unrecognized nonempty value stays unknown. Declaration-level
`target_sdk` and `min_sdk` remain unchanged. Root child nodes are reused, and
the effective numeric SDK/provider default is computed once; the redundant
per-provider parsing helper was removed.
One unnamespaced-element predicate now guards the manifest root, uses-sdk,
application, components, filters, actions and categories. Foreign lookalikes
cannot contribute component facts or satisfy an alias's preceding target.
Namespace URIs on Android attributes continue to govern attribute selection.
Eleven new negative cases fail on the published baseline, with three positive
SDK cases already passing. Three further present-empty SDK cases fail with a
simple string fallback and pass with the owned presence facts. Unknown-SDK
fixtures now supply an explicit unrecognized value rather than using omission
to mean unknown. All 100 selected component/JADX/protocol/SDK-summary cases
pass, as do `check:fast`, `format:check`, `docs:check` and discovery of all
2,333 files. Complete inventory coverage does not establish every SDK or
export state; nullable facts and limitations retain that uncertainty.
Another three alternating public-APK pairs compare published
`b50ee503fdff0912c12f1aaa76f043235d187a77` with the corrected revision above.
Every normalized fact/provenance field and all 355 component rows match the
independent oracle again. The exact-raw-record exclusions and fresh-JVM,
warm-OS-cache conditions described above apply.
| Cost | Before median (range) | After median (range) |
| ---------------------------------- | ----------------------- | ------------------------- |
| Estimated complete CLI task tokens | 135,958 (constant) | 135,958 (constant) |
| Elapsed seconds | 12.036 (11.133–12.487) | 11.024 (10.623–17.519) |
| CPU seconds | 18.436 (18.169–18.897) | 17.921 (17.252–19.856) |
| Sampled peak family RSS, MiB | 1,022.1 (981.3–1,049.8) | 1,043.8 (1,023.5–1,090.1) |
The ranges overlap and sampled memory median is higher; no speed or memory
improvement is established by this comparison. The actual stdio MCP workflow
also passes again, including all summary facts, the three manifest pages,
warm complete inspection and retained summary. That completed verification
took 26.991 seconds, 35.482 CPU seconds and 1,740.9 MiB sampled peak family RSS.
Its schema/client-buffer and comparison qualifications above remain in force.
These legacy-SDK edge cases are production-boundary/golden checks; the real
APK has an explicit SDK 33 target, so a legacy APK without uses-sdk was not
independently exercised through binary XML decoding or on a device.
## DEX names and hexadecimal SDK follow-up
Commit `27b7dc1656b66a71796d6f0d1119d79be7ab586a` corrects two additional
interpretation boundaries. Class identity expansion accepts the documented
[DEX SimpleName character ranges](https://source.android.com/docs/core/runtime/dex-format#simplename),
including leading digits, hyphens and supplemental Unicode, instead of Java
source identifier categories. The same predicate supplies activity and alias
reference matching. Package declaration validation remains separate. Spaces
are admitted by the character union introduced in DEX 040; this decoded
manifest projection does not establish a DEX version, class existence or
runtime loadability.
SDK interpretation now accepts decimal or hexadecimal positive signed-32-bit
resource integers once per inventory. [JADX's typed value decoder](https://github.com/skylot/jadx/blob/v1.5.3/jadx-core/src/main/java/jadx/core/xmlgen/entry/ValuesParser.java#L84)
prints `TYPE_INT_HEX` with a hexadecimal prefix. The unchanged Java bridge
retains those declaration strings; only the local numeric interpretation used
for default rules changes. Unrecognized previews, malformed hex, exponent
syntax, nonpositive values and signed-int overflow remain unknown. Omission
and present-empty handling from the prior correction remains intact.
Nine new controls fail before the correction: four legal DEX name/alias
combinations, three hexadecimal target levels, hexadecimal minimum fallback,
and decimal signed-int overflow. The former malformed-name fixture incorrectly
rejected a digit-led class; it now uses forbidden descriptor separators
instead. The component/MCP selection passes 77 checks. `check:fast`, complete
formatting and documentation pass. The first combined 117-case selection has
116 passes and one 30-second MCP timeout; the unchanged MCP file then passes
all three cases in isolation, with its complete retained-manifest workflow
finishing in 16.957 seconds. That workflow supplies a 1 MiB non-XML string;
the changed class and SDK interpreters are not reached for that input. No
deadline was raised. Further combined verification is recorded below.
Six alternating completed CLI tasks compare the immediately preceding head
`58b71c19b35204ebb37c94d9510025bcd18fc4ee` with this correction using the
unchanged ApiDemos APK. Every projected field for all 355 components matches
the independent decoded-XML oracle, and all normalized facts and provenance
agree. Per-session raw engine observations and their Evidence IDs are excluded
for the same documented reason as the previous comparison. Estimated full CLI
stdout plus invocation tokens remain **135,958** (tokenx 1.3.0).
Elapsed medians are 19.619 -> 17.893 s (ranges 12.708–23.907 /
13.658–30.088), waited CPU 30.279 -> 29.507 s (19.684–32.572 /
24.001–37.071), and sampled process-family RSS 1094.6 -> 1064.9 MiB
(985.0–1106.2 / 1038.3–1108.5). Every range overlaps; the environment varies
substantially and these runs establish no speed or memory improvement. CLI
and JVM processes are fresh, with warm OS/JAR caches; cold-disk and fresh
bridge-compilation costs remain unmeasured. The actual stdio SDK workflow also
passes summary, three manifest pages, warm complete output, retained summary
and advertised-schema validation again: 72.614 s elapsed, 78.638 s CPU and
1693.3 MiB sampled family RSS. Its full schema/catalog/transport token cost is
not measured. That workflow validates the existing APK's explicit SDK 33
values; no binary-XML hex fixture or numeric-led DEX class was executed or
qualified on a device. The new edge cases are boundary controls supported by
the format/decoder sources, not claims of real-device coverage.
The subsequent unchanged full selection passes **all 117 cases** in 67.186 s
elapsed, 52.269 s waited CPU and 2028.3 MiB sampled family RSS. This rerun
does not erase the earlier timeout recorded above.
## Android package identity correction
The final pre-merge review found that the package qualifier still used Java
identifier categories. Five new negative controls fail before correction:
`com._hidden`, `com.$hidden`, `com.Δelta`, an ordinary single-segment package,
and a name longer than Android's 223-character parser limit. Existing negatives
also cover digit-led and empty segments. Package qualification now has its own
Android application-ID grammar: ASCII letter-led segments, ASCII letters/digits/
underscores, at least one separator, and the platform `android` exception.
Unsupported recorded package/name strings remain source evidence; relative
component identities and alias references remain unresolved with partial coverage.
DEX class-name rules stay separate and unchanged. This does not claim that a
syntactically supported package or component is installable or loadable.
Authorities are the [Android application-ID documentation](https://developer.android.com/build/configure-app-module#set-application-id),
and Android 16's pinned
[package parser](https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-16.0.0_r1/core/java/android/content/pm/parsing/ApkLiteParseUtils.java#916)
and [name validator](https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-16.0.0_r1/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java#58).
The documentation requires nonempty segments; the pinned parser is permissive
about empty segments, which this projection leaves unsupported rather than
claiming package identity from them. No Package Manager/device execution was
performed for these cases.
Four existing amplification/copy-budget controls used invalid huge or Unicode packages.
Three now use a valid package and huge ASCII/multibyte component names, preserving
aggregate exhaustion, first-row admission and UTF-8 expansion coverage at the
correct producing boundary. The fourth preserves 511 unresolved aliases under a huge unsupported package without expanding it. The obsolete Java package predicate is removed;
package interpretation is shared by component and alias qualification.
Earlier real-ApiDemos measurements remain pinned to their stated source commits;
they are not measurements of this package correction. Response schemas and
valid ApiDemos facts are unchanged by source reasoning; no fresh speedup or
whole-agent cost claim is made for this correctness change.
Final executed source pin `7b86379e63448391973098572b884b692fbf5d8f`
incorporates main `98ec00f4068bab894639d0d1d83ac0bfc428bcbf`. All **129**
selected component/JADX/protocol/MCP checks pass: 47.290 s elapsed,
41.923 CPU s, 1,486.0 MiB sampled peak process-family RSS. The retained-manifest
MCP case passes with its existing 30-second deadline. Type/lint, full formatting
and generated-document checks also pass. The preceding attempt had 127 passes,
one obsolete huge-package identity expectation and one 30-second MCP timeout;
these attempted costs are not excluded from the narrative. The fixture expectation
is corrected as described above. No deterministic product failure was established
from the timeout and no deadline was increased. This completed selection uses
primed OS/file caches and a fresh runner; it is a validation cost, not a
before/after performance comparison. Family-memory sampling limitations above
remain applicable.
## Exact DEX class spelling follow-up
A later review identified another identity/display conflation: DEX format 040
permits U+0020 in SimpleName, including its boundaries, but component and alias
handling still trimmed it. Five source-boundary regressions fail before the
correction (87 other cases pass); all 92 component cases pass after it. They
cover trailing space, leading space, spaces after a relative dot, a space-only
segment, and a fully qualified spelling with boundary spaces. A separate control
keeps `.Hidden` distinct from `.Hidden ` in alias matching.
The XML owner now validates and resolves the original component spelling and
compares exact alias references. The redundant raw/trimmed representations and
the downstream trim-based invalidity checks are removed. The existing malformed
boundary control now uses XML character-reference tabs, which DEX disallows,
rather than incorrectly treating U+0020 as invalid. Empty names and the descriptor
separator controls remain covered. Package grammar retains its separate owner.
The domain schema and retained summary consume these strings without another
normalization step.
The source authorities are [DEX SimpleName](https://source.android.com/docs/core/runtime/dex-format#simplename)
and Android 16's [buildClassName](https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-16.0.0_r1/core/java/com/android/internal/pm/pkg/parsing/ParsingUtils.java),
which applies the package to the exact string. These are decoded-XML and
source-qualified cases. No authentic space-named DEX APK or device load was
executed; the decoded manifest alone does not establish DEX version, class
existence or loadability. Earlier real-ApiDemos measurements keep their exact
source pins and are not re-labelled as measurements of this correction. The
public response schema and ordinary valid-name output are unchanged by source
reasoning; no new token or speed improvement is claimed.
Executed space-preservation source pin `d1f15f68f65bc546590000eee86c91b1e061bf55`
incorporates merged IL2CPP/main `3ce311757dd8ac444c8297b2871464536f93816d`.
All **135** component/JADX/protocol/MCP checks pass without changing deadlines:
47.168 s elapsed, 43.081 CPU s and 1,550.0 MiB sampled peak family RSS.
Type/lint, full formatting and generated-document checks pass. Earlier combined
integration had 672 passes and one Android 30-second MCP timeout; the unchanged
three-case MCP file passed separately at the original deadline. These outcomes
remain distinct. This repeat is a fresh runner with primed OS/file caches;
validation costs are not evidence of a general performance improvement.
## Final declared-value review
Source pin `d87801811cc80dd683f482a76f9cb9ad1ab9b670` removes the last `trimmed` helper. Service
`foreground_service_type` now preserves the declared Android attribute exactly:
absence is null, while explicit empty, space-only, padded, token-list and
resource-reference spellings remain distinct. This field records a declaration;
it does not validate service-type tokens or establish runtime eligibility.
The component guide now distinguishes valid DEX boundary spaces from invalid
controls and from the separate manifest-package grammar.
Six source-boundary cases were added. Against the previous implementation,
95 checks pass and 3 fail (empty, space-only and padded declarations). After the
owner fix, all **141** selected component/JADX/protocol/MCP checks pass at the
original deadlines. The newly malformed foreground-type values are verified at
the parser boundary; the existing SDK retention workflows do not constitute a
real Android runtime verification of those values.
The selected suite took 51.269 s elapsed,
41.298 CPU s and
1632.789 MiB sampled peak family RSS.
`check:fast`, full `format:check` and `docs:check` pass. Earlier real ApiDemos
measurements retain their stated source pins; no new performance improvement
or real DEX 040 space-named class verification is claimed.
At the superseded `d799bafa5f2f7c563da04955b540345f7bae70e4` CI run,
the source worker exited while starting a JavaScript reference-lifetimes test;
there was no failed Android assertion or complete source-suite result. That
JavaScript source tree is byte-identical to the integrated main source. This is
an observed worker failure, not a reproduced main failure or a passing check.
The final head requires its own CI assessment.
+261
View File
@@ -0,0 +1,261 @@
# GameCube DOL verification, 2026-10-24
This records additional verification of [PR #6240](https://github.com/morluto/rea/pull/6240).
The lane explicitly imports a static 32-bit big-endian PowerPC DOL through the
existing POSIX Ghidra provider. It does not execute the target. Admission covers
cached MEM1 (`0x80000000..0x81800000`); Wii MEM2, uncached aliases, REL/RPX,
r2/r13 recovery and platform-specific instructions are outside this verification.
No automatic DOL detection or new provider is introduced.
## Sources and corrections
The measured feature-source pin is `7f892827610d8e0f14993c577ac48ba8ac1f7ea2`.
The comparison baseline is `2961f06776dcb73818980a2a5d6886a4fec1e521`.
Both include main `3ce311757dd8ac444c8297b2871464536f93816d`, the contributor's
DOL work and the shared inventory/caller/cleanup corrections below. Their
production difference is the BSS ownership and qualified-address correction;
this is not a comparison against main without DOL support. Contributor head
`29b4608459a2a2c4d838135a569d3e5e7727a30e` is retained in the final ancestry.
- A real Ghidra capture showed `symbol` provenance in both procedure inventories.
The shared strict decoder rejected that existing bridge field, including on
the inspected main implementation. The decoder now accepts the named optional
symbol contract for both inventories; two captured-row controls fail before
and pass after. The shared boundary selection passes all 23 checks.
- The verifier must request `analyze_function` / CLI function `detail: complete`
after the earlier summary-default change. It now uses the shared cleanup owner
for connection failures, mixed cleanup outcomes and final lineage checks.
The MCP scenario also uses the existing application harness. The Go fixture
narrows its ELF subject instead of asserting an incompatible union type.
- The header parser owns BSS gap derivation. Original data-slot order remains
intact; a sorted private range list derives at most 12 gaps. The private v2
plan declares section/gap counts, including zero gaps. Java applies these
exact ranges instead of recomputing them; attestation uses the same parsed
facts. The 1,272-byte wire bound follows the 18-slot/12-gap uint32 format.
The profile version changes to invalidate older interpretation identities.
- Attestation reuses the domain address parser for qualified coordinates.
Foreign-space uninitialized observations remain observable without becoming
MEM1 BSS; foreign-space file mappings produce a mismatch. Both added controls
fail against the previous implementation instead of throwing on valid syntax.
The suspected entry-label problem was not reproduced: real Ghidra reported
`__start` at the header entry, with `USER_DEFINED` symbol provenance from the
pre-script. That is an imported analysis label, not an original DOL symbol.
No entry naming change was made.
## Tools and artifact
Executed on Linux x64 with Node 24.22.1, npm 11.16.0, two allowed CPUs, a 2 GiB
Node heap and a 1 GiB Ghidra JVM heap. Verification-only tools were extracted
under a temporary directory; no unrelated software or user configuration was
installed or changed.
| Tool archive | Source | SHA-256 |
| --------------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------ |
| Ghidra 12.1.4 | [NSA release](https://github.com/NationalSecurityAgency/ghidra/releases/tag/Ghidra_12.1.4_build) | `ddac49f903da9d5bac833e5cc79395098b9c33cfd3279be5f31bd00387d2d4db` |
| Temurin JDK 21.0.13+9 | [Adoptium release](https://github.com/adoptium/temurin21-binaries/releases/tag/jdk-21.0.13%2B9) | `d7f5d5cf865a2902b70ff549b97586a7d3f234e016f041ab447ea740c7d66d1c` |
| lld-14 1:14.0.6-12 | Debian bookworm package, downloaded and extracted | `3b77ab0cc30bbba9e874ba57cabc090116ed52d5eebaa043cf1e1412c4523a99` |
The host compiler was Clang 14.0.6. Its existing user `ld.lld` wrapper delegated
to an x86-only linker, so the verifier selected the extracted real LLD by an
explicit compiler wrapper; the user wrapper was left intact.
The source-owned fixture is compiled from the shipped C, assembly and linker
script, then converted to DOL. Independent GNU readelf symbols/program headers
and a separate DOL header reader establish section addresses, file bytes, entry,
data and the uncovered BSS interval `[0x80013008,0x80013030)` (40 bytes).
Data slot 1 overlaps the declared BSS and remains initialized.
- DOL SHA-256: `42490c9b86ad0b5034063f65a3feeec554f583cb21ed95f4248f2a7571120f3d`.
- Persistent query ELF SHA-256: `1ae89c6a4b0e3b9c8d3415e4b4a7e3f83d3a310c3b40aa60abd333dd35d4f963`.
- Both fresh verifier builds produce that same DOL digest. Their debug-bearing
source ELFs differ by worktree, and are not claimed byte-identical.
## Real-provider verification
`node scripts/verify-real-ghidra-dol.mjs` exits 0 at both source pins using stock
`PowerPC:BE:32:default`. It exercises public CLI and MCP open/session, load-image
attestation, entry and leaf inventory, the direct call, complete decompilation,
global/tail/marker reads, uninitialized BSS reads, close and cleanup. Both report
verified lineage with zero live owned descendants at the final observation.
This does not establish that no children existed earlier.
The Gekko/Broadway language is absent from the installed catalog, so its optional
pass is skipped. Windows, this session's macOS runtime, commercial GameCube/Wii
images, other DOL layouts and console runtime behavior are unverified. The
contributor's earlier macOS/commercial-image evidence remains their evidence;
it was not rerun here.
Earlier unsuccessful attempts are retained as failures: the host linker wrapper
failed before import; then the real inventory exposed the `symbol` decoder
mismatch; after that fix, the old verifier interpreted summary delivery as a
complete dossier. None counts as a completed performance baseline. The two
successful stock CLI/MCP runs took 135.025 s / 106.206 CPU s / 1,387.105 MiB
sampled peak family RSS before, and 65.520 s / 75.296 CPU s / 1,451.117 MiB after.
These ordered runs had different cache history; they do not establish a speedup.
## Completed query-task costs
The analyst task asks for artifact identity, provider/language binding, verified
section/BSS mapping, entry and leaf, the direct call, complete decompilation and
four byte reads. Each fresh actual stdio SDK session discovers the full catalog,
performs 11 tool calls (8 distinct operations), validates used advertised input
and output JSON Schemas with Ajv 2020, validates each structured reply, closes
and checks lineage. All six tasks pass. All requested answer facts, including
mapping observations, pseudocode, byte results and limitations, are identical;
input DOL/ELF digests remain unchanged. The v2 analysis-profile identity changes,
so complete Evidence objects and Evidence IDs are not claimed identical.
Three pairs ran in order before/after, after/before, before/after. Each task
includes SDK/server/JVM startup, independent readelf/header checks, lazy schema
compilation, result validation, trace writing and cleanup. OS/file caches were
primed by validation runs. Compilation-cache history was not isolated; these
are fresh-process runs with warm files, not controlled cold-cache runs.
Tool acquisition, initial builds, token-estimator CPU, transport framing,
stderr, prompts and actual model-specific tool conversion are excluded.
| Task | Elapsed s | CPU s | Sampled peak family RSS MiB | Estimated whole-task tokens |
| -------- | --------: | -----: | --------------------------: | --------------------------: |
| 1-before | 44.031 | 55.081 | 2069.961 | 1,136,010 |
| 1-after | 53.769 | 53.543 | 1810.133 | 1,135,994 |
| 2-before | 99.072 | 95.836 | 1979.152 | 1,136,010 |
| 2-after | 62.052 | 72.321 | 1921.480 | 1,135,994 |
| 3-before | 100.541 | 97.175 | 1675.352 | 1,136,010 |
| 3-after | 65.932 | 60.857 | 1758.359 | 1,135,994 |
These are tokenx 1.3.0 estimates over all 27 reserialized JSON-RPC messages per
task, including both text and structured content. They are not billed tokens
or a measurement of model context. The catalog alone contributes 1,031,590
estimated tokens in the full RPC envelope (223 tools). Its separate
name/description/inputSchema subset is 261,284 bytes / 72,459 estimated tokens;
this is not an observed agent tool conversion. Tool-result estimates are
103,162 before and 103,146 after; requests and other protocol add 1,258.
A 16-token difference in the whole task does not establish useful context
savings. Complete retained Evidence remains accessible.
Observed before medians: 99.072 s elapsed, 95.836 CPU s, 1979.152 MiB sampled peak family RSS.
Observed after medians: 62.052 s elapsed, 60.857 CPU s, 1810.133 MiB sampled peak family RSS.
The ranges overlap and startup dominates; three pairs with unisolated caches
on a shared host do not attribute these timing differences to the BSS change
or establish a general performance gain. Controlled cold-cache costs and large
commercial-image costs remain unmeasured. The structural improvement is one
bounded interpretation of at most 18 sections and 12 gaps, without repeated
range subtraction or whole-file parsing at the header boundary.
The SDK receive budget was explicitly 64 MiB. The sampler observes recursive
process-family RSS every 50 ms; it can miss short-lived peaks and double-count
shared pages. CPU is wait-accounted process-family time. Neither metric is a
claim about exclusive physical memory or production-agent costs.
## Source and workflow checks
- Final source: `check:fast`, full `format:check`, cached runtime/skill build and
**326 passing checks / 3 skipped Windows runtime checks** across 22 exact files.
Coverage includes DOL CLI/MCP schemas, target admission, load-image tampering,
header malformation/slot ordering, BSS gaps, launcher arguments, provider
decline, raw/DOS/PowerPC compatibility, inventory decoding and cleanup owners.
- An earlier selection had 325 pass / 1 MCP 30-second timeout / 3 skip. The
unchanged MCP case passed alone at the original deadline; the final complete
22-file selection then passes all 326 without increasing that deadline.
- Documentation and full formatting are checked again after this report.
Simulated-provider scenarios do not add real Windows, Hopper or IDA coverage.
While preparing the final PR, main independently failed a release-test typecheck
([main job](https://github.com/morluto/rea/actions/runs/40079461941/job/120829870149))
and JavaScript worker startup
([main job](https://github.com/morluto/rea/actions/runs/40079461941/job/120829870212)).
These are recorded baseline failures, not successful checks or automatic
exemptions for unrelated future failures. The published head's CI must be
assessed separately before any authorized bypass.
## Final integration after main's ownership update
Source `a853c0c0ee5ea5eb66924c2c0235b223ae9d87b0` integrates main
`d9660eb03271d0ce1859cf8720ca460701dd3322`. The only conflict was the packaged
Windows verifier: main's new command/workspace owner is retained exactly, with
one additional `ReaGhidraPrepareDol.java` caller-directory collision fixture.
No previous resource-owner implementation is restored.
The combined snapshot `e267d92d7af33681620548d584e3d60622ec7abd`
(tree `5dd561143b516ae4ae5983fe8250608329f4cee6`) includes that source and
Android head `e65ba83fa319dd34eaebce7d93d78d4e67cf8458`, in addition to the
merged IPS and IL2CPP work. **1,062 checks pass, 4 skip, none fail** across the
four features and the newly changed process/package owners. Skips cover three
Windows launcher scenarios and the platform-specific adjacent-npm scenario.
The cached build passes. `check:fast` fails only at main's already recorded
`releaseProposal.test.ts:378` `includeFiles` type error; it is not reported as
passing. Standalone lint, full formatting and documentation are checked again
on the integrated DOL source after this report.
The same combined snapshot also reruns the real stock Ghidra CLI/MCP DOL lane
successfully after the process-owner changes: 111.490 s elapsed,
98.176 CPU s,
1448.965 MiB sampled peak family RSS.
Section/BSS mapping, entry/callee, decompilation, bytes, unchanged DOL identity
and final verified empty lineage pass. Gekko remains unavailable and skipped.
The six paired query costs above retain their original source pins; the new
ownership implementation was not rebenchmarked as a six-task series.
Android subsequently merges as `f2fa7f648de3dfbac5de69f3c091a54ffd571015` on
that same main parent. Its CI built report has 14,251 passing / 220 skipped
tests; the source worker and release-test failures match main. That is context
for the combined snapshot, not the CI outcome of the newly published DOL head.
## Procedure-search contract verification
Source `95e4cdfa53b049855f0d2b68b0741dada41d3e68` replaces the remaining inline public
`search_procedures` row definition with `nativeInventoryRowsSchemas.names`, the
same owner used by complete native name/procedure inventories and retained
views. The bridge decoder already preserved `symbol`; the old public schema
rejected it. The previous all-five-tools test stripped symbol facts from its
search fixture and therefore missed that consumer boundary.
The test now replays both real captured rows (generated `FUN_80003100` and the
pre-script's `__start`) with their source metadata. Before the fix, the standard
SDK raises `Structured content does not match the tool's output schema` for
the extra fields. After it, all **42** selected decoder, inventory-view, MCP,
semantic-schema and DOL-session checks pass, covering complete, summary and
retained-page delivery. The superseded inline row definition is removed;
public search and list consumers share the domain row owner. Standalone lint,
full formatting and cached build pass. Source checking reports only the same
main release-test `includeFiles` error.
A fresh real stock-Ghidra SDK task adds a matching `search_procedures` call to
the earlier analyst question. The result keeps both rows' symbol metadata,
passes the actual advertised SDK/Ajv schemas, and agrees with the list result.
All earlier requested mapping, byte, pseudocode, limitation and input-identity
facts remain equal. Final process lineage is verified with no live descendants.
This adds real search coverage; the earlier six paired tasks did not exercise
public procedure search.
The first fresh task stops during Java readiness cleanup, before DOL import:
Java exits 0 and its JDK 21 facts parse, but an observed process's ownership
environment returns EACCES. Both recorded candidate PIDs are subsequently
absent before a fresh session is attempted. The fresh session completes.
Ownership checks remain enforced; no persistent cause was isolated, and the
first attempt remains a failure. The row-schema correction is not claimed to
fix that process-retirement observation.
The completed analyst task includes that failed attempt and retry:
| Attempt | Outcome | Calls | Elapsed s | Accounted CPU s | Sampled peak family RSS MiB | Estimated RPC tokens |
| ------------------ | ----------------------------- | ----: | --------: | --------------: | --------------------------: | -------------------: |
| search-final | Java readiness cleanup failed | 1 | 31.447 | 26.352 | 1352.637 | 1,053,096 |
| search-final-retry | passed | 12 | 77.911 | 91.108 | 2039.742 | 1,139,433 |
Measured invocations total **109.358 s**,
**117.460 CPU s**, **2,192,529 estimated
RPC tokens**, 13 calls and 36 messages. The largest sampled
family RSS is 2039.742 MiB. Both catalog discoveries and
the error reply are counted. The receipt-write span approximates
180.355 s including intervening diagnostics;
diagnostic/model tokens and CPU, prompts, stderr, transport framing and actual
model-specific tool conversion remain unmeasured. Each catalog has 223 tools;
its input subset remains 72,459 estimated tokens / 261,284 bytes per attempt.
The successful attempt makes 12 calls; it is a different task from the earlier
11-call pairs and does not establish a performance gain. Tokenx 1.3.0 estimates
and the sampler limitations above still apply.
Full formatting and documentation are checked again after this addendum.
@@ -0,0 +1,34 @@
# PE TLS/load-config verification - 2026-10-24
All runs used macOS arm64 and Node 26.5.0. No target was executed.
## Cross-compiled lane
`npm run verify:pe:load-config` with Zig 0.16.0 compiled freestanding C samples
for `x86_64-windows-gnu` and `x86-windows-gnu`, then invoked the public
`inspect-pe-load-config --json` CLI.
| Target | Callback VA | RVA | File offset | Section |
| ------ | ------------- | -------- | ----------- | ------- |
| PE32+ | `0x140001000` | `0x1000` | `0x400` | `.text` |
| PE32 | `0x401000` | `0x1000` | `0x400` | `.text` |
Both images declare one callback and no load-config directory.
## Linker-produced and vendor images (not committed)
- A MinGW-linked PE32+ sample with two `.CRT$XL*` callbacks (`zig cc -target
x86_64-windows-gnu`): four callbacks reported in `.text` at `0x140001000`,
`0x140001020`, `0x140001470` and `0x140001500`, matching the callback array
at `AddressOfCallBacks` `0x14002B8C8` shown by `llvm-readobj
--coff-tls-directory`.
- Sysinternals `strings64.exe` (PE32+, MSVC): load-config fields match
`llvm-readobj --coff-load-config`.
- Sysinternals `strings.exe` (PE32, MSVC): the data directory declares 64 bytes
while the structure `Size` is 188 (`0xBC`). REA reports `size: 188`,
security cookie `0x4560dc` in `.data`, SafeSEH table `0x453b28` with 7
handlers and guard flags `0x100`, matching `llvm-readobj`.
Sized fields beyond these samples, EH-continuation and XFG declarations, and
malformed ranges are covered by synthetic fixtures. Runtime enforcement of any
mitigation is not verified.
+67 -28
View File
@@ -660,17 +660,21 @@ repeated reads of one full request line whose list membership changes across a
write, including transitions to or from an empty list, or whose entity fields
are added or removed. A type whose reads still disagree keeps every tool a
recorded view with reason `fields-differ`. Different query strings remain distinct request lines. Sparse responses do not erase the last observation of a field supplied by another request line. Entities are ordered
by type, collection and first sighting; that order is a list's state order.
by type and collection, and within a collection as the first line that lists
every entity any initial read shows in it and names no sort, direction or
random parameter (`random`, `rand`, `shuffle`, `seed`) lists them; a collection
without such a line keeps its entities' earliest sightings. That order is a
list's state order.
Every entity cites its `record_ordinals`.
`views` lists one plan per read tool in tool order:
| Shape | Plan |
| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `item` | The entity object at `pointer` (the root, one member, or `/0` or `/<member>/0`), the ordered `fields` the view shows and the `required_fields` every recording had, and `envelope_ordinal`: the recording whose response, with the entity replaced, every item response shares |
| `list` | The array at `pointer`, its `fields`, `membership` (`all`: every entity of the collection in state order, when each recording listed exactly that, had no query or only paging parameters (`limit`, `offset`, `page`, `per_page`, `perpage`, `page_size`, `pagesize`, `size`, `start`, `_start`, `_end`), no item-total member (`total`, `count`, `totalCount`, `item_count`, `totalcount`, `itemcount`, `numberOfElements`, but not `total_pages` or `per_page`; a number or a string of digits) differing from the number listed, and no `has_more`, `next` or next-page token member set; `recorded`: the entities each recording listed, except on the lines `all_record_ordinals` names, which each meet those conditions on their own and list every entity), and `counters` that equal the number listed |
| `document` | The latest initial value of a list tool whose responses hold no identified list, when its initial reads share one request line; held in `documents` with that `path`, whether or not the capture writes it, unless its value changed across any recorded write between those reads (`fields-differ`) |
| `recorded` | A `reason` the tool replays its recordings instead |
| Shape | Plan |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `item` | The entity object at `pointer` (the root, one member, or `/0` or `/<member>/0`), the ordered `fields` the view shows and the `required_fields` every recording had, and `envelope_ordinal`: the recording whose response, with the entity replaced, every item response shares, leaving out the `per_response_members` each response there mints: a top-level UUID or clock that differed on every repeat of every concrete path the view answers, with no write between, and never stayed the same on a repeat elsewhere; list views name theirs too |
| `list` | The array at `pointer`, its `fields`, `membership` (`all`: every entity of the collection in state order, when each recording listed exactly that, had no query or only paging parameters (`limit`, `offset`, `page`, `per_page`, `perpage`, `page_size`, `pagesize`, `size`, `start`, `_start`, `_end`), no item-total member (`total`, `count`, `totalCount`, `item_count`, `totalcount`, `itemcount`, `numberOfElements`, but not `total_pages` or `per_page`; a number or a string of digits; top-level, or in a top-level object beside the list such as `pageInfo.totalRows`) differing from the number listed, and no `has_more`, `next` or next-page token member set; `recorded`: the entities each recording listed, except on the lines `all_record_ordinals` names, which each meet those conditions on their own and list every entity), and `counters` that equal the number listed |
| `document` | The latest initial value of a list tool whose responses hold no identified list, when its initial reads share one request line; held in `documents` with that `path`, whether or not the capture writes it, unless its value changed across any recorded write between those reads (`fields-differ`); its `per_response_members` are the members of the value each response on that path mints |
| `recorded` | A `reason` the tool replays its recordings instead |
An item or list plan is kept only when it reproduces the latest initial
recording of each request line from the state, comparing JSON values without
@@ -700,29 +704,34 @@ collection, what each query parameter does, and names the result in `query`.
Each such line is explained by the first combination of roles that lists
exactly what it recorded from the collection's initial entities:
| Role | Parameter | Effect |
| ----------- | ------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `filter` | any, on a `field` every listed item holds the value in | entities whose field equals the value as text, or whose array field holds it directly or as an object's `id`, `slug` or `name` |
| `search` | `q`, `query`, `search`, `searchTerm`, `term`, `keyword`(s) | entities with one of `fields` containing the value, ignoring case; the fields are those it matched in the listed items, URLs only where nothing else matched |
| `sort` | `order`, `orderBy`, `ordering`, `sort`, `sortBy` (and `_by` spellings) | ordered by the field the value names (`name`, `-name`, `name:desc`, `name-ASC`): numbers by value, text ignoring case, null last ascending, ties in state order |
| `direction` | `dir`, `direction`, `order`, `orderDirection`, `sortDir`, `sortOrder` with `asc` or `desc` | the sort's direction |
| `limit` | `limit`, `per_page`, `perPage`, `page_size`, `size`, `count`, `take`, `itemsPerPage` | page size; zero or a negative value lists every match |
| `offset` | `offset`, `start`, `_start`, `skip` | matches skipped before the page |
| `page` | `page` | page number, counted from `first` (1 or 0, whichever explains the line) |
| `inert` | any | changes nothing for its recorded `values`; other values are not computed |
| Role | Parameter | Effect |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `filter` | any other, on a `field` every listed item holds the value in; one not named like the field (`shelf`, or `shelf_id`, for `shelf`) stands only when lines giving it two different values agree on it | entities whose field equals the value as text, or whose array field holds it directly or as an object's `id`, `slug` or `name` |
| `search` | `q`, `query`, `search`, `searchTerm`, `term`, `keyword`(s) | entities with one of `fields` containing the value, ignoring case; the fields are those it matched in the listed items, URLs only where nothing else matched; a term that matched no field leaves the parameter inert until another line's term does |
| `sort` | a name asking for an order in any common spelling: `sort`, `_sort`, `sort[]`, `sort[0][field]`, `sorters[...]`, `sortBy`, `sort-column`, `sortKey`, `order`, `orderBy`, `ordering` | ordered by the field the value names (`name`, `-name`, `name:desc`, `name-ASC`): numbers by value, text ignoring case, null last ascending, ties in state order |
| `direction` | a direction name (`dir`, `direction`, `sortDir`, `sortOrder`, `sortDirection`, `order_direction`, json-server's `_order`, and `order`) given `asc`, `desc`, `ascending` or `descending`; given anything else, `order` is a `sort` and the others are ordinary parameters | the sort's direction |
| `limit` | `limit`, `per_page`, `perPage`, `page_size`, `size`, `count`, `take`, `itemsPerPage` | page size; zero or a negative value lists every match |
| `offset` | `offset`, `start`, `_start`, `skip` | matches skipped before the page |
| `page` | `page` | page number, counted from `first` (1 or 0, whichever explains the line) |
| `inert` | any that names no order or direction; never a true `reverse`, or a paging value that is not a safe integer | changes nothing for its recorded `values`; other values are not computed |
Lines that disagree on a parameter's role drop it, inert values gather and
search fields unite. `query.record_ordinals` are the lines the merged roles
No two parameters of one call take the same `sort`, `direction` or paging role,
and a line with more than 12 query parameters is left unexplained. Lines that
disagree on a parameter's role drop it, inert values gather and search fields
unite. `query.record_ordinals` are the lines the merged roles
reproduce, envelope and fields included, and `query.totals` the envelope
members that count every match before paging on each of them (a
`number`-of-items member counts the page). The roles are inferences from the
members that count every match before paging on each of them, top-level or one
level down in a paging object (`/pageInfo/totalRows`); a `number`-of-items
member counts the page. The roles are inferences from the
recorded lines; a line they do not reproduce keeps its recorded members.
`writes` lists one plan per tool whose request may change state, in tool
order. Its `effect` is `create` for a `POST` to a type's
`collection_template`, `update` for a `PUT`, `PATCH` or `POST` to that template
plus one trailing parameter, `delete` for a `DELETE` to it, `document` for a
`PUT`, `PATCH` or `POST` to a document's path, and otherwise `none` with reason
plus one trailing parameter, or to that item path plus one action segment,
`delete` for a `DELETE` to it, `document` for a `PUT`, `PATCH` or `POST` to a
document's path, an `update` or `delete` of each entity a write at the
collection names by id in its body, and otherwise `none` with reason
`no-state-target`. A plan is built from the tool's first successful call: its
request body must be a usable JSON object (as response bodies are usable), and
the written entity must show in that call's response or in a later read before
@@ -731,14 +740,22 @@ explains it: generated `identity` (a sequence, UUID or token, by the recorded
value's shape), `echo` of a body member, `copy` of a body string of 8 or more
characters, a `path` value, a `slug` of a body member, a `clock` value within 10
minutes of the request in its recorded format, a `sequence` one past the
largest value of its collection or type, a `unique` UUID, or a `template` around
a generated value; other fields keep the observed value. An update's members
largest value of its collection or type, a `unique` UUID or opaque token, a
`nested-clock` object whose clocks the request stamped, or a `template` around
a generated value; other fields keep the observed value. A body that wraps one
object in a one-element array is that object, wrapped so on every call
(`body.envelope`). An update's members
echo when stored as sent; a member stored differently is accepted only with its
recorded value (`body.fixed`) and stores the value the call that showed it
recorded (the `value` of its `constant` rule), and a field it changed without
sending must be a clock, an increment or a slug, or the plan is
`unexplained-change`; an increment or slug must hold for every call of the
tool. Every member the first call sent is required, except by `PATCH` and
sending must be a clock, a nested clock, an increment, a slug, a fresh `unique`
value, a `stamp` of the acting user, a value `derived` from the one member it
stores nowhere, or for an item action a `set` of a scalar to the value it
showed, or the plan is `unexplained-change`; an increment, slug, stamp, derived
value or set must hold for every call of the tool, and an action is served
only once a call found what it sets already set. A write is served only
with the query its first call sent, which serving applies to a call that
leaves a parameter out and the catalog advertises as required. Every member the first call sent is required, except by `PATCH` and
`POST` updates.
A read on a line `query` answers is checked against the query evaluated over
@@ -785,6 +802,28 @@ derives it from the capture at startup, serves read tools and the served write
plans from it and loads replacement states through the AgentEnv data plane, as
[serve from the state](agent-environments.md#serve-from-the-state) describes.
## Environment coverage
`view: {"kind":"environment-coverage"}` with the same `template_rules` reports
what the state an environment derives from the parent would serve, without the
state's entities: `reads.state` counts read tools answered from the state,
`reads.recorded` those that replay their recordings with
`reads.recorded_reasons` per view reason, `writes.served` lists the write tools
whose effect later reads confirmed, and `writes.withheld` lists every other
write tool with its reason, the record its plan was built from
(`exemplar_ordinal`), the read that disagreed (`contradicted_by`) and a `hint`
about what to record next. Hints are advice; they never change what is served.
The view projects the environment-state view and summarizes it, so it refuses
every parent that view refuses, with the same errors, and it is always
inferred. Call it after each recording to see which writes an environment
built from it would serve. It does not apply the serve-time admission checks:
an environment whose served catalog exceeds the discovery budget, or whose
state exceeds what `data/add` loads, serves replay, and `rea environment-build`
then reports `coverage: null`; otherwise the build prints this summary as its
`coverage`. See
[Build a bundle in one step](agent-environments.md#build-a-bundle-in-one-step)
for the hint per reason.
## Resource and lifecycle boundaries
Complete evidence has a 32 MiB input, 96 MiB decoder reply and 64-level nesting
+7
View File
@@ -41,6 +41,10 @@ cannot be established from that URL alone; choose the context explicitly when
known. `import_map` is optional and reads local JSON only. Its exact bytes and
base URL are retained; selecting it does not prove the map was installed in the
captured page. HTTP(S) importer contexts without URL userinfo are supported initially.
An unusable reported URL leaves the importer context unavailable; supply
`importer_url` explicitly when its value is known. An explicitly supplied malformed
URL is reported against that input field. URLs using unsupported schemes remain
capability limitations.
## Interpret the evidence
@@ -65,6 +69,9 @@ The selected source is loaded from `files/` under the manifest's current
directory. Relocation retains the manifest's originally reported paths and the
actual current source path separately. Unsafe paths, symlink source directories,
nonregular files, wrong digests/sizes and malformed UTF-8 fail explicitly.
Malformed source bytes or unsafe source paths declared by the manifest are
artifact failures. Malformed caller-selected manifest or import-map files identify
the supplied input field.
## Engine and lifecycle
+6
View File
@@ -66,6 +66,7 @@
"bridge/ghidra/ReaGhidraNoReturnFix.java",
"bridge/ghidra/ReaGhidraMipsSoftFloat.java",
"bridge/ghidra/ReaGhidraPrepareRaw.java",
"bridge/ghidra/ReaGhidraPrepareDol.java",
"bridge/android/ReaJadxBridge.java",
"bridge/mitmproxy/capture.py",
"scripts/rea.mjs",
@@ -187,15 +188,18 @@
"verify:keyed-archive": "npm run build:cached && node scripts/verify/apple/keyed-archive.mjs",
"verify:macos-bundle": "npm run build:cached && node scripts/verify/apple/macos-bundle.mjs",
"verify:legacy-simulator": "npm run build:cached && node scripts/verify/apple/legacy-simulator.mjs",
"verify:node-sea": "npm run build:cached && node scripts/verify-node-sea.mjs",
"verify:architecture-guards": "node scripts/verify/architecture-guards.mjs",
"verify:apktool": "npm run build:cached && node scripts/verify-apktool.mjs",
"verify:pcap": "npm run build:cached && node scripts/verify-pcap.mjs",
"verify:pe:load-config": "npm run build:cached && node scripts/verify-pe-load-config.mjs",
"verify:flutter": "npm run build:cached && node scripts/verify-flutter.mjs",
"verify:adb": "npm run build:cached && node scripts/verify-adb.mjs",
"verify:ghidra": "npm run build:cached && node scripts/verify-real-ghidra.mjs",
"verify:ghidra:seeds": "npm run build:cached && node scripts/verify/ghidra/seeds.mjs",
"verify:ghidra:mips": "npm run build:cached && node scripts/verify-real-ghidra-mips.mjs",
"verify:ghidra:ppc": "npm run build:cached && node scripts/verify-real-ghidra-ppc.mjs",
"verify:ghidra:dol": "npm run build:cached && node scripts/verify-real-ghidra-dol.mjs",
"verify:ghidra:riscv": "npm run build:cached && node scripts/verify-real-ghidra-riscv.mjs",
"verify:ghidra:6502": "npm run build:cached && node scripts/verify-real-ghidra-6502.mjs",
"verify:ghidra:m68k": "npm run build:cached && node scripts/verify-real-ghidra-m68k.mjs",
@@ -218,6 +222,7 @@
"verify:frida:bindings": "node scripts/run-typescript.mjs --noEmit -p tsconfig.frida-verification.json",
"verify:browser:dom": "node scripts/verify-browser-dom-destinations.mjs",
"verify:browser:network": "npm run build:cached && node scripts/verify-browser-network.mjs",
"verify:browser:request": "npm run build:cached && node scripts/verify-browser-request.mjs",
"verify:electron": "npm run build:cached && node scripts/verify-real-electron.mjs",
"verify:inspector": "npm run build:cached && node scripts/verify/inspector/runtime-observation.mjs",
"verify:javascript-runtime": "npm run build:cached && node scripts/verify/javascript/runtime-observation.mjs",
@@ -229,6 +234,7 @@
"verify:application-workflows": "npm run build:cached && node scripts/verify/javascript/application-workflows.mjs",
"verify:managed": "npm run build:cached && node scripts/verify/managed/conformance.mjs",
"verify:firmware": "npm run build:cached && node scripts/verify/firmware/analysis.mjs",
"verify:devicetree": "npm run build:cached && node scripts/verify/firmware/devicetree.mjs",
"fixtures:firmware": "node scripts/fixtures/firmware/generate.mjs",
"verify:android": "npm run build:cached && node scripts/verify/android/analysis.mjs",
"verify:jeb": "npm run build:cached && node scripts/verify-jeb.mjs",
+130
View File
@@ -0,0 +1,130 @@
import { once } from "node:events";
import { createServer } from "node:http";
/** The CSRF cookie and the stored bearer token the page holds; request steps must send both and the capture must not keep them. */
export const requestFixtureCsrf = "csrf-fixture-7c1d2e";
export const requestFixtureToken = "bearer-fixture-9f3a6b";
const html = `<!doctype html><meta charset="utf-8"><title>Items</title><body><main id="ready">Items</main>
<script>localStorage.setItem('auth', JSON.stringify({ token: ${JSON.stringify(requestFixtureToken.split("-"))}.join('-') }));</script>`;
const json = (response, status, value) => {
response.writeHead(status, { "Content-Type": "application/json" });
response.end(value === undefined ? undefined : JSON.stringify(value));
};
/** The item API's answer to one authorized request, as a status and body. */
const answerItems = (store, method, id, body) => {
const { items } = store;
if (id === undefined) {
if (method === "GET")
return [200, { count: items.size, results: [...items.values()] }];
if (method === "POST") {
const created = { id: store.next++, title: body.title, done: false };
items.set(created.id, created);
return [201, created];
}
return [405, { error: "method not allowed" }];
}
const item = items.get(id);
if (item === undefined) return [404, { error: "not found" }];
if (method === "GET") return [200, item];
if (method === "PUT") {
const updated = { ...item, title: body.title, done: body.done };
items.set(id, updated);
return [200, updated];
}
if (method === "DELETE") {
items.delete(id);
return [204, undefined];
}
return [405, { error: "method not allowed" }];
};
/** Start a small item API that, like a cookie-session app with a CSRF header and a stored bearer token, refuses writes without both. */
export async function startBrowserRequestSite() {
const store = {
items: new Map([
[1, { id: 1, title: "Seeded item", done: false }],
[2, { id: 2, title: "Second seeded item", done: true }],
]),
next: 3,
};
const writes = [];
const hosts = [];
const server = createServer(async (request, response) => {
const url = new URL(request.url, "http://fixture.test");
hosts.push(`${request.headers.host} ${url.pathname}`);
const chunks = [];
for await (const chunk of request) chunks.push(chunk);
const body =
chunks.length === 0 ? undefined : JSON.parse(Buffer.concat(chunks));
if (url.pathname === "/") {
response.writeHead(200, {
"Content-Type": "text/html; charset=utf-8",
"Set-Cookie": `csrftoken=${requestFixtureCsrf}; Path=/; SameSite=Lax`,
});
response.end(html);
return;
}
if (url.pathname === "/favicon.ico") {
response.writeHead(204);
response.end();
return;
}
if (url.pathname === "/api/elsewhere") {
const port = new URL(`http://${request.headers.host}`).port;
response.writeHead(302, {
Location: `http://localhost:${port}/api/items`,
});
response.end();
return;
}
if (url.pathname === "/api/session") {
json(response, 200, { token: requestFixtureToken });
return;
}
const match = /^\/api\/items(?:\/(\d+))?$/u.exec(url.pathname);
if (match === null) {
json(response, 404, { error: "not found" });
return;
}
const id = match[1] === undefined ? undefined : Number(match[1]);
if (request.method !== "GET") {
writes.push({
method: request.method,
path: url.pathname,
csrf: request.headers["x-csrftoken"] === requestFixtureCsrf,
bearer:
request.headers.authorization === `Bearer ${requestFixtureToken}`,
});
if (
request.headers["x-csrftoken"] !== requestFixtureCsrf ||
request.headers.authorization !== `Bearer ${requestFixtureToken}`
) {
json(response, 403, { error: "forbidden" });
return;
}
}
const [status, value] = answerItems(store, request.method, id, body);
json(response, status, value);
});
server.listen(0, "127.0.0.1");
await once(server, "listening");
const address = server.address();
if (typeof address !== "object" || address === null)
throw new Error("Request fixture has no TCP address");
return {
origin: `http://127.0.0.1:${address.port}`,
writes,
/** Host header and path of every request the server received. */
hosts,
items: store.items,
async close() {
server.closeAllConnections();
await new Promise((resolve, reject) =>
server.close((error) => (error ? reject(error) : resolve())),
);
},
};
}
@@ -146,7 +146,23 @@ export function assertBrowserNetworkEvidence(
);
assert.ok(first && next);
assert.equal(next.redirected_from_transaction_id, first.transaction_id);
// Every request is stamped when it was observed; finished ones carry the browser's duration.
for (const request of requestEvents)
assert.ok(
Number.isFinite(Date.parse(request.observed_at)),
`Request ${request.url.url} has no observation time`,
);
const timed = events.filter((event) => event.kind === "request-finished");
for (const finished of timed)
assert.ok(
typeof finished.elapsed_ms === "number" &&
Number.isFinite(Date.parse(finished.started_at)),
`Finished request ${finished.url.url} has no browser start or duration`,
);
assert.ok(timed.length > 0, "No request finished");
return {
timed_requests: requestEvents.length,
timed_completions: timed.length,
same_url_markers: markers,
binary_bytes: networkFixtureBinary.length,
compressed_representation: "playwright-response-body-bytes",
+376
View File
@@ -0,0 +1,376 @@
import assert from "node:assert/strict";
import { writeFile } from "node:fs/promises";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
import { chromium } from "playwright-core";
import { browserScenarioSchema } from "../../dist/domain/browserScenario.js";
import { browserScenarioCaptureSchema } from "../../dist/domain/browserScenarioCapture.js";
import { parseEvidence } from "../../dist/domain/evidence.js";
import { performScenarioRequest } from "../../dist/browser/PlaywrightScenarioRequest.js";
import { BrowserScenarioSecrets } from "../../dist/browser/BrowserScenarioSecrets.js";
import { PrivateRuntimeRoot } from "../../dist/process/PrivateRuntimeRoot.js";
import {
requestFixtureCsrf,
requestFixtureToken,
startBrowserRequestSite,
} from "../fixtures/browser-request-site.mjs";
import { withVerifierCleanup } from "./verifier-cleanup.mjs";
import { createPackageVerifierCommands } from "./verify-package-commands.mjs";
const authenticated = [
{ name: "X-CSRFToken", value: { source: "cookie", name: "csrftoken" } },
{
name: "Authorization",
value: { source: "local_storage", key: "auth", json_pointer: "/token" },
prefix: "Bearer ",
},
];
const read = (step_id, path) => ({
step_id,
action: "request",
method: "GET",
path,
expected_status: [200],
});
const write = (step_id, method, path, { json, status }) => ({
step_id,
action: "request",
method,
path,
headers: authenticated,
...(json === undefined ? {} : { json }),
expected_status: [status],
});
const pause = (step_id) => ({
step_id,
action: "wait_for_timeout",
duration_ms: 1100,
});
/** A recording that writes two scratch items and reads each write back at once, as an environment needs. */
const writeSession = (executable, origin) =>
browserScenarioSchema.parse({
browser: { mode: "launch", executable_path: executable },
start_url: { url: `${origin}/` },
actions: [
{
step_id: "ready",
action: "wait_for",
locator: { kind: "css", selector: "#ready" },
state: "visible",
timeout_ms: 10_000,
},
read("list-1", "/api/items"),
read("list-2", "/api/items"),
read("item-1", "/api/items/1"),
write("create-1", "POST", "/api/items", {
json: { title: "Scratch item 1" },
status: 201,
}),
read("created-1", "/api/items/3"),
read("list-created-1", "/api/items"),
write("create-2", "POST", "/api/items", {
json: { title: "Scratch item 2" },
status: 201,
}),
read("created-2", "/api/items/4"),
read("list-created-2", "/api/items"),
pause("settle-creates"),
write("update-1", "PUT", "/api/items/3", {
json: { title: "Scratch item 1 renamed", done: true },
status: 200,
}),
read("updated-1", "/api/items/3"),
read("list-updated-1", "/api/items"),
write("update-2", "PUT", "/api/items/4", {
json: { title: "Scratch item 2 renamed", done: false },
status: 200,
}),
read("updated-2", "/api/items/4"),
read("list-updated-2", "/api/items"),
write("delete-1", "DELETE", "/api/items/3", { status: 204 }),
{ ...read("deleted-1", "/api/items/3"), expected_status: [404] },
read("list-deleted-1", "/api/items"),
write("delete-2", "DELETE", "/api/items/4", { status: 204 }),
{ ...read("deleted-2", "/api/items/4"), expected_status: [404] },
read("list-deleted-2", "/api/items"),
read("session", "/api/session"),
],
capture: {
at_end: ["url"],
network: { request_body: true, response_body: true, header_values: true },
},
});
/** Run the CLI under the owner, which stops it and every browser it launched on timeout or failure. */
const cliOf = (commands, entrypoint) => async (args, maxBuffer) =>
(
await commands.exec(process.execPath, [entrypoint, ...args, "--json"], {
env: {
...process.env,
REA_LOG_LEVEL: "silent",
HOPPER_LAUNCHER_PATH: "/rea-unconfigured-provider/hopper",
},
timeout: 120_000,
maxBuffer,
})
).stdout;
const captureOf = async (cli, scenario, maxBuffer) => {
const stdout = await cli(
["capture-browser-scenario", JSON.stringify(scenario)],
maxBuffer,
);
const evidence = parseEvidence(JSON.parse(stdout));
return {
stdout,
evidence,
capture: browserScenarioCaptureSchema.parse(evidence.normalized_result),
};
};
/** Every request step completed and sent exactly its own request. */
const assertRequestsAttributed = (scenario, capture) => {
const steps = capture.steps.filter(
(step) => step.step_id !== "scenario_start",
);
assert.equal(steps.length, scenario.actions.length);
for (const step of steps)
assert.equal(step.status, "completed", JSON.stringify(step));
const requests = capture.events.items.filter(
(event) => event.kind === "request",
);
scenario.actions.forEach((action, index) => {
if (action.action !== "request") return;
const stepIndex = capture.steps.findIndex(
(step) => step.step_id === action.step_id,
);
assert.equal(capture.steps[stepIndex].action, "request");
const sent = requests.filter(
(event) =>
event.step_index === stepIndex &&
event.method === action.method &&
new URL(event.url.url).pathname === action.path,
);
assert.equal(sent.length, 1, `step ${String(index)} ${action.step_id}`);
});
};
/** The CSRF cookie and stored token reached the server but no record keeps them. */
const assertPageValuesRedacted = (evidence, capture) => {
// Bodies are base64 in the Evidence, so decode them before looking for the values.
const contents = capture.events.items.filter(
(event) => event.kind === "network-content",
);
const bodies = contents
.filter((event) => event.body.state === "captured")
.map((event) => Buffer.from(event.body.content, "base64").toString());
const headerValues = contents.flatMap((event) =>
event.headers.state === "captured"
? event.headers.items.map((item) => item.value ?? "")
: [],
);
const retained = [JSON.stringify(evidence), ...bodies, ...headerValues];
for (const value of [requestFixtureCsrf, requestFixtureToken])
assert.ok(
retained.every((text) => !text.includes(value)),
`${value} was retained`,
);
assert.ok(
headerValues.some((value) =>
value.includes("[REDACTED:page:create-1.X-CSRFToken]"),
),
"the CSRF header was not recorded redacted",
);
assert.ok(
bodies.some(
(body) =>
body.includes('"token"') && body.includes("[REDACTED:page:create-1."),
),
"the session response was not recorded redacted",
);
assert.ok(
capture.limitations.some((line) => line.startsWith("Request steps sent")),
);
};
/** A status outside expected_status fails the step and cancels the rest; a redirect to another origin is not followed. */
const assertUnexpectedStatusFails = async (cli, scenario, site) => {
const failing = browserScenarioSchema.parse({
...scenario,
actions: [
write("delete-missing", "DELETE", "/api/items/999", { status: 204 }),
read("after", "/api/items"),
],
});
const { capture } = await captureOf(cli, failing, 16 * 1024 * 1024);
const missing = capture.steps.find(
({ step_id }) => step_id === "delete-missing",
);
assert.equal(missing.status, "failed");
assert.match(missing.error ?? "", /answered 404; expected 204/u);
assert.equal(
capture.steps.find(({ step_id }) => step_id === "after").status,
"cancelled",
);
const redirected = browserScenarioSchema.parse({
...scenario,
actions: [read("elsewhere", "/api/elsewhere")],
});
const followed = await captureOf(cli, redirected, 16 * 1024 * 1024);
const step = followed.capture.steps.find(
({ step_id }) => step_id === "elsewhere",
);
assert.equal(step.status, "failed");
assert.match(step.error ?? "", /request steps do not follow/u);
// Chrome reports the redirected request and aborts it; the other origin must never receive it.
assert.ok(
site.hosts.every((line) => !line.startsWith("localhost")),
"a request step followed a redirect to another origin",
);
};
/** A genuinely busy page must not dispatch a write after its deadline or host cancellation. */
const assertQueuedWritesStopped = async (executable, site) => {
const browser = await chromium.launch({
executablePath: executable,
headless: true,
});
try {
const page = await browser.newPage({ serviceWorkers: "block" });
await page.goto(site.origin);
for (const cancelled of [false, true]) {
const scenario = browserScenarioSchema.parse({
...writeSession(executable, site.origin),
actions: [
{
...write("queued", "POST", "/api/items", {
json: { title: "must not be created" },
status: 201,
}),
headers: [
{
name: "X-CSRFToken",
value: { source: "literal", value: requestFixtureCsrf },
},
{
name: "Authorization",
value: {
source: "literal",
value: `Bearer ${requestFixtureToken}`,
},
},
],
timeout_ms: cancelled ? 5000 : 50,
},
],
});
const secrets = BrowserScenarioSecrets.resolve(scenario, {});
if (!secrets.ok) throw secrets.error;
// A preceding evaluation monopolizes the page thread while Node can
// still cancel. Its protocol request is queued before the request step.
const busy = page.evaluate(() => {
const until = Date.now() + 300;
while (Date.now() < until) {}
});
const controller = new AbortController();
const before = site.writes.length;
const request = performScenarioRequest({
page,
action: scenario.actions[0],
secrets: secrets.value,
startOrigin: site.origin,
signal: controller.signal,
});
const outcome = request.then(
() => ({ ok: true }),
(error) => ({ ok: false, error }),
);
const timer = cancelled
? setTimeout(() => controller.abort("queued request cancelled"), 30)
: undefined;
try {
const result = await outcome;
assert.equal(result.ok, false, "queued write unexpectedly completed");
assert.equal(
site.writes.length,
before,
"the server received a late write",
);
await busy;
} finally {
if (timer !== undefined) clearTimeout(timer);
}
}
} finally {
await browser.close();
}
};
/**
* Record a write session through request steps in a real browser, then build
* an environment from it: every request lands in its own step, the page's
* CSRF cookie and stored token authenticate the writes and never reach the
* Evidence, and the build serves the recorded writes.
*/
export async function verifyBrowserRequestSteps(
executable,
entrypoint = fileURLToPath(new URL("../rea.mjs", import.meta.url)),
) {
const site = await startBrowserRequestSite();
let root;
let commands;
const proof = async () => {
root = await PrivateRuntimeRoot.create({ prefix: "rea-request-e2e-" });
commands = await createPackageVerifierCommands({ workspace: root });
const cli = cliOf(commands, entrypoint);
const scenario = writeSession(executable, site.origin);
const { stdout, evidence, capture } = await captureOf(
cli,
scenario,
64 * 1024 * 1024,
);
assertRequestsAttributed(scenario, capture);
assert.equal(site.writes.length, 6);
assert.ok(site.writes.every(({ csrf, bearer }) => csrf && bearer));
assertPageValuesRedacted(evidence, capture);
const capturePath = join(root.path, "items.capture.json");
await writeFile(capturePath, stdout);
const built = JSON.parse(
await cli(
["environment-build", capturePath, join(root.path, "env")],
16 * 1024 * 1024,
),
);
assert.equal(built.status, "built");
assert.equal(built.name, "items");
const served = built.coverage.writes.served.map(({ tool }) => tool).sort();
assert.deepEqual(
served,
["delete_items_by_id", "post_items", "put_items_by_id"],
JSON.stringify(built.coverage),
);
await assertUnexpectedStatusFails(cli, scenario, site);
await assertQueuedWritesStopped(executable, site);
return {
request_steps: scenario.actions.filter(
({ action }) => action === "request",
).length,
authenticated_writes: site.writes.length,
served_writes: served,
tool_count: built.tool_count,
queued_writes_refused: 2,
};
};
// The owner stops its commands' processes before removing the root; a failed stop keeps the root and is reported.
return withVerifierCleanup(proof, [
() => (commands === undefined ? root?.close() : commands.close()),
() => site.close(),
]);
}
+22
View File
@@ -127,6 +127,7 @@ export const toolFamilyCatalog = (sources) => {
...sources.webScriptContracts.WEB_SCRIPT_TOOL_CONTRACTS,
...sources.javascriptRecoveryContracts
.JAVASCRIPT_RECOVERY_TOOL_CONTRACTS,
...sources.environmentContracts.ENVIRONMENT_TOOL_CONTRACTS,
],
},
{
@@ -267,6 +268,20 @@ export const providerCatalog = (sources) => {
({ name }) => name === "inspect_pe_imports",
),
},
{
identity: sources.artifactProviders.DEVICETREE_PROVIDER,
contracts:
sources.binaryDiagnosticsContracts.BINARY_DIAGNOSTICS_TOOL_CONTRACTS.filter(
({ name }) => name === "inspect_devicetree",
),
},
{
identity: sources.artifactProviders.PE_LOAD_CONFIG_PROVIDER,
contracts:
sources.binaryDiagnosticsContracts.BINARY_DIAGNOSTICS_TOOL_CONTRACTS.filter(
({ name }) => name === "inspect_pe_load_config",
),
},
{
identity: sources.artifactProviders.ELF_NOTES_PROVIDER,
contracts:
@@ -281,6 +296,13 @@ export const providerCatalog = (sources) => {
({ name }) => name === "inspect_apple_ips",
),
},
{
identity: sources.artifactProviders.KERNEL_MODULE_PROVIDER,
contracts:
sources.binaryDiagnosticsContracts.BINARY_DIAGNOSTICS_TOOL_CONTRACTS.filter(
({ name }) => name === "inspect_kernel_module",
),
},
{
identity: sources.artifactProviders.BYTE_ENTROPY_PROVIDER,
contracts:
+1
View File
@@ -116,6 +116,7 @@ const SOURCE_PATHS = {
applicationContracts: "dist/contracts/applicationToolContracts.js",
analysisViewContracts: "dist/contracts/analysisViewToolContracts.js",
webScriptContracts: "dist/contracts/webScriptToolContracts.js",
environmentContracts: "dist/contracts/environmentToolContracts.js",
javascriptRecoveryContracts:
"dist/contracts/javascript/javascriptRecoveryToolContracts.js",
javascriptRecoveryProvider: "dist/javascript/recovery/WakaruRelease.js",
+5
View File
@@ -0,0 +1,5 @@
/** Preserve the caller's npm entry and arguments without invoking a shell. */
export function npmInvocation(
arguments_: readonly string[],
environment: Readonly<NodeJS.ProcessEnv>,
): { readonly command: string; readonly arguments: readonly string[] };
+19
View File
@@ -0,0 +1,19 @@
import { dirname, join } from "node:path";
/** Preserve the caller's npm entry and arguments without invoking a shell. */
export const npmInvocation = (arguments_, environment) => {
const selectedEntry = environment.npm_execpath;
if (
selectedEntry !== undefined &&
(typeof selectedEntry !== "string" || selectedEntry.length === 0)
)
throw new Error("npm_execpath must name the selected npm CLI entry point");
const entry =
selectedEntry ??
(process.platform === "win32"
? join(dirname(process.execPath), "node_modules/npm/bin/npm-cli.js")
: undefined);
return entry === undefined
? { command: "npm", arguments: arguments_ }
: { command: process.execPath, arguments: [entry, ...arguments_] };
};
@@ -0,0 +1,8 @@
export { runOwnedVerifierTerminal } from "./verifier-terminal-owner.mjs";
export type {
VerifierTerminalOptions,
VerifierTerminalDependencies,
VerifierTerminalResult,
VerifierTerminalFailure,
VerifierTerminalCleanupFailure,
} from "./verifier-terminal-owner.mjs";
+12
View File
@@ -0,0 +1,12 @@
let implementation;
/** Load the development terminal owner and canonical process helpers together. */
export async function runOwnedVerifierTerminal(options, dependencies) {
implementation ??= importVerifierSource(
new URL("./verifier-terminal-owner.mts", import.meta.url).href,
import.meta.url,
);
const module = await implementation;
return module.runOwnedVerifierTerminal(options, dependencies);
}
import { importVerifierSource } from "./verifier-source-loader.mjs";
+141
View File
@@ -0,0 +1,141 @@
import {
createVerifierCommands,
VerifierCommandExitError,
type VerifierCommands,
type VerifierCommandsOptions,
type VerifierCommandOptions,
type VerifierCommandOutput,
type VerifierCleanupOwner,
} from "./verifier-command-owner.mjs";
import { npmInvocation } from "./npm-invocation.mjs";
export interface PackageVerifierWorkspace {
readonly path: string;
close(): Promise<void>;
}
export interface PackageVerifierCommands extends Pick<
VerifierCommands,
"exec"
> {
execNpm(
arguments_: readonly string[],
options?: VerifierCommandOptions,
): Promise<VerifierCommandOutput>;
run(
command: string,
arguments_: readonly string[],
environment?: NodeJS.ProcessEnv,
): Promise<string>;
runWithStatus(
command: string,
arguments_: readonly string[],
environment?: NodeJS.ProcessEnv,
): Promise<{ readonly stdout: string; readonly status: number }>;
adopt(owner: VerifierCleanupOwner, resource?: string): void;
close(): Promise<void>;
}
/** The same command/workspace capability remains available after failed close. */
export class PackageVerifierCleanupFailure extends Error {
readonly path: string;
constructor(
cause: unknown,
readonly cleanupOwner: PackageVerifierCommands,
readonly workspace: PackageVerifierWorkspace,
) {
super(
`Package verification cleanup incomplete; retained ${workspace.path}: ${cause instanceof Error ? cause.message : String(cause)}`,
{ cause },
);
this.name = "PackageVerifierCleanupFailure";
this.path = workspace.path;
}
}
class PackageCommands implements PackageVerifierCommands {
#workspaceReleaseStarted = false;
#closing: Promise<void> | undefined;
constructor(
readonly workspace: PackageVerifierWorkspace,
private readonly commands: VerifierCommands,
) {}
exec(
command: string,
arguments_: readonly string[],
options: VerifierCommandOptions = {},
): Promise<VerifierCommandOutput> {
return this.commands.exec(command, arguments_, options);
}
execNpm(
arguments_: readonly string[],
options: VerifierCommandOptions = {},
): Promise<VerifierCommandOutput> {
const invocation = npmInvocation(arguments_, options.env ?? process.env);
return this.exec(invocation.command, invocation.arguments, options);
}
async run(
command: string,
arguments_: readonly string[],
environment?: NodeJS.ProcessEnv,
): Promise<string> {
return (await this.exec(command, arguments_, { env: environment })).stdout;
}
async runWithStatus(
command: string,
arguments_: readonly string[],
environment?: NodeJS.ProcessEnv,
): Promise<{ readonly stdout: string; readonly status: number }> {
try {
return {
stdout: await this.run(command, arguments_, environment),
status: 0,
};
} catch (cause: unknown) {
if (cause instanceof VerifierCommandExitError && cause.code === 1)
return { stdout: cause.snapshot.stdout.text, status: 1 };
throw cause;
}
}
adopt(owner: VerifierCleanupOwner, resource = this.workspace.path): void {
if (this.#workspaceReleaseStarted)
throw new Error("Package workspace release has started");
this.commands.adopt(owner, resource);
}
close(): Promise<void> {
if (this.#closing !== undefined) return this.#closing;
let attempt: Promise<void>;
attempt = this.#release().catch((cause: unknown) => {
if (this.#closing === attempt) this.#closing = undefined;
throw new PackageVerifierCleanupFailure(cause, this, this.workspace);
});
this.#closing = attempt;
return attempt;
}
async #release(): Promise<void> {
await this.commands.close();
this.#workspaceReleaseStarted = true;
await this.workspace.close();
}
}
/** Remove the package workspace only after its shared command owner releases. */
export async function createPackageVerifierCommands({
workspace,
launcher,
}: VerifierCommandsOptions & {
readonly workspace: PackageVerifierWorkspace;
}): Promise<PackageVerifierCommands> {
const commands = await createVerifierCommands(
launcher === undefined ? {} : { launcher },
);
return new PackageCommands(workspace, commands);
}
Loaded 100 of 685 files, more files were not shown because too many files have changed in this diff. Show more