# Code Review: Remozio Revision 30 — Section 19 ADB Deployment Bridge

**Scope:** `REVIEW-BUNDLE.md` only (exact source lines preserved). Omitted plan text is treated as unknown, not as a defect. No files were modified. Per line 3, section 19 has had no independent review; this report supplies one.

**Verdict:** Section 19 is unusually honest about platform limits and correctly keeps the bridge outside the approval authority. It contains **one high-severity design defect** (unauthenticated local ADB listener), several medium consistency/placement gaps against sections 3, 16, 17, and 18, and a set of genuinely open feasibility questions that the plan already gates correctly.

---

## Design defects

### D1 — HIGH: Local ADB listener has no client authentication (lines 1678, 1690, 1696)

Line 1678 authenticates only the *control plane*: "Authenticate **the command** to the background bridge with a local peer check and restrict it to the configured administrator account." The *data plane* — "binds a temporary listener on `127.0.0.1` for the local ADB client" — is an ordinary loopback TCP listener. On macOS, **any local process may connect** to a loopback port; a TCP listener cannot restrict callers to the administrator account. The "one active connection" bound at line 1678 is a slot, not an authorization check.

Rationale: the stated threat model (line 61) explicitly includes "a malicious unprivileged requesting process." That process can enumerate loopback ports, win the race for the single connection, and complete ADB authentication, because line 1690 keeps the pairing key "in ADB's existing host store" — `~/.android/adbkey`, readable by any process running as that key's owner (typically the same interactive/admin user the coding agent runs as). Line 1682's "Forward ADB bytes without terminating or replacing ADB's own TLS" does not help: ADB host-key authentication is satisfiable by the same-user attacker who can read the key. Net effect: while **Ready for remote deployment** is active, the phone's debugging surface is extended to the entire Mac user session, contradicting the administrator restriction at line 1678 and the boundary at line 1670 ("Do not let a Remozio approval, decision key, push token, or transport credential grant ADB access" — the bridge's transport credential is effectively doing exactly that for uninvited local processes). Even without key access, occupying the single connection is a denial of service against deployment.

Minimal amendment: the temporary listener must authenticate its single client with the same rigor as the command — e.g., hand off the listening socket (or a one-time bound token) only to the authorized command's process via local authenticated IPC, and reject connections whose peer credentials are not the configured administrator account. Additionally specify which account runs the `adb` client and owns the paired key, and state that the key must not be readable by processes other than that account (see D7).

### D2 — MEDIUM: Self-deployment of the companion kills the bridge session and Ready mode (lines 1672, 1686, 1696)

The feature's own primary use case — "deploying a build from the enrolled Mac to the enrolled Android phone" (line 1668) — includes deploying a new build of the Remozio companion itself ("normal app updates can use the bridge," line 1672). When `pm install` commits the replacement, Android kills the app process, terminating the foreground service (line 1686) and the WebSocket. Post-replacement, background-start restrictions prevent the app from silently restoring a foreground service, so **Ready mode is lost after every self-deploy**, and the next deploy needs a phone tap. Per line 25, adding manual steps is a consultation-triggering change, and per line 27 such a recurring UX cost must be recorded as a pending decision; the bundle shows no such record for this loop, and the acceptance list at line 1696 does not test self-update ("restart and changing ADB ports recover without re-pairing" does not cover package replacement of the host app).

Minimal amendment: add a self-update acceptance case to line 1696, and explicitly document that self-deployment ends Ready mode with a defined recovery step (or record a pending decision on a persistent installer stub — a design change requiring consultation).

### D3 — MEDIUM: Mac bridge endpoint missing from release manifest, protected placement, and update path (lines 102, 1426–1436, 1670, 1676)

Line 102 introduces "a separate Mac-side transport endpoint and command" as a first-class component, and line 1676 makes it a network-exposed unprivileged service. But the release manifest at line 1434 lists only "app, helper, GUI adapter, transport, and administrative setup-controller builds," and line 1436's "authenticate each running component" likewise omits it. No section specifies: which service account runs the endpoint (sharing the dedicated transport account of line 1141 would co-mingle bridge authority with approval transport, undermining the separation asserted at line 1670); where its binaries live; custody of the phone-bridge-identity verification keys (line 1690 covers only the phone side); or how it updates. Line 1428's invariant — never launch privileged/exposed code from a location replaceable by an unprivileged process — should clearly apply but is never applied to it. A user-replaceable bridge binary can redirect sessions, target a different phone, or subvert line 1678's "Do not accept arbitrary hostnames, ports, or destinations."

Minimal amendment: add the bridge endpoint to the line 1434 manifest and the line 1436 authentication check; assign it a service identity distinct from the approval transport account; apply the section 3/17 placement and update rules; specify local custody of the bridge identity keys.

### D4 — LOW/MEDIUM: Section 18 compatibility manifest omits the bridge (lines 1552–1569)

Line 1552 requires every component to declare "what it can send, parse, and execute," and the table at lines 1558–1566 enumerates Android app, Mac authority, GUI adapter, command frontend, Mac transport, relay/push gateway — but not the Android bridge client or Mac bridge endpoint, even though the bridge introduces a new wire protocol ("the same framed byte stream," line 1676, plus framing/backpressure rules at line 1682). A companion update that changes bridge framing has no negotiation or compatibility story, and line 1567's "do not claim an untested continuous range" cannot apply to a protocol that is never declared.

Minimal amendment: add bridge rows to the table (envelope/framing versions, capability flags) and treat bridge protocol evolution under the authenticated negotiation rules at lines 1571–1589.

### D5 — LOW: Section 16 wording conflicts with exposing the bridge endpoint (lines 1157, 1676)

Line 1157: "Expose only the Remozio protocol endpoint, never the privileged helper, a general shell, or other local services." Line 1676 routes the bridge "through the section 16 internet transport candidate, Cloudflare Tunnel plus Access." As written, these contradict: the bridge is a second, distinct endpoint. The intent (only enumerated, authenticated Remozio endpoints) is recoverable but should be stated, ideally with the bridge on its own Access application so its service-credential revocation (line 1690) is independent.

Minimal amendment: reword line 1157 to "only the enumerated Remozio endpoints (approval and the deployment bridge), each with its own Access application."

### D6 — LOW/MEDIUM: Approval-latency separation is asserted but no mechanism survives the shared path (lines 1670, 1696)

Line 1670 requires separation of transport/rate limits "so a large APK cannot delay a short-lived approval," and line 1696 makes "bridge load does not delay an approval" an acceptance criterion. But both flows traverse the same cloudflared tunnel (line 1141), the same WAN uplink, and the same phone radio; application-level queueing cannot prevent link-level contention. As specified, the design has no mechanism (no prioritization, no pause-on-pending-request) by which the acceptance criterion could pass under a saturated uplink.

Minimal amendment: define a precedence policy — e.g., bridge transfers throttle or pause while any approval request is pending or within its response window — so the acceptance test is achievable by construction rather than by luck.

### D7 — LOW: adb-key account mismatch and recurring-admin UX cost are unresolved (lines 1678, 1690)

Line 1678 restricts the command to "the configured administrator account," while line 1690 leaves the pairing key in ADB's per-user host store. If the interactive developer is not that administrator account, either `adb` runs with an unpaired key (fails authorization) or the key must be copied across accounts (undermining custody). If the developer *is* the admin, the restriction is cheap; either way the plan should say so. If deploys require an admin context on a standard-user Mac, that is a recurring manual step requiring consultation per line 25.

Minimal amendment: one sentence in section 19 naming the adb client's account and requiring the paired key to remain unreadable to other local accounts; record a pending decision if this forces per-deploy elevation.

### D8 — LOW: Foreground-service type has no fallback (line 1686)

"Use the Android `connectedDevice` foreground-service type only if its platform and distribution requirements are met" — the `connectedDevice` type requires holding qualifying permissions (e.g., Bluetooth/UWB/network-state) that a WebSocket-to-Mac bridge may not legitimately hold. The conditional has no else-branch: if unmet, is the bridge `specialUse`, or does Ready mode simply not exist? The design's core (persistent outbound connection owned by a foreground service, line 1686) depends on this answer.

Minimal amendment: specify the fallback type (with declaration/justification) or state that the feature is unavailable when `connectedDevice` requirements cannot be met, and fold that into the line 1694 gate.

### D9 — LOW: "Not trusted for debugging" reporting presumes knowledge the app may not have (line 1688)

Line 1688 requires reporting Unavailable when "the current Wi-Fi network is not trusted for debugging," but an ordinary app has no public API to query ADB's trusted-network state; it can only infer from discovery/connection failure. As written the requirement may be unsatisfiable.

Minimal amendment: restate as "trust state is inferred from discovery/connect outcomes and reported as Unavailable," and verify inference fidelity in the line 1694 gate.

---

## Platform feasibility questions (not defects — already gated)

- **F1:** Can an ordinary app discover its own `_adb-tls-connect._tcp` service via NSD, and what does Android 17's local-network permission require (lines 1680, 1694)? The plan correctly treats the Pixel 11 Pro loopback observation as reachability only.
- **F2:** Cloudflare Access service-token authentication on WebSocket upgrade, with rotation/revocation (lines 1171–1177, 1694). Consistent with section 16; open as stated.
- **F3:** ADB Wi-Fi 2.0 trusted-network re-enable persistence across reboots/network changes (line 1688) — correctly marked "test the exact behavior."
- **F4 — add to the gate:** Cloudflare's proxy imposes idle timeouts on long-lived WebSockets; an idle ADB session (no keepalive traffic) may be dropped by the edge regardless of device-side socket survival. Line 1694's "maintain a foreground WebSocket in idle" should explicitly include edge idle-timeout behavior and keepalive design.
- **F5:** `adb install` policy for app updates on target SDK/Android 17 (line 1672) — correctly conditional.

## Checks that pass

Approval authority separation (lines 1668, 1670 vs. 73–83); no plaintext `adb tcpip` mode (line 1682); byte caps/framing/backpressure (line 1682); independent revocation and session teardown (line 1690, consistent with line 1177); no Cloudflare credentials on the phone (lines 1676 vs. 1167); honest unavailability reporting (line 1688); Tailscale as a development-only fallback does not conflict with the product-scope "no VPN required" at line 87; the per-session-confirmation question is properly flagged as a UX policy decision (line 1686), consistent with lines 25–27.

**Top actions:** fix D1 before any implementation; resolve D2–D3 during planning since they change manifests and acceptance criteria; D5–D9 are one-line amendments.
