Documentation

cockpit browser

Open web pages in a task's in-app browser so people and agents can safely inspect, operate, and verify the same tabs.

Applies to
DesktopPWACLI
WindowsmacOSLinux

Verified 2026-10-10 · v4.103.0

Markdown

Some screens and steps may differ in the latest version.

cockpit browser is the official surface for opening real web pages in a task-scoped in-app browser and inspecting their DOM, appearance, and outcomes. It is driveable, not just viewable: it can click, type, select, upload, paste, press keys, and scroll.

HTML Surface displays reports produced by an agent. Use the in-app browser for external navigation, sign-in, form operation, and Web UI verification.

Share one page between person and agent

Each tab has one canonical page instance shared by the person viewing the side panel and the agent operating it. Switching tasks, tabs, or panels detaches the page from the window without reloading it, preserving unsent forms, scroll position, SPA state, and popup or OAuth context.

The User-Agent sent to sites omits Electron's product token and uses one consistent value in every frame, including cross-origin iframes running in a separate process. Authentication and bot checks that compare browser information across frames therefore see one browser.

Browser sessions and navigation history survive task completion, resume, and app restart. Form values and scroll position are restored after restart on a best-effort basis, and saved state is encrypted when the operating-system credential store is available.

After an app restart, a saved tab that has not been used stays unloaded without a renderer until it is needed. loaded: false together with rendererResponsive: false in tabs means unloaded, not stuck. Selecting that tab, showing it in the side panel, or targeting its tab ID with a command loads it.

A parked tab can receive input while hidden without showing a separate window or switching macOS Spaces. The same page is visible when the panel is shown again.

While a Cockpit overlay such as a dialog intersects the browser area, Cockpit temporarily parks the canonical page and displays its latest static snapshot instead of a blank surface. The snapshot is not interactive and does not reflect later page updates. Closing the overlay restores the same live page with its form and page state intact. Only when a snapshot cannot be captured promptly does Cockpit show a temporarily-hidden message instead.

Inspect a host tab from the PWA

Select Cockpit Browser in the PWA task panel to choose a session and tab inside that task's assigned Browser Identity and inspect the host's current viewport as a still image. The Identity selector contains only the Identities assigned to the task. Switching PWA panels or tabs does not navigate the host page, change the Desktop panel selection, or select a different host tab.

While the panel is selected, connected, and in the foreground, it refreshes ten seconds after the previous capture finishes. Refresh captures the current viewport immediately. The PWA does not wake an unloaded saved tab; it asks you to open that tab on the host. The preview shows its capture time and is marked stale while disconnected, after a failed update, or after 20 seconds without a successful capture.

The four Scroll the host tab controls move the real host page by about 80% of its viewport and then capture a new image. This changes the Desktop user's scroll position, but it does not activate links, fill forms, or submit anything. Enlarge shows the received image at its original resolution and lets you pan it locally. This is a periodic still preview, not video streaming or complete remote-browser control. Text inside the image is not exposed as text to a screen reader.

Frames are JPEG images limited to 1280 pixels and 256 KiB before base64 encoding. Cockpit does not save them to disk or persist them in PWA browser storage. Every request verifies the task, assigned Identity, session, and tab before and after capture. Metadata is limited to the title and origin; URL credentials, paths, queries, fragments, data URL payloads, general browser RPC, storage export, and local screenshot paths are not exposed.

Open a page

In the Desktop in-app browser, enter keywords in Search or enter URL to open a Google search. Domain names and IP addresses open as URLs; Cockpit supplies HTTP for localhost, 127.x.x.x, and [::1], and HTTPS for other hosts. An explicit scheme such as https:, file:, or about: is preserved. Ordinary text containing whitespace is treated as a search query even when it starts with a scheme-like prefix. This address-bar resolution applies only to the on-screen field; pass a URL to CLI open and goto.

cockpit browser open https://example.com --json
cockpit browser tabs --summary --json
cockpit browser show --json

Inside a Cockpit task, omitting a session or task ID uses the current task. open reuses its most recent session and selected tab. Add --new-tab when the current page has unsent state to preserve.

cockpit browser open https://example.com/form --new-tab --json
cockpit browser goto <tabId> https://example.com/next --json
cockpit browser reload <tabId> --json

tabs --summary reports selection, a body-text preview, loading time, renderer responsiveness, and the latest navigation error. Popups and page-side window.close() are reflected in Cockpit's tabs.

Choose snapshot or screenshot

cockpit browser snapshot <tabId> --json
cockpit browser snapshot <tabId> --wait-for-text "Ready" --timeout 10000 --json
cockpit browser screenshot <tabId> --full-page --output ./page.png --json

snapshot returns rendered text, links, buttons, inputs, roles, accessible names, and selectors. Use it to identify controls and verify machine-readable state. Use screenshot for visual layout, imagery, canvas content, or a result a person needs to see.

screenshot --full-page captures a long window-scrolling document in clips of at most 4096 CSS px and stitches them together. When the document itself cannot scroll but an inner overflow region can, Cockpit scrolls and stitches the single largest container, then restores its scroll position. Check documentHeight, capturedHeight, and scrolledElement in the result. A warning reports incomplete coverage caused by the 20,000 px cap, a repeated frame, or layouts with several or nested scrollers.

Locators traverse open shadow roots. A normal CSS selector works across them, and host >>> inner pins an explicit shadow boundary. Closed shadow roots are inaccessible from the page; use screenshot coordinates when necessary.

Find open tabs and control audio

Settings → Browser combines tabs and Identity management. Select All or an Identity in the left sidebar to filter the tab list, including tabs not currently visible. Selecting an Identity also shows its name, color, data clearing, and deletion controls above the tabs. Each tab row shows its task, identity, favicon, title, URL, and audio state. Select the task name to open the owning task and reveal the tab. Mute, unmute, and close controls act on one tab at a time.

The visible browser panel also provides mute or unmute on the current tab when it is playing audio. Muting there and muting from Settings or the CLI update the same per-tab state.

From the CLI, cockpit browser tabs reports audible and muted. Use cockpit browser mute <tabId> or cockpit browser unmute <tabId> without bringing the tab into view. Muting is saved per tab across app restarts and renderer recreation; closing the tab discards that setting. Muting does not close the page or stop its other activity. Browser Identity access boundaries still apply to agent operations; the Desktop list does not grant an agent access to other identities.

All and each Identity have a Close all tabs button. Review the scope and tab count in the confirmation dialog before proceeding; cancelling closes nothing. Identities, cookies, and login data are preserved, but unsaved input is lost. Tabs opened after confirmation are not included.

From the CLI, use cockpit browser tab close-all --confirm, adding --browser-identity <name-or-id> to limit the scope. Inside a task, the command is restricted to that task’s sessions and assigned Identity. Outside a task, omitting the Identity filter targets tabs across all Identities.

Evaluate JavaScript in the main frame

Use evaluate to inspect framework state, localStorage, element properties, and other values that are not rendered in the DOM.

cockpit browser evaluate <tabId> --expression "document.title"
cockpit browser evaluate <tabId> --expression "JSON.parse(localStorage.getItem('draft')).body" --json
cat inspect.js | cockpit browser evaluate <tabId> --stdin

JavaScript runs only in the tab's main frame and never enters an iframe. Top-level await is supported, and a returned Promise is awaited. The result must be JSON-serializable; DOM nodes, functions, symbols, and cyclic objects cannot be returned. Evaluation carries no user gesture, so it cannot stand in for a person when a Web API requires one. Pass a longer script through --expression-file or --stdin.

Operate by meaningful name

cockpit browser click <tabId> --role button --name "Submit" --json
cockpit browser type <tabId> --role textbox --name "Email" --text "name@example.com" --json
cockpit browser select <tabId> --selector "#plan" --option-text "Team" --json
cockpit browser scroll-into-view <tabId> --role button --name "Continue" --json

Prefer --role and --name, then a stable CSS selector, then viewport coordinates. Accessible names are computed from aria-labelledby, aria-label, labels, alt text, values, content, and titles. Matching is case-insensitive and exact by default; use --name-match prefix|contains explicitly when needed.

No match, several matches, and all-hidden matches fail with browser_target_not_found, browser_target_ambiguous, and browser_target_not_visible, respectively, and return candidate details. Cockpit never silently operates the first ambiguous candidate.

Before input, Cockpit scrolls the window and every scrollable ancestor on both axes to reveal the target. If the center of a requested child, such as text inside a large card link, does not land on the link, Cockpit keeps the nearest clickable ancestor and tries several safe hit points. It still refuses to click through an unrelated modal, banner, or other covering element and returns browser_click_target_unreachable with diagnostics for the candidate points.

Verify operations with postconditions

cockpit browser click <tabId> --role button --name "Save" \
  --wait-for-text "Saved" --timeout 10000 --json

cockpit browser click <tabId> --selector "#checkout" \
  --wait-for-url "*/complete" \
  --wait-for-response-url "*/api/orders*" \
  --wait-for-response-status "200-299" --json

changed and changeSignals report observed changes to the URL, DOM, form values, and related state. changed: false is not proof of success. Add a postcondition that matches the requested outcome: URL, visible text, selector state, a new tab, or a network status.

If eventDispatched: false but inputDelivered: true, the operation may have taken effect even though event propagation was stopped. Avoid an immediate retry that could submit twice; inspect the current page with a snapshot first.

Network verification returns only method, status, and a URL with query values and the hash masked. It never returns request or response bodies, headers, or payloads.

Type, paste, upload, and dismiss dialogs

cockpit browser paste <tabId> --selector "[contenteditable]" --text "Long content" --json
cockpit browser upload <tabId> --role button --name "Attach a file" --path ./build.zip --json
cockpit browser upload <tabId> --click-role button --click-name "Add image" --path ./cover.png --json
cockpit browser upload <tabId> --await-chooser --path ./cover.png --json
cockpit browser press <tabId> --selector "#search" --key Enter --json
cockpit browser dismiss-dialog <tabId> --button-text "OK" --json

type replaces the target value and verifies the final value. paste sends a real clipboard paste event, verifies that the requested text appeared, and restores the previous clipboard representations. If the clipboard has an image but no text representation, Cockpit supplies that image as a PNG in the paste event's files and items, then reports whether the page received it and rendered an image element. If rendering is not observed within two seconds, the result is verified: false with a warning; inspect the page before pasting again.

upload sets a file input or a file input inside a dropzone without opening the operating-system picker. For a page that creates a transient file input inside a click handler, use a --click-* locator for the control that opens the chooser, or use --await-chooser for the chooser remembered by the preceding click. A chooser is remembered for up to 60 seconds and discarded by navigation or the next click that opens no chooser. A chooser such as showOpenFilePicker with no backing input is unsupported. Inspect target and chooser in the result to confirm which input received the files.

dismiss-dialog handles a pending JavaScript dialog first, then a visible HTML dialog. Use --reject for the negative action. Postconditions do not expand authority: submitting, purchasing, publishing, deleting, and other external state changes remain subject to user approval.

Recover loading and renderer failures

Use health from tabs to distinguish these cases:

Value Meaning
loadingDurationMs How long Chromium has reported the page as loading
rendererResponsive Whether the renderer answered a short JavaScript ping
lastNavigationError The latest main-frame load failure

Wait when the site is merely slow, and inspect the URL or connection after a navigation error. If the renderer is stuck, recreate only that renderer while keeping the same tab ID, URL, session, and Browser Identity.

cockpit browser tab recreate <tabId> --json

Sign-in and safety boundaries

The in-app browser grants site permissions such as notifications, geolocation, and clipboard reading and writing by default, without a Cockpit confirmation dialog. OS and Web API restrictions still apply. It denies media capture including camera and microphone, screen capture, fullscreen, automatic fullscreen, permission to open external apps, keyboard lock, and deprecated synchronous clipboard reading. Open only sites you trust. This policy applies to every Browser Identity; separating Identities does not restrict site permissions.

See Browser Identity for cookie and localStorage isolation, task and Autorun assignment, system-browser session import, clearing, and removal.

When an agent uses cockpit secret request, the dedicated Desktop window and Ask tab, or the Inbox in an HTTPS-connected PWA, receive a request to fill the password field currently shown in the in-app browser once. Review the PC, task, purpose, Identity, actual URL, and field before entering the value yourself. Cockpit fills only that field and does not press Submit. It also never returns the value to the CLI or task. The input surface disappears as soon as the request completes, fails, is cancelled, or expires. See Security and data for destination rules, expiration, storage, and retry boundaries.

Passkeys can use Touch ID in signed macOS builds and Windows Hello on Windows. On macOS, Cockpit can use only passkeys registered from the in-app browser; it cannot directly use passkeys previously registered in Safari, Chrome, or iCloud Keychain.

On macOS and Linux, a USB security key is only partly usable. A request that does not ask for a PIN — typically second-factor sign-in where the site sends allowCredentials — completes by touching the key. Signing in with a saved passkey always fails, because the browser raises user verification to required for that request. Registering a new passkey on a key that has a PIN fails the same way, and a phone or tablet cannot be used. Windows handles every transport through the operating system's own WebAuthn dialog, so security keys and phone passkeys are expected to work there, but that is not verified yet.

When no passkey matches Touch ID, the browser panel explains why and offers registration of a new passkey, password sign-in, and cockpit browser import-session as alternatives. cockpit browser diagnostics records the recent attempt as no-matching-credential. On macOS, import-session transfers eligible sign-in state from Chrome, Brave, Edge, Arc, Vivaldi, Opera, or Firefox; it does not import the passkey itself.

Linux has no platform authenticator, so use a security key within the limits above, or a password. If no prompt appears or the attempt fails, use cockpit browser diagnostics to inspect platform-authenticator state and the most recent WebAuthn attempt. NotAllowedError is recorded by cause: discoverable-credential-unavailable for saved-passkey sign-in that is not supported yet, user-verification-unavailable for a PIN or other user verification Cockpit cannot collect, and not-allowed for a cancelled request, a timeout, or no authenticator that could answer.

You can also finish the sign-in in an external browser and bring it back with cockpit browser import-session. That is a workaround rather than passkey support: it transfers only cookies and localStorage, so some sites remain signed out.

Only HTTP and HTTPS links can normally leave the in-app browser. A mailto link opens only after the person's own interaction and explicit confirmation; other schemes are rejected. Agent automation does not count as that person interaction.