# Remozio plan review: revision 13

**Scope:** complete plan, revision 13, plus the round 6 disposition. This is a plan-only review; nothing was tested, browsed, or inspected in source.

The round 6 corrections are all present in revision 13:
- protected placement
- the restart matrix
- mixed-phone contract selection
- last-phone revocation

The earlier invariants hold against the new sections, with the exceptions below:
- scope and biometric classification
- key separation
- relay non-authority
- memory-only payloads
- the no-replay rule for Unknown

## Findings

### 1. Medium: an automatic update drain can halt remote work indefinitely, and nothing remote can end it (§17 "Activate a compatible component set"; §17 "Sparkle release delivery")

**Scenario:**
1. Automatic updates are enabled. Sparkle stages a new root authority while the user is away.
2. A long-lived `remozio sudo` command is running, such as a watcher or privileged dev server.
3. The drain starts and stops admitting new requests. Every new command gets "Update waiting".
4. Every new Little Snitch or 1Password prompt is also refused, and no phone notification is sent.
5. §17 explicitly allows the long command to defer activation indefinitely. Admission therefore stays frozen indefinitely too.

The plan does offer "defer or cancel-update-drain", but it has three gaps:
- It shows blockers in the phone status but never says whether the phone can defer or cancel.
- §6 lists no signed purpose for that control.
- The status channel "cannot authorize actions", so it cannot carry this control either.

A remote user therefore has no defined way to recover. This silently reduces remote functionality, which §1 forbids without consultation.

**Minimal correction:**
- Stage the update without draining. Start the root drain only in one of two cases:
  - the authority is already quiescent (no pending requests, active commands, or live permits);
  - the user explicitly starts it.
- Otherwise show "Update ready" and keep admitting requests.
- If phone-side defer or cancel is wanted, define it as a narrow signed purpose that is non-authorizing for actions. It could use the decision key, since it grants no target authority. Verify it at the root.

**Needs user decision:** whether a security-fix update may start a drain automatically, possibly after a bound. That trades update latency against a remote admission freeze. It is a policy choice, not something to settle silently.

### 2. Low–Medium: old but validly signed builds can still act as privileged peers (§17 "What each signature proves"; §3 "Authenticate local process boundaries"; §18 "Compatible additions and breaking changes")

**Scenario:**
1. A malicious same-user process launches an older, publicly released Remozio GUI adapter or app build. That build is correctly signed with the same Team ID and signing identifier, and has a fixed vulnerability.
2. It passes the designated-requirement and live XPC checks.
3. It shares the keychain access group, so it can use adapter receiving keys and request permits or credential packages.

The same-user attacker is in scope. §17 enforces minimum builds only "where required", and §18's persisted security generations are never tied to peer admission.

**Minimal correction:**
- On every privileged XPC request, the root authority checks the peer's signed build or security generation against the persisted authenticated minimum. This applies to every role that receives permits, credentials, or adapter-key certification.
- The adapter-key certification in §3 records that generation. Raising the floor invalidates earlier certifications.

This does not worsen UX.

### 3. Low: GUI adapter replacement during an update has no quiesce rule (§17 "Activate a compatible component set", restart matrix; §9 "Scope mutation is part of one attempt")

The drain is defined only for root-authority replacement. A normal Sparkle app-bundle install terminates and relaunches the app.

**Scenario:**
- If the adapter is hosted in the app or relaunched with it, an install during a consumed Little Snitch permit turns a routine allow-once into Unknown.
- The dialog default may have been mutated by then (§9).
- A pending 1Password prompt with a ~60 s lifetime may be lost to the restart.

The matrix handles the aftermath but does nothing to avoid causing it.

**Minimal correction:** defer adapter termination until no live permit exists, with a short bounded wait before relaunch. Record interruptions caused by the update in the audit log.

This does not worsen UX.

## Explicitly gated or deferred, not findings

These have defined evidence and a failure or consultation path:
- the Cloudflare Access credential provisioning source
- renewed service-management authorization on update
- protected root placement
- the local command approval surface
- one-biometric credential release
- 1Password and Little Snitch fixtures
- descriptor-based execution and sudoers compatibility
- the idle threshold and retention duration

## Remaining implementation limits

None of the following can be established by plan review:
- AX click atomicity
- Android key-invalidation behavior
- launch-time signature enforcement on macOS 14
- push delivery under Doze

**SUBSTANTIVE FINDINGS REMAIN**
