Repository navigation
Settings > Devices: a per-device Full access switch for acpmux-paired client machines - #25069
Conversation
`device list --json` gives each acpmux client pairing its pair_list Full-access setting (absent reads as off), so Settings > Devices can show it. Other rows have no such setting and omit the field. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16c8c-54ec-71b2-a0fa-125d1404dd72&host=big-red
Each acpmux-paired client machine's row carries its Full-access setting and a sentence
saying what it allows. `cmux.settings.devices.full_access {id, on}` sets it through
acpmux's `_acpmux/pair_set` on a connection proven as the person (turning it on is refused
without the proof), then reads the list again. Other rows have no setting.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16c8c-54ec-71b2-a0fa-125d1404dd72&host=big-red
… rows A row whose host data carries `full_access` gets a switch titled "Full access" with the host's sentence on what it allows; toggling it calls `cmux.settings.devices.full_access` and reads the host lists again. The mock provider gains a paired client row for it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16c8c-54ec-71b2-a0fa-125d1404dd72&host=big-red
Settings draws no description line over 60 characters in any language (rowSummary.test.tsx). The device's name is already the row's title, so the help line says only what Full access allows; the refusal line is shorter too. Every locale fits. The mock provider's handler reads `on` as a boolean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16c8c-54ec-71b2-a0fa-125d1404dd72&host=big-red
…as FeedControl does; the switch shows only for a boolean Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16c8c-54ec-71b2-a0fa-125d1404dd72&host=big-red
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
Gallery before/after for
States
Pick other states with a Not verified: macOS jobs did not run: compile and app tests were skipped on CI passes on Written by web and size checks: green Tour of The path-selected recorder failed for core-after, core-before; see the run artifact and failed manifests. Path-selected interactions
core-before: the media could not be published (refused 1 file(s) that show private names: 25069/d4d91be3/next-core-before/shots/07-new-terminal-tab.png (fleet shell prompt). Nothing was uploaded. Show fixture names in the scene instead (the sample)Core UI (after): FAILED — 16 ok, 3 failed, 1 unavailable
Failed steps: 5. Split right Screenshots (12)1. Launch Tours are data in |
|
Owned by hb-acpmux on big-red · session |
|
Found 12 test failures on Blacksmith runners: Failures
...and 2 more test failures. View all on Blacksmith
|
|
Bead |
…sure, not an async default literal (crash ratchet) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16c8c-54ec-71b2-a0fa-125d1404dd72&host=big-red
…person's own click in Settings, spent once Codex review: page script in the Settings WebView could call cmux.settings.devices.full_access with no gesture, and the native closure proves the app as the acpmux person. On now needs context.userGesture in the Settings page with a gesture event not already spent; off needs none. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16c8c-54ec-71b2-a0fa-125d1404dd72&host=big-red
…emote-full-access-device-ui
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
|
Shipped in:
|
















![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Follows #25038 (merged): acpmux's per-device Full-access setting (
pairing.jsonfullAccess,_acpmux/pair_set,full_accessinpair_list).The ask, word for word:
Leo, relayed: "#2 seems reasonable i think... it's a gate that you can enable for your phone, which is an attack vector you opt in to".
The brief also said: "Find the Mac UI that lists paired devices ... and add the per-device toggle with that explanation there. If the UI is a separate surface, make it a second small PR after the acpmux one." This is that second PR.
Which devices get the switch
Only acpmux-paired client machines (rows with id
paired:<acpmux id>) get the switch. Phones don't reach acpmux with a per-device credential yet: a phone paired with this Mac talks to the Mac app, not to acpmux as its own pairing. acpmux has no per-device record for a phone that could carry the setting, so a phone row has no switch. Peers, logins, read-only and daemon pairings don't get one either. The switch is off by default, because a pairing withoutfullAccessreads as off.What changed
cmux-tui
device list --json: each acpmuxclientpairing carriesfull_access(frompair_list; a missing value reads asfalse). Other rows omit the field.Swift (CmuxNextServer):
DeviceListing.Device.fullAccessdecodes it.DeviceListRowand the page JSON carryfull_accessplusfull_access_help: "Its chats edit files and run commands here without asking".rowSummary.test.tsx), and that sentence is about 104. The help line is the device's own row under its name, so the shortened line drops the name and says what Full access allows. Every locale fits.DevicesHostState.full_access_titleis "Full access".DeviceDirectory.setFullAccess(id:on:)stripspaired:, calls the injectedpair_set, records an error, and reads the list again. A row without the setting never reaches acpmux.Swift (CmuxNextApp):
pair_setopens anAcpmuxPersonLineto this app's acpmux, the same proven-as-the-person connection the approval banner uses._acpmux/pair_set {id, fullAccess}.cmux.settings.devices.full_access {id, on}on the Settings provider reaches it.context.userGesture, from the host's record, never the page's word), and that gesture is spent once. So page script in the Settings WebView alone never turns a machine's Full access on (Codex review). Off needs no gesture: it only narrows what the machine may do, so a revoke always works, assistive technology included.permission.person_required).Errors: the person sees one of two messages:
AcpmuxPersonLine.calldrops the error object, sofull_access_roleandnot_foundshow these same messages.Revoking: turning the switch off takes effect at once. acpmux's
pair_setoff holds that machine's running chats' policies again (held_again, acpmux: a paired machine's Full-access setting (off by default) lets it drive a remote chat in Full access #25038), and the list is read again.Webview (Settings page):
Switch. The switch is disabled while its call runs.Strings: four new
AddDevice.xcstringskeys, translated for all 21 locales. The localization ratchet's counts for the catalog are unchanged. This is most of the line count: 524 of the 834 added lines are catalog JSON (21 locales), and the code and tests are about 310 lines.Tests
devices::tests::client_and_read_only_pairings_are_devices_that_use_or_watch_this_onechecks:AddDeviceFlowTests.fullAccessIsAPairedClientsSwitchSetThroughAcpmuxchecks:full_accessandfull_access_title;paired:;SettingsDeviceFullAccessGestureTestschecks:origin: "user", and from another page with a gesture;hostSections.test.tsx"only a paired client machine has a Full access switch..." checks:{id, on: true}, and off sends{id, on: false}, updating the switch each time.Fleet runs (
cmux-ci test) at the head:cargo fmt -p cmux-tui --checkpasses, andcargo test -p cmux-tui --lib devicespasses 16 of 16.bun test src/pages/settingspasses 310 and fails 7. The 7 failures are inserverCard,claimPaintandcategories, and the same 7 fail with this PR's webview diff reversed, so they come from base.vp fmt --check,vp lintand typecheck all pass.Stop conditions
This adds no timers, observers or loops. Each
AcpmuxPersonLinelives for one toggle: it is closed bydefer, and its own deadline timer (5 s) ends it in any case.No capture: this runs on CI only. Swift is not built on agent hosts, so no screenshot of the real Settings window was taken. The capture plan, once a CI build exists: Settings > Devices with a paired client machine, showing the switch off, then on with its help line.
Agent session: big-red
🤖 Generated with Claude Code