Repository navigation
gpu-browser (2/2): npx @agent-socket/gpu-browser installs it under its own OS account and shares it - #2
gpu-browser (2/2): npx @agent-socket/gpu-browser installs it under its own OS account and shares it#2repalash wants to merge 35 commits into
Conversation
…account and shares it The CLI (bin/, runs as you) and the root installer (install/) for the server from the previous commit. First run: explains what it will do, asks once, one sudo prompt. The installer creates the agent-socket-gpu-browser account (no login, no password, own group; on macOS via dscl the way MacPorts makes its user, with the implicit login attributes removed; on Linux a system user, in render/video for the GPU), fetches Node.js (pinned by SHA-256), playwright-core (no install scripts) and Chromium as that account, makes a root-owned copy, self-tests it by starting Chromium exactly the way every run starts it (adding an AppArmor userns profile on Ubuntu 23.10+), and only then swaps it in and writes one sudo rule: the installing user may start that server as that account without a password, nothing else. The rule's file is zz-agent-socket-gpu-browser because sudo applies the last matching rule; if a broader rule still wins, the CLI asks for the password instead of failing. Running: prints the link (60 min by default, --for), relays approvals to the terminal (TTY only; `always` remembers the host in ~/.agent-socket/gpu-browser/allowed-hosts.txt), re-validates everything the server says, and ends the link on Ctrl-C. status, stop, install, uninstall (a root-owned uninstall.sh stays in /opt for when the package is gone). Every folder it creates has a README. Tested on Linux for real (a non-root user, real account, downloads, sudo rule, sandboxed Chromium, approval, update, uninstall leaving nothing). CI does the same on fresh Ubuntu and macOS runners, path-filtered, plus shellcheck and dash. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CpTxf6FhMdiFuXFsbUam53
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: 1e7820ccdd
ℹ️ 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".
…d on Linux) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CpTxf6FhMdiFuXFsbUam53
…ppArmor glob - install.sh: a failed or interrupted first install removes what it created (account, group, AppArmor profile, /opt dir); a failed update keeps the working install. An existing group of the account's name is never reused; uninstall removes only the account's own group. - AppArmor profile attaches to $DIR/**/chrome; when the self-test still fails, both runs' output is shown. - CLI: "minutes" in config.json must be a number >= 0; the note of the home folder's old mode is kept unless it was restored or you declined. - install-smoke.sh shows the CLI's output when an agent call fails; CI prints AppArmor kernel messages on a Linux failure. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CpTxf6FhMdiFuXFsbUam53
…sandbox on/off/profiled Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CpTxf6FhMdiFuXFsbUam53
Linux: the AppArmor profile over a dot directory and a system account started through sudo. macOS: whether closing a context ends Chromium for the runner user too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CpTxf6FhMdiFuXFsbUam53
…ly and through Playwright Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CpTxf6FhMdiFuXFsbUam53
…four ways Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CpTxf6FhMdiFuXFsbUam53
…ce the crash Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CpTxf6FhMdiFuXFsbUam53
…nchctl asuser) In the launchd domain of an account that never logs in, Chromium crashed (SIGSEGV) shortly after a browser context closed. Started with plain sudo -u it doesn't, and it still renders on the GPU (CI: "ANGLE Metal Renderer: Apple Paravirtual device"). The start command, the sudo rule and the self-test are now the same on macOS and Linux. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CpTxf6FhMdiFuXFsbUam53
…stics Both install jobs pass now; the diagnostics steps were for finding the two causes (launchctl asuser on macOS, an inherited XDG_CONFIG_HOME on Linux) and are gone again, as is the no-cancel concurrency they needed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CpTxf6FhMdiFuXFsbUam53
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7475095d6d
ℹ️ 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".
…pt dir we didn't make - A failure or signal in the middle of the swap moves what is in .old back, so the previous install keeps working (tested by failing an update right after its first move). - /opt/agent-socket-gpu-browser that has neither install.env nor the new .created-by-agent-socket-gpu-browser marker is refused by install and left alone by uninstall. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CpTxf6FhMdiFuXFsbUam53
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f386d2a14d
ℹ️ 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".
…or profile it added - A machine-wide lock (/var/run/agent-socket-gpu-browser-install.lock, mkdir + pid) keeps two installs from sharing .staging/.new/.old; one left by a killed run is taken over. Tested: a second install started during the first is refused, the first completes, the lock is gone. - The cleanup removes an AppArmor profile created by this run before the first-install-only part, so a failed update doesn't leave one behind. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CpTxf6FhMdiFuXFsbUam53
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 519dbdbb9a
ℹ️ 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".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2273975559
ℹ️ 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".
…keep special home mode bits - /etc/sudoers.d/zz-agent-socket-gpu-browser or /etc/apparmor.d/agent-socket-gpu-browser without the installer's marker stops the install before anything is created (tested with a sudoers file of that name: refused, file unchanged). - The home folder's old mode is recorded with its setgid/sticky bits (e.g. 2750), so uninstall can restore it exactly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CpTxf6FhMdiFuXFsbUam53
…t by a hard kill - The lock is created as a symlink whose target is the installer's pid (atomic, never ownerless), so a second installer can't mistake a just- created lock for a stale one; only the owner removes it. - A .old left by an update killed mid-swap (SIGKILL, power loss) is put back at the start of the next run instead of being deleted. Tested: a live lock refuses; a stale one (dead pid) is taken over; an install with node moved into .old is recovered and updates cleanly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CpTxf6FhMdiFuXFsbUam53
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4b4b9a930f
ℹ️ 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".
…nning server - The install directory and its marker come first, and the marker journals the group, account and AppArmor profile before each is made, so a first install killed hard (SIGKILL, power loss) is finished or undone by the next run, including a group left between groupadd and useradd (tested: reused, then removed when that run failed). The journal is emptied once an install finishes. - The swap is refused while a server runs (its pid file), checked while holding the install lock; the server refuses to start while the lock is held (PR #1). Tested both ways on an install. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CpTxf6FhMdiFuXFsbUam53
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fea5fd983a
ℹ️ 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".
…t can prompt; safe uninstall - An update keeps .old (and writes install.env aside) until its last step, so a failure after the swap still restores the working copy with its own install.env (tested by failing an update just before that step). - Without the sudo rule, the start no longer passes -n, so sudo can ask for the password itself when the earlier sudo -v isn't cached. - uninstall runs this package's own uninstall.sh (not whatever is in /opt/agent-socket-gpu-browser), and says there is nothing to remove when that directory has neither install.env nor the installer's marker. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CpTxf6FhMdiFuXFsbUam53
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1ed14fd454
ℹ️ 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".
…m runs; re-check a lock takeover - A recorded pid (the install lock's, the server's pid file) blocks only while ps shows that same program (install.sh; server.mjs under the account's uid), so a reused pid no longer refuses installs or updates. - After taking over a stale install lock, the installer waits a second and checks the lock still points at it, so two runs recovering the same stale lock can't both proceed. Tested on an install: pid files pointing at unrelated live processes no longer block an update, a start or an install; a real server still blocks an update. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CpTxf6FhMdiFuXFsbUam53
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Integration harness: Cause. The local Cloudflare simulator's rate limiter ( Proposed fix (harness only; I haven't pushed it here, so this PR doesn't widen): start the burst at the beginning of a window. --- a/harness/scenarios/90-ws-rate-limit.mjs
+++ b/harness/scenarios/90-ws-rate-limit.mjs
@@
const a = new Assert("90-ws-rate-limit")
+ // The local limiter counts in fixed windows aligned to the wall clock
+ // (floor(now / 10 s)); a burst straddling a boundary splits across two
+ // windows and may never reach the limit. Start it at a window's start.
+ await new Promise((r) => setTimeout(r, 10_000 - (Date.now() % 10_000) + 50))
const results = await Promise.all(Array.from({ length: 150 }, upgrade))I couldn't re-run the failed job: the API refused with 403 "Resource not accessible by integration". A manual re-run, or the next push, will run it again. Generated by Claude Code |
What this PR does
Adds
npx @agent-socket/gpu-browser. On its first run it installs the GPU browser server from part 1 (blitzdotdev/agent-socket#1) under a dedicated OS account. Every run prints a link for the agent, and you answer the agent's requests in that same terminal.Why
Part 1 only runs as a development run. Users need it isolated (it must not read their files) and installed without reading a README or running manual steps.
What gets installed, after one confirmation and one sudo prompt. Details and order are in
gpu-browser/install/README.md:agent-socket-gpu-browseraccount. No login, no password, its own group.dsclthe way MacPorts creates its user. The login attributes macOS adds on its own are deleted, so the account stays out of Users & Groups and the login window.IsHiddenis not used, because EDR tools flag it.render/videoso Chromium can open the GPU.playwright-core, installed with--ignore-scripts;/opt/agent-socket-gpu-browser. The account can run it but not change it.launchctl asuser <uid> sudo -u …. It checks that Chromium launches with its sandbox and reports the GPU. On Ubuntu 23.10+ it adds an AppArmor userns profile for this Chromium only. The copy is swapped in only if the test passes, so a failed update leaves the working install alone.zz-…because sudo applies the last matching rule. Testing showed that a user's own broader rule in/etc/sudoers.d/<user>otherwise wins and asks for a password. If that happens anyway, the CLI asks for the password once instead of failing.chmod 700, and uninstall offers to restore the old mode.Running it:
--forchanges that).alwayssaves the host to~/.agent-socket/gpu-browser/allowed-hosts.txt.status,stop,installanduninstall. A root-owneduninstall.shstays in/opt, so removal works even after the npm package is gone./opt/…, the account's home,~/.agent-socket/and~/.agent-socket/gpu-browser/.Base branch is part 1's branch, so this diff shows only part 2.
Checklist
gpu-browser/(newbin/andinstall/), CI (new path-filteredgpu-browser.yml), and docs.gpu-browser/test/install-smoke.shdoes a real install, start, a link used like an agent would (it checks the server runs as the account with Chromium's sandbox, a page, a screenshot, loopback blocked), stop with the link dead, then uninstall with nothing left. CI runs it on fresh ubuntu-latest and macos-latest runners, plus shellcheck anddash -n.always, thenyfor one session only), an update, and an uninstall that restores the home mode.npm test: unchanged parts pass. The GPU browser unit tests (8) and harness 63 pass on this branch.Not verified by me: the macOS path. That covers the dscl account, Chromium under
launchctl asuserfor an account that never logged in, the sudo rule's matching on macOS, and the real Metal GPU. The macOS CI job exercises all of it except the GPU, so its result is the first real answer. Publishing@agent-socket/gpu-browserto npm is a maintainer step.🤖 Generated with Claude Code
https://claude.ai/code/session_01CpTxf6FhMdiFuXFsbUam53
Generated by Claude Code