gpu display: sync the clipboard between the guest and the viewer - #3
gpu display: sync the clipboard between the guest and the viewer#3ya-luotao wants to merge 2 commits into
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6929e80d42
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let backend: std::sync::Arc<dyn msb_krun::backends::vsock::VsockPortBackend> = | ||
| server.clipboard_backend(); | ||
| builder = builder.vsock(move |vsock| { | ||
| vsock.custom(crate::gpu_display::protocol::CLIPBOARD_VSOCK_PORT, backend) |
There was a problem hiding this comment.
Reject collisions with the clipboard vsock port
When a GPU-enabled sandbox already configures a stream route on host port 5910, this adds a second route for the same port. The vendored VmBuilder rejects duplicate stream ports during build(), so an otherwise valid existing sandbox configuration now fails to start whenever GPU display is enabled. Reserve and validate this port before constructing the builder, or choose a route that cannot collide with user configuration.
AGENTS.md reference: AGENTS.md:L124-L124
Useful? React with 👍 / 👎.
| self.sender.send(&ViewerMsg::Clipboard { | ||
| mime: TEXT_MIME.to_string(), | ||
| data: base64::engine::general_purpose::STANDARD.encode(&text), | ||
| }); | ||
| self.last_host_text = Some(text); |
There was a problem hiding this comment.
Replay host clipboard data after the guest connects
If the viewer polls the Mac pasteboard before the guest clipboard agent connects—a normal startup race for msb run --display—the server drops this message in send_to_guest, but the viewer still records the text as sent here. Later focus and key events suppress that same value, so the guest never receives the initial host clipboard unless the user changes it again. Retain and replay the latest host selection when the agent connects, or only mark it delivered after acknowledgement.
Useful? React with 👍 / 👎.
| }; | ||
| if text.is_empty() | ||
| || self.last_host_text.as_deref() == Some(text.as_str()) | ||
| || self.last_guest_text.as_deref() == Some(text.as_str()) |
There was a problem hiding this comment.
Clear stale guest-origin clipboard suppression
After the guest sends value A and the host subsequently sends value B, last_guest_text remains A because the guest agent intentionally does not echo B. If the user later copies A on the Mac, this condition suppresses it even though the guest currently holds B, preventing a legitimate clipboard update. Track the origin of the current pasteboard value or clear the guest marker once the host selection changes.
Useful? React with 👍 / 👎.
| fn shutdown(&self, _how: VsockShutdown) -> io::Result<()> { | ||
| self.retire(); |
There was a problem hiding this comment.
Preserve the unclosed half of a vsock stream
When the guest requests only SHUT_RD or SHUT_WR, this ignores the direction and retires the entire current connection. The opposite half of the stream is still valid under the VsockStreamBackend contract, but subsequent reads report EOF and writes are discarded, so clipboard updates stop prematurely for agents that half-close. Track read/write closure separately and retire only after both directions close or the stream is dropped.
AGENTS.md reference: AGENTS.md:L124-L124
Useful? React with 👍 / 👎.
Text copied in the guest's Wayland session reaches the macOS pasteboard and back, for as long as `msb display` is open. The display server owns an in-process vsock backend on port 5910 and registers it whenever it starts, independent of the user's `--vsock` routes: `VmBuilder::vsock()` threads the same builder, so the extra `.vsock()` call adds a route rather than replacing the existing ones. The guest agent speaks newline-delimited JSON both ways, carrying the selection as base64 with a `mime` field so images can be added later without a new message; the same value reaches the viewer as `ServerMsg::Clipboard` / `ViewerMsg::Clipboard`. Unknown JSON lines were already skipped by both ends, so this stays wire-compatible. The backend serves one guest at a time, like viewers: a new connection retires the old stream, which then reports EOF rather than an empty read (`Ok(0)` on a live stream would close it). Queues are capped at 16 MiB in both directions, the last guest selection is replayed to a viewer that attaches later, and nothing here touches the gpu worker thread, so the `present_frame` contract is unchanged. Loop prevention lives at the edges: the viewer remembers the last text it sent and received, and reads the pasteboard on focus and on key presses. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HjNQH1XtX3TCGAKMoBNEw8
The pasteboard is pushed into the sandbox whenever the window takes focus or a key is pressed, and the guest can replace what the Mac holds, so a viewer on an untrusted image needs a switch. Anything other than unset or `1` disables both directions: the pasteboard is never opened, so nothing is read from it, and a guest selection is dropped before it is decoded. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HjNQH1XtX3TCGAKMoBNEw8
6929e80 to
16c6800
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 16c680032d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| /// Clipboard text is kept in sync with the Mac pasteboard while the window is | ||
| /// open. Set `MSB_DISPLAY_CLIPBOARD=0` to turn that off in both directions. |
There was a problem hiding this comment.
Document the default-on clipboard trust boundary
Clipboard synchronization is enabled by default and allows an untrusted guest to replace the host pasteboard, but the new behavior and MSB_DISPLAY_CLIPBOARD=0 opt-out appear only in source-level CLI documentation; the published docs/ contain no mention of msb display clipboard behavior. Users following the documentation therefore cannot discover this security-relevant default or how to disable it, so add it to the relevant CLI/display documentation.
AGENTS.md reference: AGENTS.md:L321-L324
Useful? React with 👍 / 👎.
Stacks on #1 (base
display-frame-path; retarget togpu-m0once #1 merges). Guest side: ya-luotao/msb-omarchyclipboard-agent.Text copied in the guest's Wayland session reaches the macOS pasteboard and back for as long as
msb displayis open.gpu_display/clipboard.rs) on port 5910 and registers it whenever it starts, independent of the user's--vsockroutes (VmBuilder::vsock()adds routes rather than replacing them; the vsock device is present without--vsockbecause TSI rides on it).{"t":"set","mime":"text/plain;charset=utf-8","data":"<base64>"}both ways;mimeleaves room for images. The same value reaches the viewer asServerMsg::Clipboard/ViewerMsg::Clipboard(additive, wire-compatible).arboard; reads the pasteboard onFocused(true)and on key presses, remembers what it last sent/received so nothing bounces.MSB_DISPLAY_CLIPBOARD=0turns the sync off (the host pasteboard is otherwise pushed into the sandbox on every focus/key press, and a guest can overwrite the Mac pasteboard).Verified end to end on the
omarchysandbox:printf final-check | wl-copyin the guest →pbpasteon the Mac prints it;ViewerMsg::Clipboardforhello-from-mac→wl-pastein the guest prints it; a viewer that attaches late receives the remembered guest selection. The pasteboard→viewer hop (arboard::get_texton focus) could not be driven from a script (no Automation permission) and fires on the first click into the window.🤖 Generated with Claude Code
https://claude.ai/code/session_01PN7mepn7ryjXupoHbQjFmR