Repository navigation
Install agents from oap:// links in OAP Desktop - #41
Open
josephschorr wants to merge 28 commits into
Open
josephschorr wants to merge 28 commits into
josephschorr wants to merge 28 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Member
|
Security review pass on the install-link flow (clickjacking, info leakage, user approval). Fixes are in 6f6d680. Fixed
Still open, not addressed here
Tests: race-enabled tests pass for 🤖 Generated with Claude Code |
An error occurred while trying to automatically change base from
feat/desktop-install-protocol
to
main
October 30, 2026 19:57
josephschorr
force-pushed
the
feat/desktop-install-links
branch
from
October 30, 2026 23:03
fc5907e to
eefb1a5
Compare
Register the Apple Event handler before the run loop and consume links on their own goroutines. Each link is parsed, offered to the install gate under the lifecycle lock (held while bring-up runs, refused when stopped/failed or busy), fetched through linkinstall.Prepare with the allowlist re-read from config.json, approved in an always-shown Cancel-default dialog, and installed through installApproved, now shared with the .oap file picker. phase() is factored out of the settings state closure and reports starting from the moment bring-up is committed (so a cold-launch link is held) and stopped after Stop (it previously kept reporting the last bring-up step).
Claim the link held during bring-up in the same critical section that flips running, and launch it before the post-bring-up refreshes, so a link arriving in that window is refused as busy instead of taking the gate. Every release of the gate also drains any link still held, and a link held behind a busy gate is logged. Add Gate.Pending for that diagnostic, and factor the stopped transition into markStoppedLocked so it is testable.
oras dials ghcr.io:443, ghcr.io. and GHCR.IO as ghcr.io, but RefHasPrefix compared hosts byte-exactly, so an allowlist entry spelled ghcr.io:443/... admitted OAP agent refs that the signing-key pin treated as unpinned. Compare canonical hosts (lowercase, one trailing dot stripped, explicit :443 dropped), error on hosts with no unambiguous canonical form, and refuse non-canonical hosts in ValidatePrefix so such entries can't be saved.
With oap desktop running from a terminal, clicking an install link made LaunchServices start the .app, whose startup orphan cleanup then killed the terminal instance's live VM. Take an exclusive non-blocking flock on <support>/desktop.lock first, hold it for the process lifetime, and have a second instance log, notify and exit without touching the VM.
- A dialog that can't be shown is installDialogFailed, logged and notified on both the link and file paths, never a silent cancel; only osascript's -128 is a cancel. - Link failures from a malformed install source, an unusable ref or the embedded signing key get their own messages instead of "Couldn't download". - A ref that names a digest must resolve to that digest before any pull. - Confirming Uninstall marks the cluster stopped and drops a held link with a notification, so links arriving mid-uninstall are refused. - Log a GetURL Apple Event that carries no URL instead of dropping it.
A link clicked while OAP Desktop isn't open launches it and is held until start-up finishes; links are refused only while it is stopped, failed to start, or setup isn't finished.
ref is React's reserved element-ref attribute; carrying the OCI image reference in it was fragile. Update the component, its test, the four agent pages and the Markdown rendition rule, which gains a test row.
Extract the relay-animated OAP mark from the setup page into shared relay.js/relay.css assets and serve a small /installing page that uses them. The page reads the agent name from its query string and writes it with textContent only, so bundle-supplied names never reach the markup.
Add --width/--height to the hidden desktop-window command on every platform, defaulting to the setup window's 760x640 so existing callers are unchanged.
A launched .app's stderr goes nowhere, so a notification osascript could not post was effectively invisible. runDesktop now routes those failures to the desktop log as well.
After the user approves an install (oap:// link or the .oap picker), open a small Installing window with the relay-animated mark, close it when the install finishes, then open the New-chat picker with the installed agent preselected. Failures, including a cluster that isn't running, get a native dialog instead of an easily-dropped notification; needs-config shows a dialog and then opens the dashboard. The adoption notice is kept since the chat page doesn't carry it.
- Flatten every non-printable rune in the link approval dialog, not just control characters. Format runes (U+202E, U+200B, U+FEFF) could reorder or hide text, and %q escapes them as \uXXXX, which AppleScript rejects, so the dialog failed to show at all. - Record link installs as registry installs with the link's ref and the OCI digest Prepare verified or resolved, instead of as a local file. - Keep raw registry errors out of link notifications (they stay in the log) and rate-limit link banners, so a web page firing oap:// links cannot flood the screen. Dropped banners are logged. - Send X-Frame-Options: DENY and frame-ancestors 'none' from LoopbackGuard, covering the setup and settings servers: a framed page's own requests carry the loopback Origin and would pass the Origin check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An Install chosen before the approval dialog has been up for a second now shows the dialog again with a note, so a click or keypress already in progress when a page fires a link cannot approve it. Cancelling the dialog refuses links for ten seconds, so a page cannot immediately re-raise it. A second link arriving during start-up no longer silently replaces the held one: the first keeps its place and the second is refused with a notification.
Every osascript call spliced its text into the script source with Go's %q, which is not AppleScript string syntax: a format rune such as U+202E became and the dialog failed to show with a syntax error, and the file-picker install dialog passed a bundle's name and description through unfiltered. Text now reaches the script as argv, never as source, so no input can break or rewrite the script. The shared dialog sanitizer also strips format runes, and the file-picker confirm dialog now flattens the bundle's name, version and description like the link dialog does.
josephschorr
force-pushed
the
feat/desktop-install-links
branch
from
November 5, 2026 16:36
eefb1a5 to
a658bb5
Compare
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #40, which publishes the signed example agents this installs. Retarget to
mainonce #40 merges.Motivation
We want an "Install in OAP Desktop" button on the docs pages for the example agents. Any web page or app can open a custom link, so it can't actually be one click. Desktop must ask the user every time, and it only accepts agents from sources the user trusts.
Summary of changes
oap://install/agent?...links. The link format is versioned, so new kinds of links can be added later, and an older app tells the user to update instead of failing silently.Alternatives considered
Known gaps (follow-ups)
Provenance
Ship gate
mage test:unitmage test:integrationmage test:e2eAll three suites pass. A few unrelated tests timed out on a heavily loaded machine; each passed when re-run on its own and they are outside the code this change touches.
We also tested this by hand on a locally built OAP Desktop:
Regeneration
config/, CLI docsCoverage