[PR #6376] [MERGED] feat(config): make the bridge request ceiling configurable #7321

Closed
opened 2026-11-01 21:47:02 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/6376
Author: @morluto
Created: 10/22/2026
Status: ✅ Merged
Merged: 10/22/2026
Merged by: @morluto

Base: main ← Head: cursor/bridge-max-request-bytes-8c69


📝 Commits (10+)

  • 28cb952 fix(reconstruction): dedupe residual unknown ids with uniqueSorted
  • d143798 feat(config): make the bridge request ceiling configurable
  • 82a2a4c fix(cutter): keep cleanup ownership when a sent command's reply is oversized
  • 8fb084c style: format the bridge request ceiling sources
  • eea805f fix(cutter): exclude the LF from the response frame budget
  • 6f64f4b fix(bridge): measure escaped JSON before allocating the request
  • 584a41b fix(config): derive the bridge request maximum from JSON expansion
  • 47a0283 fix(bridge): size the Cutter frame deadline and send the measured Hopper payload
  • da3628e fix(config): bound the bridge request maximum by the serialized string
  • 4d1d7c3 fix(hopper): serialize a bridge request when it reaches the wire

📊 Changes

43 files changed (+3390 additions, -286 deletions)

View changed files

📝 .agents/skills/reverse-engineer-anything/references/native-and-artifacts.md (+29 -0)
📝 bridge/cutter/rea_bridge.py (+598 -95)
📝 bridge/hopper_bridge.py (+91 -5)
📝 docs/reverse-engineering-tools.md (+40 -9)
📝 scripts/lib/test-projects.mjs (+1 -1)
📝 src/application/Doctor.test.ts (+1 -0)
📝 src/application/ReconstructionObligationCandidates.ts (+4 -4)
📝 src/application/SetupRegistrationEnvironment.ts (+1 -0)
➕ src/config/bridgeMaxRequestBytes.test.ts (+147 -0)
➕ src/config/bridgeMaxRequestBytes.ts (+184 -0)
📝 src/config/environment.ts (+2 -0)
📝 src/config/parseConfig.ts (+8 -0)
📝 src/config/types.ts (+3 -0)
📝 src/contracts/cutterToolContracts.ts (+2 -2)
📝 src/cutter/CutterBridgeClient.ts (+248 -145)
➕ src/cutter/cutterAuthenticatedResponse.test.ts (+172 -0)
➕ src/cutter/cutterAuthenticatedResponse.ts (+340 -0)
➕ src/cutter/cutterBridgeMac.test.ts (+42 -0)
➕ src/cutter/cutterBridgeMac.ts (+16 -0)
📝 src/hopper/BridgeLauncher.ts (+21 -3)

...and 23 more files

📄 Description

Summary

Part of #6176.

The Hopper and Cutter request payload ceiling was a hard-coded 16 MiB constant in both Python bridges and the TypeScript pre-dispatch check. REA_BRIDGE_MAX_REQUEST_BYTES is now the single setting. Unset, the ceiling stays 16777216 bytes. A decimal integer from 7464 to 536870887 is accepted. Anything else is configuration_invalid and does not fall back to the default.

Current main also calls undefined uniqueBy while merging residual unknown ids, so typecheck failed before this change. Those ids now use the existing uniqueSorted helper, matching the adjacent string lists.

Problem and expected behavior

Three copies of the 16 MiB request budget had to agree: MAX_REQUEST_BYTES in bridge/hopper_bridge.py, MAX_FRAME_BYTES in bridge/cutter/rea_bridge.py, and the client check before dispatch. There was no runtime override.

A caller can now raise or lower that ceiling with REA_BRIDGE_MAX_REQUEST_BYTES. The payload budget still excludes the terminating LF, and an incomplete Hopper frame is still Incomplete bridge request frame. An out-of-band bridge without the setting stays at 16777216 bytes. The client compares the bridge advertisement before a discovery status probe and before sending a command. A different ceiling is a mismatch only after the hello proves that descriptor is a live authenticated bridge. A stale descriptor is ignored, and that hello does not send a status request. Discovery fails when any such live ceiling differs, even if another live session matches. A command response discarded because it exceeds the ceiling after dispatch stays an unknown execution and sets output_truncated.

Change and scope

src/config/bridgeMaxRequestBytes.ts owns the setting, using the same schema in startup parseConfig and doctor parseEnvironment. The floor, 7464 bytes, is the authenticated Cutter frame of Hopper's largest bounded symbol name (1024 U+0001 characters). The required Cutter recovery status, 64 receipts with an unknown path, is smaller than that floor. A status or command path that fits is kept. A path that does not fit is null, so the client does not reuse the descriptor path with the new generation. The session id, connection proof, and receipts remain. The maximum, 536870887, is one byte under Node's 536870888-character string ceiling so the framing LF still fits. The ceiling is the encoded payload. Escaped JSON is measured against it before JSON.stringify, so a U+0001 expansion is counted once and is not divided into the maximum again. A structured Cutter reply is admitted when that encoded payload fits. Numbers, booleans, null, and container punctuation use their JSON lengths, so a dense array is not rejected by a fixed per-value charge. Scalar values in a reply are encoded in bounded batches. A 100 MiB encoded ceiling is accepted.

Hopper receives a caller-selected value as a bootstrap integer, which wins over the process environment. Cutter reads the environment that starts Cutter and publishes max_request_bytes on its descriptor. A missing advertisement means 16777216. Cutter response frames use the same ceiling and also exclude the terminating LF. The 25000 ms request deadline and the 256-candidate discovery budget do not follow this setting. Neither does the fixed cap of 8 Cutter connection threads.

Setup keeps a caller-selected value in SELECTED_REGISTRATION_SETTINGS. Oversize and mismatch text names the setting, effective value, source, default, floor, and maximum. Escaped request JSON is measured within the ceiling before JSON.stringify. The Cutter hello frame still must arrive within 1 second. The authenticated command frame is read at one second per mebibyte of the ceiling, and that read never waits longer than the 25 second client deadline. The bridge searches only the newly read bytes for the framing LF. Hopper serializes a request when it reaches the wire, so a call waiting in the FIFO does not retain a second encoded frame. Cutter hashes the HMAC prefix and the payload as separate updates on both the client and the bridge, so an exact-limit frame does not build one preimage string. The plugin measures and signs the response from bounded JSON pieces, then writes that frame in 64 KiB chunks. The client unescapes each socket chunk into one response string, checks the proof, and then parses that string. A hello that never sends its authenticated frame does not hold the accept loop. At most 8 connection threads run. A further accept is closed, and a failure to start a thread closes that socket without stopping the listener. completed_connection is the longest prefix of accepted connections whose handlers have terminated. A mismatched descriptor counts only after that hello proof. A response dropped for the ceiling sets output_truncated on the unknown execution.

Follow the contribution scope. This configures an existing local bridge admission. It does not add a provider or a target execution, injection, mutation, or protection-recovery capability.

not applicable

Contract and boundary impact

  • Semantic owner and earliest changed stage: src/config/bridgeMaxRequestBytes.ts owns the ceiling. Startup and doctor parse it before launch. The bridges apply it when they load. CutterBridgeClient.listSessions owns whether an advertised ceiling is a live session.
  • Contribution scope fit and linked maintainer decision: ordinary configuration of an existing admission. Maintainer scope decision not applicable.
  • CLI and MCP/tool-catalog contract: cutter_command documents the setting. list_cutter_sessions states that the 256-candidate discovery budget is separate and still has no runtime override. Invalid values are configuration_invalid. A ceiling mismatch during discovery is resource_constraint when the hello proves a live authenticated bridge, including when another session matches. A stale descriptor is not that mismatch.
  • Provider, bridge, target-format, or platform compatibility: Hopper health gains max_request_bytes. Cutter descriptors gain the same optional field. Missing means 16777216, so an older bridge still matches a default client. Cutter responses share the ceiling and exclude the terminating LF. Hopper responses are unchanged. Both Python bridges accept only ASCII decimal digits.
  • Evidence, artifact, provenance, or reconstruction contract: a Cutter reply that exceeds the ceiling after the command was sent stays an unknown execution (transport-response-missing) with output_truncated true. Its message names the ceiling and says the command may have completed, Evidence records the omitted output, and receipt cleanup still waits. A frame that overflows before the command is sent is a resource_constraint. A response whose payload is exactly the ceiling, followed by LF, is accepted. A tight status or command reports current_file as null when the observed path does not fit, and the client does not replace that null with the descriptor path. A structured reply under the encoded ceiling is returned rather than marked output_truncated. completed_connection advances only through connections that have terminated.
  • Target selection, authority, upstream behavior, and cleanup details (if applicable): none. The ceiling bounds local bridge bytes. A mismatch or oversize refusal does not send the command to the mismatched peer. The discovery proof is a hello and closes before a status request. A Hopper startup mismatch still runs the existing cleanup path. An oversized Cutter reply after dispatch keeps the existing pending-cleanup owner.
  • Generated metadata (docs/public/product-catalog.json), package, or installation impact (CI generates and validates ignored catalogs and skills/; edit authored instructions in .agents/skills/reverse-engineer-anything/ and never commit build outputs): authored skill text under .agents/skills/reverse-engineer-anything/references/native-and-artifacts.md only. No generated catalogs are committed.

Expected effects (check known effects; use documentation-only on its own).
Unchecked items do not establish absence; leave uncertainties visible.

  • Read existing files or captured data.
  • Launch or attach to a process.
  • Inject instrumentation or run scripts/code.
  • Change process memory, registers, files, or configuration.
  • Contact network services.
  • Recover protected code or work around protections.
  • Documentation or usability only; no target interaction.
  • Not sure yet; investigation or maintainer guidance needed.

Evidence and regression coverage

  • Tests added or updated: owner parse tests, including a 100 MiB encoded ceiling and the string-bound maximum; doctor, setup, and client-configuration rejection and preservation; Hopper bootstrap injection; Hopper health parse; Hopper and Cutter frame probes, including a lowered ceiling, a ceiling above 16 MiB, invalid values, and non-ASCII digits; client mismatch and pre-dispatch refusal; a stale descriptor whose ceiling differs is ignored, including beside a matching live session; an unauthenticated listener is not a mismatch; discovery fails when one live session matches and another live authenticated ceiling does not, and that probe sends no status or command; an exact-ceiling Cutter response, including one whose LF arrives in a later chunk; one byte over that ceiling stays an unknown execution, sets output_truncated, names the setting, and Evidence records the omitted output; a U+0001 command whose escaped JSON exceeds the ceiling is refused before JSON.stringify on both bridges; the authenticated Cutter read budget is 16 seconds at the default ceiling, 17 seconds at 17 MiB, and 25 seconds at the maximum; a queued Hopper request is not serialized until the active request leaves the wire; the small Cutter HMAC split-update check stays in the default suite, and the maximum-sized preimage is opt-in via REA_BRIDGE_MAC_LIMIT_TEST=1; a status with a 4095-byte path and 64 receipts at a 7464-byte ceiling reports current_file as null and keeps the new generation; a command reply that does not fit keeps completion and generation and reports an oversized path as null; a short path is kept when a long version is what overflows; 2000 zeroes are admitted under an 8192-byte ceiling and 8000 zeroes are rejected; 40000 zeroes encode as a handful of bounded pieces and match compact JSON; a status does not complete an earlier connection that is still reading; a ninth connection is not accepted while the worker cap is lowered to 1, and the listener still serves after that worker exits; a failed Thread.start closes that socket, leaves the descriptor in place, and does not stick the completion watermark; a streamed socket frame matches the joined encoding for quotes, a backslash, a newline, and U+2028; the client decoder accepts the same escapes, a raw U+2028, and a long run of backslashes.
  • Base reproduction or other evidence: unset probes still serve exactly 16777216 payload bytes plus LF and still reject an incomplete frame with Incomplete bridge request frame. Python tests/fixtures/cutter_bridge_protocol_test.py passed through cutterBridgeProducer.test.ts on e44d54056 (31 tests). A mixed value encoded by the streamed writer matched the previous encoder byte for byte (34179 bytes) on an earlier commit; that encoder path is unchanged aside from scalar batching, which the new equality test covers.
  • User-visible CLI/MCP output (if applicable): invalid values fail doctor and startup as configuration_invalid and name REA_BRIDGE_MAX_REQUEST_BYTES. Oversize and mismatch text names the effective value, source, default, floor, and maximum. A tight status message names the setting and says the path is unknown. A mismatched discovery names the setting and says no status request was sent to the mismatched session. An oversized command reply's Evidence has output_truncated: true and the omitted-output limitation.
  • Remaining proof gaps: no live Hopper or Cutter GUI. Bridge behavior is from the shipped Python modules over socket pairs and the Cutter protocol fixture. Peak memory for a queue of maximum-sized Hopper frames was not measured; the queue no longer retains those frames. A 64 MiB fitting Cutter response was measured in one process per encoder on ca8dcc6cb, including construction of the output string. The retained encoder peaked at 345868 KiB RSS (0.781s elapsed, 0.513s user CPU). The streamed write peaked at 84016 KiB (1.841s elapsed, 1.834s user CPU). Both frames were 67109017 bytes and were not truncated. A 512 MiB frame was not measured. Agent tokens do not apply. The default suite does not hash the 512 MiB HMAC preimage. The 7 million zero timing from review was not re-measured; the regression locks piece count and JSON equality. The 16 MiB backslash scan timing was not re-measured; the decoder now stops at the first quote or backslash.

For evidence-bearing changes:

  • Observed, derived, and inferred claims remain distinguishable.
  • Artifact identity, source provenance, and failed attempts remain preserved.
  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Validation performed

On the tree committed as 0849b0452 unless noted:

  • npm run check:fast — passed (typecheck and lint).
  • npm run test:focused -- tests/boundary/providers/cutterBridgeRequestLimit.test.ts — 10 passed.
  • npm run test:focused -- tests/boundary/providers/CutterBridgeClient.test.ts — 30 passed on aa58c8939. The client source is unchanged in 0849b0452.

Earlier on this branch, before aa58c8939: npm run format:check passed (5132 files); python3 tests/fixtures/cutter_bridge_protocol_test.py passed (31 tests); npm run test:focused for the Cutter producer, null-identity, and client files passed 46 tests on e44d54056. Those suites were not re-run for this discovery and truncation change. npm run test:local -- src/cutter/cutterBridgeMac.test.ts src/config/bridgeMaxRequestBytes.test.ts passed 17 with 1 skipped on fbd0868fe. The skipped test is the opt-in 512 MiB HMAC proof. npm run test:acceptance -- tests/acceptance/setup/registrationRuntimeEnvironmentCli.test.ts passed 15 with 1 skipped on an earlier registration commit.

Real Hopper and Cutter applications were not available in this environment.

Compatibility, safety, and release

  • Breaking changes or migration steps: none for an unset ceiling; it stays 16777216 bytes. A configured value outside 7464 to 536870887 bytes is configuration_invalid. A missing health or descriptor field means that same default. A status or command path that does not fit is null rather than the descriptor path. A structured reply that fits the encoded ceiling is returned instead of output_truncated. Discovery fails when a live authenticated ceiling mismatches. A stale descriptor does not. A command response discarded for the ceiling reports output_truncated. completed_connection no longer means "the status connection ordinal minus one".
  • Real Hopper/Ghidra, browser, or OS coverage: not run. Python bridge modules and local socket fixtures were executed.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: the ceiling bounds bytes read from the local bridge. Invalid configuration is refused. The Hopper bootstrap injects only the already-parsed integer. Oversized escaped JSON is counted before allocation. A waiting Hopper request does not retain an encoded frame. Cutter proof bytes are unchanged; the prefix and payload are hashed as separate updates on both sides. A fitting Cutter response is measured and signed from bounded pieces and written in 64 KiB chunks. The client keeps one decoded response string rather than the paged wire frame plus a concatenated copy and an outer JSON string. At most 8 unauthenticated connection threads are admitted. A ceiling mismatch is accepted only from a peer that proves the descriptor token. No new target authority.

Author readiness

Human owner: @morluto

  • I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
  • I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
  • I declare this PR ready for initial maintainer review.

Before merge

  • This PR addresses a concrete problem or an agreed enhancement.
  • Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.
Open in Web Open in Cursor 

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/6376 **Author:** [@morluto](https://github.com/morluto) **Created:** 10/22/2026 **Status:** ✅ Merged **Merged:** 10/22/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `cursor/bridge-max-request-bytes-8c69` --- ### 📝 Commits (10+) - [`28cb952`](https://github.com/morluto/rea/commit/28cb9528b652322c168a7d840e43e6a74c485628) fix(reconstruction): dedupe residual unknown ids with uniqueSorted - [`d143798`](https://github.com/morluto/rea/commit/d14379870a0ab7dc5e323c1b18dcf0dc2753ba8f) feat(config): make the bridge request ceiling configurable - [`82a2a4c`](https://github.com/morluto/rea/commit/82a2a4cc06ccfe06dcaa2b0c7708319075101d2e) fix(cutter): keep cleanup ownership when a sent command's reply is oversized - [`8fb084c`](https://github.com/morluto/rea/commit/8fb084cc805689a69305b2e875ad6f52eca2c025) style: format the bridge request ceiling sources - [`eea805f`](https://github.com/morluto/rea/commit/eea805f752570dbe0cc9e05d84c5dd6b4d387f83) fix(cutter): exclude the LF from the response frame budget - [`6f64f4b`](https://github.com/morluto/rea/commit/6f64f4b0adaeae2bf61e4faeb13ae82aa5ff455e) fix(bridge): measure escaped JSON before allocating the request - [`584a41b`](https://github.com/morluto/rea/commit/584a41b31f004aa38c8904c8d9cc2039721623af) fix(config): derive the bridge request maximum from JSON expansion - [`47a0283`](https://github.com/morluto/rea/commit/47a028360a68ff45486b77c625b29321f3b2ab30) fix(bridge): size the Cutter frame deadline and send the measured Hopper payload - [`da3628e`](https://github.com/morluto/rea/commit/da3628e46ecdb7e3e9778ff429e444291dc28f2f) fix(config): bound the bridge request maximum by the serialized string - [`4d1d7c3`](https://github.com/morluto/rea/commit/4d1d7c3155b615d5efbe49df13911ebdffddbd5b) fix(hopper): serialize a bridge request when it reaches the wire ### 📊 Changes **43 files changed** (+3390 additions, -286 deletions) <details> <summary>View changed files</summary> 📝 `.agents/skills/reverse-engineer-anything/references/native-and-artifacts.md` (+29 -0) 📝 `bridge/cutter/rea_bridge.py` (+598 -95) 📝 `bridge/hopper_bridge.py` (+91 -5) 📝 `docs/reverse-engineering-tools.md` (+40 -9) 📝 `scripts/lib/test-projects.mjs` (+1 -1) 📝 `src/application/Doctor.test.ts` (+1 -0) 📝 `src/application/ReconstructionObligationCandidates.ts` (+4 -4) 📝 `src/application/SetupRegistrationEnvironment.ts` (+1 -0) ➕ `src/config/bridgeMaxRequestBytes.test.ts` (+147 -0) ➕ `src/config/bridgeMaxRequestBytes.ts` (+184 -0) 📝 `src/config/environment.ts` (+2 -0) 📝 `src/config/parseConfig.ts` (+8 -0) 📝 `src/config/types.ts` (+3 -0) 📝 `src/contracts/cutterToolContracts.ts` (+2 -2) 📝 `src/cutter/CutterBridgeClient.ts` (+248 -145) ➕ `src/cutter/cutterAuthenticatedResponse.test.ts` (+172 -0) ➕ `src/cutter/cutterAuthenticatedResponse.ts` (+340 -0) ➕ `src/cutter/cutterBridgeMac.test.ts` (+42 -0) ➕ `src/cutter/cutterBridgeMac.ts` (+16 -0) 📝 `src/hopper/BridgeLauncher.ts` (+21 -3) _...and 23 more files_ </details> ### 📄 Description <!-- CURSOR_AGENT_PR_BODY_BEGIN --> ## Summary Part of #6176. The Hopper and Cutter request payload ceiling was a hard-coded 16 MiB constant in both Python bridges and the TypeScript pre-dispatch check. `REA_BRIDGE_MAX_REQUEST_BYTES` is now the single setting. Unset, the ceiling stays 16777216 bytes. A decimal integer from 7464 to 536870887 is accepted. Anything else is `configuration_invalid` and does not fall back to the default. Current `main` also calls undefined `uniqueBy` while merging residual unknown ids, so typecheck failed before this change. Those ids now use the existing `uniqueSorted` helper, matching the adjacent string lists. ## Problem and expected behavior Three copies of the 16 MiB request budget had to agree: `MAX_REQUEST_BYTES` in `bridge/hopper_bridge.py`, `MAX_FRAME_BYTES` in `bridge/cutter/rea_bridge.py`, and the client check before dispatch. There was no runtime override. A caller can now raise or lower that ceiling with `REA_BRIDGE_MAX_REQUEST_BYTES`. The payload budget still excludes the terminating LF, and an incomplete Hopper frame is still `Incomplete bridge request frame`. An out-of-band bridge without the setting stays at 16777216 bytes. The client compares the bridge advertisement before a discovery status probe and before sending a command. A different ceiling is a mismatch only after the hello proves that descriptor is a live authenticated bridge. A stale descriptor is ignored, and that hello does not send a status request. Discovery fails when any such live ceiling differs, even if another live session matches. A command response discarded because it exceeds the ceiling after dispatch stays an unknown execution and sets `output_truncated`. ## Change and scope `src/config/bridgeMaxRequestBytes.ts` owns the setting, using the same schema in startup `parseConfig` and doctor `parseEnvironment`. The floor, 7464 bytes, is the authenticated Cutter frame of Hopper's largest bounded symbol name (1024 U+0001 characters). The required Cutter recovery status, 64 receipts with an unknown path, is smaller than that floor. A status or command path that fits is kept. A path that does not fit is null, so the client does not reuse the descriptor path with the new generation. The session id, connection proof, and receipts remain. The maximum, 536870887, is one byte under Node's 536870888-character string ceiling so the framing LF still fits. The ceiling is the encoded payload. Escaped JSON is measured against it before `JSON.stringify`, so a U+0001 expansion is counted once and is not divided into the maximum again. A structured Cutter reply is admitted when that encoded payload fits. Numbers, booleans, null, and container punctuation use their JSON lengths, so a dense array is not rejected by a fixed per-value charge. Scalar values in a reply are encoded in bounded batches. A 100 MiB encoded ceiling is accepted. Hopper receives a caller-selected value as a bootstrap integer, which wins over the process environment. Cutter reads the environment that starts Cutter and publishes `max_request_bytes` on its descriptor. A missing advertisement means 16777216. Cutter response frames use the same ceiling and also exclude the terminating LF. The 25000 ms request deadline and the 256-candidate discovery budget do not follow this setting. Neither does the fixed cap of 8 Cutter connection threads. Setup keeps a caller-selected value in `SELECTED_REGISTRATION_SETTINGS`. Oversize and mismatch text names the setting, effective value, source, default, floor, and maximum. Escaped request JSON is measured within the ceiling before `JSON.stringify`. The Cutter hello frame still must arrive within 1 second. The authenticated command frame is read at one second per mebibyte of the ceiling, and that read never waits longer than the 25 second client deadline. The bridge searches only the newly read bytes for the framing LF. Hopper serializes a request when it reaches the wire, so a call waiting in the FIFO does not retain a second encoded frame. Cutter hashes the HMAC prefix and the payload as separate updates on both the client and the bridge, so an exact-limit frame does not build one preimage string. The plugin measures and signs the response from bounded JSON pieces, then writes that frame in 64 KiB chunks. The client unescapes each socket chunk into one response string, checks the proof, and then parses that string. A hello that never sends its authenticated frame does not hold the accept loop. At most 8 connection threads run. A further accept is closed, and a failure to start a thread closes that socket without stopping the listener. `completed_connection` is the longest prefix of accepted connections whose handlers have terminated. A mismatched descriptor counts only after that hello proof. A response dropped for the ceiling sets `output_truncated` on the unknown execution. Follow the [contribution scope](https://github.com/morluto/rea/blob/main/CONTRIBUTING.md#contribution-scope). This configures an existing local bridge admission. It does not add a provider or a target execution, injection, mutation, or protection-recovery capability. not applicable ## Contract and boundary impact - Semantic owner and earliest changed stage: `src/config/bridgeMaxRequestBytes.ts` owns the ceiling. Startup and doctor parse it before launch. The bridges apply it when they load. `CutterBridgeClient.listSessions` owns whether an advertised ceiling is a live session. - Contribution scope fit and linked maintainer decision: ordinary configuration of an existing admission. Maintainer scope decision not applicable. - CLI and MCP/tool-catalog contract: `cutter_command` documents the setting. `list_cutter_sessions` states that the 256-candidate discovery budget is separate and still has no runtime override. Invalid values are `configuration_invalid`. A ceiling mismatch during discovery is `resource_constraint` when the hello proves a live authenticated bridge, including when another session matches. A stale descriptor is not that mismatch. - Provider, bridge, target-format, or platform compatibility: Hopper health gains `max_request_bytes`. Cutter descriptors gain the same optional field. Missing means 16777216, so an older bridge still matches a default client. Cutter responses share the ceiling and exclude the terminating LF. Hopper responses are unchanged. Both Python bridges accept only ASCII decimal digits. - Evidence, artifact, provenance, or reconstruction contract: a Cutter reply that exceeds the ceiling after the command was sent stays an unknown execution (`transport-response-missing`) with `output_truncated` true. Its message names the ceiling and says the command may have completed, Evidence records the omitted output, and receipt cleanup still waits. A frame that overflows before the command is sent is a `resource_constraint`. A response whose payload is exactly the ceiling, followed by LF, is accepted. A tight status or command reports `current_file` as null when the observed path does not fit, and the client does not replace that null with the descriptor path. A structured reply under the encoded ceiling is returned rather than marked `output_truncated`. `completed_connection` advances only through connections that have terminated. - Target selection, authority, upstream behavior, and cleanup details (if applicable): none. The ceiling bounds local bridge bytes. A mismatch or oversize refusal does not send the command to the mismatched peer. The discovery proof is a hello and closes before a status request. A Hopper startup mismatch still runs the existing cleanup path. An oversized Cutter reply after dispatch keeps the existing pending-cleanup owner. - Generated metadata (`docs/public/product-catalog.json`), package, or installation impact (CI generates and validates ignored catalogs and `skills/`; edit authored instructions in `.agents/skills/reverse-engineer-anything/` and never commit build outputs): authored skill text under `.agents/skills/reverse-engineer-anything/references/native-and-artifacts.md` only. No generated catalogs are committed. Expected effects (check known effects; use documentation-only on its own). Unchecked items do not establish absence; leave uncertainties visible. - [ ] Read existing files or captured data. - [ ] Launch or attach to a process. - [ ] Inject instrumentation or run scripts/code. - [ ] Change process memory, registers, files, or configuration. - [ ] Contact network services. - [ ] Recover protected code or work around protections. - [ ] Documentation or usability only; no target interaction. - [ ] Not sure yet; investigation or maintainer guidance needed. ## Evidence and regression coverage - Tests added or updated: owner parse tests, including a 100 MiB encoded ceiling and the string-bound maximum; doctor, setup, and client-configuration rejection and preservation; Hopper bootstrap injection; Hopper health parse; Hopper and Cutter frame probes, including a lowered ceiling, a ceiling above 16 MiB, invalid values, and non-ASCII digits; client mismatch and pre-dispatch refusal; a stale descriptor whose ceiling differs is ignored, including beside a matching live session; an unauthenticated listener is not a mismatch; discovery fails when one live session matches and another live authenticated ceiling does not, and that probe sends no status or command; an exact-ceiling Cutter response, including one whose LF arrives in a later chunk; one byte over that ceiling stays an unknown execution, sets `output_truncated`, names the setting, and Evidence records the omitted output; a U+0001 command whose escaped JSON exceeds the ceiling is refused before `JSON.stringify` on both bridges; the authenticated Cutter read budget is 16 seconds at the default ceiling, 17 seconds at 17 MiB, and 25 seconds at the maximum; a queued Hopper request is not serialized until the active request leaves the wire; the small Cutter HMAC split-update check stays in the default suite, and the maximum-sized preimage is opt-in via `REA_BRIDGE_MAC_LIMIT_TEST=1`; a status with a 4095-byte path and 64 receipts at a 7464-byte ceiling reports `current_file` as null and keeps the new generation; a command reply that does not fit keeps completion and generation and reports an oversized path as null; a short path is kept when a long version is what overflows; 2000 zeroes are admitted under an 8192-byte ceiling and 8000 zeroes are rejected; 40000 zeroes encode as a handful of bounded pieces and match compact JSON; a status does not complete an earlier connection that is still reading; a ninth connection is not accepted while the worker cap is lowered to 1, and the listener still serves after that worker exits; a failed `Thread.start` closes that socket, leaves the descriptor in place, and does not stick the completion watermark; a streamed socket frame matches the joined encoding for quotes, a backslash, a newline, and U+2028; the client decoder accepts the same escapes, a raw U+2028, and a long run of backslashes. - Base reproduction or other evidence: unset probes still serve exactly 16777216 payload bytes plus LF and still reject an incomplete frame with `Incomplete bridge request frame`. Python `tests/fixtures/cutter_bridge_protocol_test.py` passed through `cutterBridgeProducer.test.ts` on `e44d54056` (31 tests). A mixed value encoded by the streamed writer matched the previous encoder byte for byte (34179 bytes) on an earlier commit; that encoder path is unchanged aside from scalar batching, which the new equality test covers. - User-visible CLI/MCP output (if applicable): invalid values fail doctor and startup as `configuration_invalid` and name `REA_BRIDGE_MAX_REQUEST_BYTES`. Oversize and mismatch text names the effective value, source, default, floor, and maximum. A tight status message names the setting and says the path is unknown. A mismatched discovery names the setting and says no status request was sent to the mismatched session. An oversized command reply's Evidence has `output_truncated: true` and the omitted-output limitation. - Remaining proof gaps: no live Hopper or Cutter GUI. Bridge behavior is from the shipped Python modules over socket pairs and the Cutter protocol fixture. Peak memory for a queue of maximum-sized Hopper frames was not measured; the queue no longer retains those frames. A 64 MiB fitting Cutter response was measured in one process per encoder on `ca8dcc6cb`, including construction of the output string. The retained encoder peaked at 345868 KiB RSS (0.781s elapsed, 0.513s user CPU). The streamed write peaked at 84016 KiB (1.841s elapsed, 1.834s user CPU). Both frames were 67109017 bytes and were not truncated. A 512 MiB frame was not measured. Agent tokens do not apply. The default suite does not hash the 512 MiB HMAC preimage. The 7 million zero timing from review was not re-measured; the regression locks piece count and JSON equality. The 16 MiB backslash scan timing was not re-measured; the decoder now stops at the first quote or backslash. For evidence-bearing changes: - [x] Observed, derived, and inferred claims remain distinguishable. - [x] Artifact identity, source provenance, and failed attempts remain preserved. - [x] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. ## Validation performed On the tree committed as `0849b0452` unless noted: - `npm run check:fast` — passed (typecheck and lint). - `npm run test:focused -- tests/boundary/providers/cutterBridgeRequestLimit.test.ts` — 10 passed. - `npm run test:focused -- tests/boundary/providers/CutterBridgeClient.test.ts` — 30 passed on `aa58c8939`. The client source is unchanged in `0849b0452`. Earlier on this branch, before `aa58c8939`: `npm run format:check` passed (5132 files); `python3 tests/fixtures/cutter_bridge_protocol_test.py` passed (31 tests); `npm run test:focused` for the Cutter producer, null-identity, and client files passed 46 tests on `e44d54056`. Those suites were not re-run for this discovery and truncation change. `npm run test:local -- src/cutter/cutterBridgeMac.test.ts src/config/bridgeMaxRequestBytes.test.ts` passed 17 with 1 skipped on `fbd0868fe`. The skipped test is the opt-in 512 MiB HMAC proof. `npm run test:acceptance -- tests/acceptance/setup/registrationRuntimeEnvironmentCli.test.ts` passed 15 with 1 skipped on an earlier registration commit. Real Hopper and Cutter applications were not available in this environment. ## Compatibility, safety, and release - Breaking changes or migration steps: none for an unset ceiling; it stays 16777216 bytes. A configured value outside 7464 to 536870887 bytes is `configuration_invalid`. A missing health or descriptor field means that same default. A status or command path that does not fit is null rather than the descriptor path. A structured reply that fits the encoded ceiling is returned instead of `output_truncated`. Discovery fails when a live authenticated ceiling mismatches. A stale descriptor does not. A command response discarded for the ceiling reports `output_truncated`. `completed_connection` no longer means "the status connection ordinal minus one". - Real Hopper/Ghidra, browser, or OS coverage: not run. Python bridge modules and local socket fixtures were executed. - Package or release metadata impact: none. - Security, privacy, process, or containment review: the ceiling bounds bytes read from the local bridge. Invalid configuration is refused. The Hopper bootstrap injects only the already-parsed integer. Oversized escaped JSON is counted before allocation. A waiting Hopper request does not retain an encoded frame. Cutter proof bytes are unchanged; the prefix and payload are hashed as separate updates on both sides. A fitting Cutter response is measured and signed from bounded pieces and written in 64 KiB chunks. The client keeps one decoded response string rather than the paged wire frame plus a concatenated copy and an outer JSON string. At most 8 unauthenticated connection threads are admitted. A ceiling mismatch is accepted only from a peer that proves the descriptor token. No new target authority. ## Author readiness Human owner: @morluto - [x] I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work. - [x] I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps. - [x] I declare this PR ready for initial maintainer review. ## Before merge - [x] This PR addresses a concrete problem or an agreed enhancement. - [x] Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff. - [x] The PR has one focused outcome and the title follows `type(scope): outcome`. - [x] Related issue is linked, or the reason for not linking one is stated above. - [x] Tests cover changed observable behavior and meaningful failure paths. - [x] Owning docs, contracts, and generated metadata are updated where needed. - [x] User-visible CLI/MCP changes include representative output. - [x] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. <!-- CURSOR_AGENT_PR_BODY_END --> <div><a href="https://cursor.com/agents/bc-659bd62b-4d7b-575b-87d2-bc8717558c69?cursor_ref=pr_footer&cursor_cta=open_in_web"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-web-light.png"><img alt="Open in Web" width="114" height="28" src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a>&nbsp;<a href="https://cursor.com/background-agent?bcId=bc-659bd62b-4d7b-575b-87d2-bc8717558c69&cursor_ref=pr_footer&cursor_cta=open_in_cursor"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img alt="Open in Cursor" width="131" height="28" src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a>&nbsp;</div> --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-11-01 21:47:02 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#7321
No description provided.