Repository navigation
Conversation
figitaki
force-pushed
the
carey/lightpanda-host
branch
from
October 14, 2026 22:52
304e105 to
9f86d56
Compare
SBM_BROWSER=lightpanda runs each session's browser in its own `lightpanda serve` process on a random loopback port. The broker passes --cdp-max-connections 1, so no other CDP client can attach after it connects. It also loads iframes, turns off the metrics endpoint, and turns off telemetry. Lightpanda's default page has no browser context, so hosts can now open the session's page themselves (BrowserHost.openPage). The existing e2e suite passes on Lightpanda 1.0.0. THREAT-MODEL.md says what changes: there is no sandbox, and CDP is a TCP port instead of a pipe. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
figitaki
force-pushed
the
carey/lightpanda-host
branch
from
October 15, 2026 21:20
9f86d56 to
46f42d6
Compare
PR #26 committed node_modules as a symlink to an absolute path on one machine. Everywhere else it is a dangling link, and in the original checkout it points at itself, so bun install and bun test fail there. The ignore rule was node_modules/ with a trailing slash, which matches only directories. A symlink with that name is a file to git, so it was not ignored. The rule is now node_modules, which covers both. The image build copies only package.json, bun.lock, src and tsconfig.json, so CI was never affected. Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…ts (#37) * Build browser-secrets target observations from the live page Add src/browser/observe.ts. It builds the TargetObservation that @turnkey/browser-secrets authorize decides on. Frame, document, URL and origin come from Page.getFrameTree on the broker's CDP session. Shadow-root mode and selector matches are read in an isolated world, so page script cannot fake them. A selector the browser rejects records false. Detached and unknown elements are left out, so authorize refuses them. BrowserSession gets a host-owned session ID that changes on each browser start. The tests run the observation builder against the fixture pages, and run the browser-secrets conformance fixture cases that the fixture pages can serve against live observations. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * Authorize fills with browser-secrets instead of BindingPolicy fill_secret and await_fill now decide with @turnkey/browser-secrets authorize, on an observation of the live page, in place of BindingPolicy.assertAllowed and its elementMatches callback. Request shape and binding checks run before the browser is touched. Each target is authorized again immediately before its write, and isSameDestination requires the tab, document and element that were checked. await_fill also requires the page it checked before the approval wait. Secret refs come from the package's createSecretRef, so both backends parse sbm:* bindings with the shared parser. bindingError is now a code such as invalid_fields instead of a sentence. A refused fill returns FillDeniedError: the package's fixed message for the deny reason, the reason code, and the element uid. It no longer includes the page URL or the bound origin. The e2e and HTTP tests now check the reason code, because the message text changed. Remove src/broker/binding.ts and parseBinding from mock-secrets.ts. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * Redact through the browser-secrets RedactionRegistry Every agent session now gets the @turnkey/browser-secrets RedactionRegistry, and server.ts scrubs each tool result with registry.scrub(). The broker's own registry is no longer used. - Before any write, injectResolved registers redactionEntries(ref, value): the plaintext and, for an sbm:fields secret, each payload part. - Each target is registered with its document-scoped identity (browser session, tab, frame, document, element) from the decision made just before its write. inject.ts no longer touches the registry. - The snapshot tags and elides fields with isRegisteredTarget, findSecretIds and findMatches, and reads the document identity from the browser. A behavior change: values shorter than four characters, such as a three-digit CVC, are no longer text-scanned. The filled field is still redacted by its target tag. The redaction tests now run on the package registry and record this change. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * Remove the broker's own redaction modules and URLPattern shim Nothing imports src/redaction/registry.ts or src/redaction/scrub.ts after the move to @turnkey/browser-secrets, so delete them. The package brings URLPattern (global or polyfill), so src/urlpattern.d.ts goes too. The import script checks a URL pattern with the package's parseBinding. docs/DESIGN.md now describes the observe-and-authorize fill flow and the package registry. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * Pin @turnkey/browser-secrets to 0.0.1-alpha.0 The first release of the package is an alpha, so the dependency is an exact version. A caret range would not follow alpha bumps. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * Resolve @turnkey/browser-secrets 0.0.1-alpha.0 from npm The package is on npm now, so the lockfile records the registry version instead of nothing. `bun install --frozen-lockfile` in the image build needed this entry. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* Serve MCP 2026-07-28 with browser handles bound to the caller open_browser returns a server-minted browser_id (192 random bits). Every browser tool takes an optional browser_id and acts in that browser. close_browser or the idle timeout ends it. A handle holds its own browser, redaction registry, element uids and pending fills, so fill_secret parks a fill on the handle and await_fill redeems it from any connection. - A handle is bound to the endpoint, principal (iss, sub) and OAuth client that opened it, the same binding a session has. For anyone else it gives the same "Unknown browser_id" as an id never minted. - The 2026-07-28 leg (createMcpHandler) replaces rejectModern. A modern request has no browser of its own, so browser tools need browser_id. - 2025 sessions keep their own browser for calls without browser_id, and can use handles too. This is for the Arcade gateway, which opens a session for every tool call. - Handles get their own copy of the session caps and the session idle timeout. A busy handle is not swept. - At a session limit, the caller's own least recently used session with no running browser and no request in flight is closed first, so a session-per-call client does not hit its limit after a few calls. Refs EMG-288 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Document browser handles and the 2026-07-28 leg HOSTED.md describes both protocol eras, the new Browser handles section, the session reclaim rule, and the limits handles share with sessions. The tool reference and skill cover open_browser, close_browser and browser_id. Refs EMG-288 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Update the README layout table for browser-secrets and auth src/redaction/ moved into @turnkey/browser-secrets, and src/auth/ had no row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Fix the open Dependabot alerts on the MCP TypeScript SDK. - @modelcontextprotocol/server 2.2.0 -> 2.3.1 (runtime): GHSA-jm63-cq47-j5jp, GHSA-46jr-4wx8-v453, GHSA-rvq5-wwqv-78pq - @modelcontextprotocol/client 2.2.0 -> 2.3.1 (dev): GHSA-6prh-2h8m-c8cw Both are direct dependencies. @modelcontextprotocol/core moves to 2.3.1 with them in bun.lock. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Kargo's NewestBuild strategy orders images by created time. Images built on the same base carried the base image's created time, so Kargo kept the first one and did not promote later builds. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Kargo's NewestBuild strategy reads the org.opencontainers.image.created label when it is set. The oven/bun base image sets that label, so every broker image built on it reported the base image's time, and Kargo kept promoting main-eaeea99. This replaces the SOURCE_DATE_EPOCH change, which Kargo ignores. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been 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.
The original Lightpanda prototype launched the browser inside the broker process environment. This update brings the experiment onto main
794d8642and replaces that launch path with an externally provisioned, one-shot worker container connected through a private Unix socket.The worker has no broker credentials or state, accepts one client, and is retired on disconnect, startup failure, crash or expiry. The offline examples use separate network namespaces, no networking, read-only roots, no capabilities and only a dedicated IPC directory. The broker receives no container-runtime or Kubernetes privileges. A startup gate rejects missing CDP document identity or isolated-world semantics instead of falling back to page-controlled evidence.
Main's caller ownership, scoped pending approvals, target binding, per-write authorization and redaction remain in place. SDK cancellation now reaches the fill path and is checked before export and each plaintext CDP write; cancelling a Lightpanda request closes its worker. The browser receives only intended approved field values, never Turnkey credentials or export bundles.
Validation:
This remains a draft offline experiment: one worker per broker, no pool or reconnect, no Internet browsing configuration, no Kubernetes deployment, and no real-secret setup. Mock approval is not Turnkey quorum. The compatibility gate and these tests are not a browser sandbox audit; deployment controls and any destination egress require separate review. See
docs/LIGHTPANDA.mdandexamples/lightpanda/for the boundary and reproducible tests.History preserves the original PR commit as an ancestor, with its in-broker implementation explicitly superseded; the branch update is a normal fast-forward, not a force-push. No merge to main or image publication is included.