Chromeless
A real, cloud-side Chromium you can drive — Triform's own browser binary that holds a persistent CDP/Playwright session, captures it as an encoder-controlled WebRTC screencast, and (when attached to an agent) hands that browser to the agent as a tool.
Working with it
Opening a Chromeless launches an embedded browser — its dedicated working surface.
How it appears
The same element type rendered as a definition, a circle instance, and a live workspace card.
When to use / not
When to use
- Driving a live web page from a run — navigate, snapshot the accessibility tree, click, fill, and type against real DOM that persists across calls.
- Giving an agent eyes and hands on the web: attach chromeless and it injects the `browser` tool into the agent's tool set.
- Watching the session as video — encoder-level control (sw / x264 / vaapi / nvenc) over a FrameSink-based WebRTC screencast in the portal's browser panel.
- Carrying a logged-in session forward by bonding a cookie-jar, so cookies survive cold pods and sibling browsers.
When not to use
- The target site needs a real user's authentication, OAuth, or session cookies — use user-browser (Triform Connect extension) instead.
- You only need an HTTP call against a JSON API — http is lighter than spinning up a whole browser; chromeless `fetch` is for when you need the page's own session.
- You want to script the browser from your own machine rather than cloud-side — that is the recorder / local automation path, not chromeless.
Topology
Attaches to another element as a modifier, shaping that element's behaviour rather than running on its own.
Properties
urlstring- Current URL (must be a valid URI)
start_urlstring- URL the chromeless navigates to on first open. Defaults to https://triform.wtf so the session shows content from the first second; can be customised per element via the properties panel. Distinct from `url`, which tracks the currently-loaded URL and mutates as the user navigates.
viewport_widthinteger- Viewport width in pixels
viewport_heightinteger- Viewport height in pixels
record_interactionsboolean- Record all browser interactions for replay automation
backendstring- Which runtime hosts this element's chromium target. "firecracker" is the only value: it routes through the isolation-executor microVM pool (ISOLATION_EXECUTOR_URL + BROWSER_USE_ISOLATION=1), allocating a per-element cb-chromium sandbox and returning a direct CDP-WS URL — the native-WebRTC product path. The URI scheme is `chromeless://`.
is_primary_for_circleboolean- User-pinned "default for agents" flag. When the platform's primary-browser resolver runs (agents asking "which browser should I use?"), sessions with this flag set win over the legacy slug-based deterministic ordering. At most one browser element per circle should carry this; the UI ensures that when a user pins a new session, any previously-pinned session is unpinned in the same round-trip.
enginestring- Encoder profile for the cloud_browser_worker binary. "sw" (default) uses software VP8/VP9 and works on every cluster node. "x264" uses libx264 for better compression at the cost of CPU. "vaapi" and "nvenc" select hardware encoders on GPU-capable nodes. "all" lets the dispatcher pick the best available encoder on the target node. Takes effect on next session start. Has no effect when backend=personal (the extension manages its own capture path).
default_visibilitystring- Default visibility applied to new tabs whose creating op didn't specify one. Per-tab overrides live on `tabs[].visibility`. Applies only when backend=user-extension.
osstring- Operating system (macos, windows, linux, chromeos). user-extension backend only.
browser_namestring- Browser engine/brand (chrome, firefox, edge, brave, arc). user-extension backend only.
extension_versionstring- Version of the installed Triform Connect extension (manifest.json). user-extension backend only.
device_namestring- Human-friendly device name (e.g. 'Chrome on MacBook Air'). user-extension backend only; shown in Devices tab.
allowed_domainsarray- Only sync cookies for these domains (empty = all). user-extension backend only.
sync_cookiesboolean- Automatically sync cookies from the connected browser to the server jar. user-extension backend only.
Capabilities
Defined for this element
- Webrtc
- Compute
Operations
- activityGET
- attachPOST
- attach-jarPOST
- attachmentsGET
- batch_statsGET
- capturePOST
- checkPOST
- clickPOST
- closePOST
- composePOST
- consolePOST
- console-messagesPOST
- contextGET
- cookie-deletePOST
- cookie-getPOST
- cookie-listPOST
- cookie-setPOST
- createPOST
- dblclickPOST
- deleteDELETE
- detachPOST
- detach-jarPOST
- dialog-acceptPOST
- dialog-dismissPOST
- disablePOST
- domPOST
- dom-streamGET
- dragPOST
- enablePOST
- evalPOST
- evaluatePOST
- export_bundleGET
- favouritesPOST
- fetchPOST
- fillPOST
- getGET
- go-backPOST
- go-forwardPOST
- gotoPOST
- helpPOST
- history-restorePOST
- history-savePOST
- hoverPOST
- import_bundlePOST
- intentionGET
- interactionsPOST
- invokePOST
- last-faviconGET
- last-frameGET
- list_attachmentsGET
- load-cookies-from-jarPOST
- logsGET
- mcp_tool_defGET
- networkPOST
- network-requestsPOST
- panel-state-streamGET
- playwrightPOST
- pressPOST
- press-keyPOST
- promotePOST
- readmeGET
- readme_updatePOST
- reloadPOST
- remove-modifierPOST
- renderGET
- resetPOST
- restorePOST
- run_getGET
- runsGET
- save-cookies-to-jarPOST
- schemaGET
- screencast-streamGET
- screenshotPOST
- selectPOST
- select-optionPOST
- snapshotPOST
- sourcePOST
- source_branchesGET
- source_promotePOST
- source_repairPOST
- source_statusGET
- source_validatePOST
- statsGET
- stopPOST
- storagePOST
- tab-closePOST
- tab-close-allPOST
- tab-listPOST
- tab-newPOST
- tab-selectPOST
- tabsPOST
- treeGET
- typePOST
- uncheckPOST
- updatePATCH
- update_metaPATCH
- uploadPOST
- versionGET
- ws-screencastGET
Ports
Inputs
- urlrequest
- pagerequest
Composition
Chromeless (chromeless)
Category: tools | Form: | Symbol: Cl
Provides a headless browser session to attached elements via the Triform cloud_browser_worker binary — custom Chromium build with an embedded encoder factory and FrameSinkVideoCapturer
Headless browser powered by the Triform
cloud_browser_workerbinary, built from forgejo.triform.dev/triform/chromeless (image: registry.triform.cloud/cloud-browser-webrtc/cb-chromium). Same CDP surface as Browserless — goto, snapshot, click, fill, press, tab management — but the Chromium process is ours: the encoder factory is embedded (sw / x264 / vaapi / nvenc profiles selectable viaengine), and FrameSinkVideoCapturer is used for screencast instead of the Browserless intermediary. When attached to an agent, injects thebrowsertool and skill instructions into the agent’s tool set and system prompt. Sessions are auto-created on first use and share the same playwright-cli + accessibility-tree ref model as the standardbrowserelement. Usebrowserwhen you want the stock Browserless pool; usechromelesswhen you need encoder-level control, FrameSink capture, or a specific build profile from the chromeless repo. The fail_action is “allow” so chromeless failures don’t block the attached element.
Guide
The sole “automate-a-real-browser” element. Backed by the Triform cloud_browser_worker binary — a custom Chromium build from forgejo.triform.dev/triform/chromeless with an embedded encoder factory and FrameSinkVideoCapturer. Provides persistent browser sessions, CDP-mediated automation, and agent tool injection, with full encoder-level control and Pattern-C native WebRTC.
Historical note: this element supersedes the retired
browserelement (Browserless pool, Pattern-A extension-hosted WebRTC, JPEG screencast intermediary). Migration 0119 wiped any remainingbrowserrows.
Overview
The chromeless element manages persistent Playwright sessions backed by the cloud_browser_worker binary. The binary owns the encoder factory directly — select sw (software VP8/VP9), x264, vaapi (Intel/AMD), or nvenc (NVIDIA) via the engine property. FrameSinkVideoCapturer is used for screencasts, giving cleaner frame delivery than the legacy Browserless intermediary path.
Sessions persist across operations — cookies, JS state, and DOM survive between calls. It works in two modes:
- Standalone — Call
goto,snapshot,click,fill,type, etc. directly via the API - Modifier — Attach to an agent to inject browser tools into the agent’s tool set
Quick Start
# Create a chromeless element
POST /api/my-circle/tools/chromeless/ {
"slug": "recorder",
"name": "Recorder",
"spec": { "viewport_width": 1280, "viewport_height": 720, "engine": "vaapi" }
}
# Navigate to a page
POST /api/my-circle/tools/chromeless/recorder/ops/goto {
"url": "https://example.com"
}
# Returns: { "url": "...", "title": "Example" }
# Get an accessibility snapshot to find interactive elements
POST /api/my-circle/tools/chromeless/recorder/ops/snapshot
# Returns a structured tree with [ref=eN] markers — use those refs in click/fill/type
# Take a screenshot (alias for `capture`)
POST /api/my-circle/tools/chromeless/recorder/ops/screenshot
# Returns: { "screenshot": "<base64-webp>", "mimeType": "image/webp" }
# Or attach it to an agent
POST /api/my-circle/tools/chromeless/recorder/ops/attach {
"target_id": "<agent-uuid>"
}
# Agent now has the `browser` tool injected into its tool set
Properties
| Property | Type | Default | Description |
|---|---|---|---|
url | string | "" | Currently loaded URL (persisted across operations) |
viewport_width | u32 | 1280 | Viewport width in pixels (320–3840) |
viewport_height | u32 | 720 | Viewport height in pixels (240–2160) |
timeout_ms | u32 | 30000 | Page load timeout (1000–120000 ms) |
record_interactions | boolean | true | Record interactions for replay automation |
session_id | string | "" | Explicit session ID for sharing (auto-generated if empty) |
engine | string | "sw" | Encoder profile: sw / x264 / vaapi / nvenc / all |
backend | string | "cloud" | Runtime binding: cloud = cloud_browser_worker, personal = Triform Connect |
tool_names | string[] | ["browser"] | Tool names injected when attached to agents |
Encoder Profiles
| Profile | Description | Notes |
|---|---|---|
sw | Software VP8/VP9 (default) | Works on every cluster node |
x264 | libx264 H.264 | Better compression, higher CPU cost |
vaapi | VAAPI hardware encode | Intel/AMD GPU nodes only |
nvenc | NVENC hardware encode | NVIDIA GPU nodes (triform-5) |
all | Auto-pick best available | Dispatcher resolves at session-create time |
Operations
Same CDP / Playwright op surface previously shared with the retired browser element:
| Canonical | Alias | Method | Description |
|---|---|---|---|
goto | — | POST | Navigate to a URL; returns { url, title } |
snapshot | — | POST | Capture accessibility tree with [ref=eN] annotations |
capture | screenshot | POST | Take a base64 WebP screenshot |
source | — | POST | Get raw page HTML via page.content() |
click | — | POST | Click element by ref (preferred) or CSS selector |
type | — | POST | Type text into an input by ref or selector |
fill | — | POST | Fill a form field by ref |
hover | — | POST | Hover over an element by ref |
press-key | — | POST | Press a keyboard key |
select-option | — | POST | Select dropdown option by ref |
evaluate | — | POST | Run JavaScript in the page context |
wait-for | — | POST | Wait for time / text / text_gone |
go-back | — | POST | History back |
go-forward | — | POST | History forward |
reload | — | POST | Reload current page |
dialog-accept / dialog-dismiss | — | POST | Handle dialogs |
cookie-get / cookie-set / cookie-delete | — | POST | Cookie jar management |
tab-list | tabs | POST | List all open tabs |
tab-new | — | POST | Create a new tab |
tab-select | — | POST | Switch the active tab |
tab-close | — | POST | Close a tab |
tab-close-all | — | POST | Close all tabs (optionally keeping one) |
favourites | — | POST | Manage saved bookmarks |
network-requests | — | POST | Inspect captured network activity |
console-messages | — | POST | Read captured browser console output |
fetch | — | POST | HTTP request using the browser’s session and cookies |
storage | — | POST | Read localStorage and sessionStorage for an origin |
attach-jar / detach-jar | — | POST | Bond/unbond to a cookie-jar element |
save-cookies-to-jar / load-cookies-from-jar | — | POST | Copy cookies to/from attached jar |
playwright | — | POST | Raw playwright-cli escape hatch |
render | — | GET | Serve cached HTML for iframe display (portal-internal) |
dom | — | POST | Get latest cached DOM snapshot |
interactions | — | POST | Human+agent interaction log |
Modifier Behavior
| Field | Value |
|---|---|
applies_to | actors, frontend |
cascade_behavior | nearest (closest chromeless wins) |
fail_action | allow (chromeless failure doesn’t block execution) |
evaluation_order | 50 |
When to Use
- Use chromeless for: any cloud-side browser automation — it’s the only browser-side element type now. Encoder-controlled WebRTC screencasts, FrameSink-based capture, persistent CDP sessions, agent tool injection.
- Use user-browser instead when: the target site requires authentication, OAuth, or session cookies from a real user (Triform Connect extension).
Errors
| Code | Class | Retryable | Description |
|---|---|---|---|
BROWSER_TIMEOUT | timeout | Yes | Page load timed out |
BROWSER_NAVIGATION_FAILED | internal | Yes | Navigation to URL failed |
Binary Provenance
- Repository:
forgejo.triform.dev/triform/chromeless - Image:
registry.triform.cloud/cloud-browser-webrtc/cb-chromium:cr7727-sw - Build profiles:
:cr7727-sw(default),:cr7727-x264,:cr7727-vaapi,:cr7727-nvenc - Backend env var (physics side):
CHROMELESS_URL
Relationships
- Attaches to: function, triformer, condition, evaluator, loop, wait, hitl, view, spa, ssr
- Uses: variable, cookie-jar
Capabilities
- executable: Element can be invoked to execute operations
- headless-browsing: Browse web pages headlessly via Playwright CLI + cloud_browser_worker
- screenshots: Capture page screenshots
- dom-interaction: Click, fill, type, hover, and select via accessibility-tree refs
- page-observation: Accessibility tree snapshots with [ref=eN] element targeting
- encoder-control: Selectable encoder profile: sw / x264 / vaapi / nvenc
- framesink-capture: FrameSinkVideoCapturer-based screencast (no Browserless intermediary)
Properties
| Property | Type | Default | Description |
|---|---|---|---|
tool_names | array | ["browser"] | Built-in tool names this element provides to attached agents |
agent_instructions | string | "" | Core skill guide injected on first tool use. Teaches the agent how to use browser commands. Source: skills/SKILL.md |
skill_references | object | {} | Reference guides for advanced topics. Keyed by topic name, served via the help command. |
url | string | "" | Current URL (must be a valid URI) |
start_url | string | "https://triform.wtf" | URL the chromeless navigates to on first open. Defaults to https://triform.wtf so the session shows content from the first second; can be customised per element via the properties panel. Distinct from url, which tracks the currently-loaded URL and mutates as the user navigates. |
viewport_width | integer | 1280 | Viewport width in pixels |
viewport_height | integer | 720 | Viewport height in pixels |
viewer | object | {} | Viewer state — tracks whether the browser panel is currently visible, when it was last open, and (when set) the preferred media transport for the screencast. |
user_agent | string | — | Custom user agent string |
timeout_ms | integer | 30000 | Page load timeout in milliseconds |
record_interactions | boolean | true | Record all browser interactions for replay automation |
session_id | string | "" | Explicit session ID for sharing sessions across elements (auto-generated if empty) |
hydration_source | string | "session_data" | Phase D — which durable store the executor reads when rehydrating a fresh CDP session after chromeless prune, pod failover, or first attach. Per-element opt-in for the unified state/cookies.json.gz + state/local_storage.json.gz + state/session_storage.json.gz CAS path that pairs with the browser-cdp-events Iggy stream (Phases B + C). . Behaviour: . - session_data (default) — legacy path. Reads encryptedcookies + per-origin localStorage from browser_sessions.session_data (Postgres). What everyexisting element gets; no behavioural change unless the property is flipped explicitly. . - cas — Phase D path. Reads the gzipped JSON blobs fromthe element’s git CAS repo at state/cookies.json.gz and the storage siblings,restores cookies via Storage.setCookies BEFORE anynavigation, and registers a per-tab Page.addScriptToEvaluateOnNewDocument hook that injectsper-origin storage BEFORE first-paint. Falls back to session_data when the CAS blobs are missing orunreadable so a fresh-flipped element doesn’t lose the first hydrate. . Both paths share the same restore-cookies-before-tabs ordering. The deploy-wide env var BROWSER_HYDRATE_FROM_CAS=1 is the second gate — both must be on for the CAS path to engage. Once the env gate flips to default-on (post-soak) this property becomes the only off-switch; until then it’s the per-element opt-in. . Iggy replay (close the seq-gap between snapshot and now) is a follow-up tier within Phase D — not yet implemented; until it lands the staleness window is bounded by the 60s compaction cadence, same shape as the legacy path. |
current_title | string | "" | Title of the current page (reflects active tab) |
tabs | array | [] | Active tabs in this browser session. Each tab carries its own visibility (offscreen|tab) so an agent can drive some tabs invisibly while showing others to the user on the same browser — e.g. scrape in the background AND hand off a visible tab for human takeover, both without a second element. |
active_tab_id | string | "" | ID of the currently active tab |
dom_state | object | {} | Per-tab DOM pointer map, keyed by tab_id → {dom_version, dom_updated_at, dom_iggy_offset, dom_truncated}. The DOM bytes themselves live in the Iggy stream browser-dom (topic snapshots), keyed by {element_id}.{tab_id} — NOT on spec, because multi-MB outerHTML on every element GET / SSE would saturate fanout. . SUPERSEDES the per-tab dom_version / dom_updated_at / dom_iggy_offset / dom_truncated fields declared on each tabs[] item (kept there for backwards compatibility, but no longer written). Lives at top level because set_element_spec_key("tabs", ...) performs a whole-array replacement that races every goto / navigate / reconcile write and consistently clobbered nested per-tab pointer writes (2026-04-24 incident, see browser_ops.rs bump_tab_dom_pointers comment block). |
tab_cas_state | object | {} | Per-tab CAS asset pointer map, keyed by tab_id → {has_favicon, favicon_mime, favicon_captured_at, has_dom, dom_captured_at, has_frame, frame_captured_at}. Mirrors the dom_state shape (top-level keyed map, not nested in tabs[]) for the same reason: avoids the set_element_spec_key("tabs", ...) whole-array replacement race documented in the dom_state docstring above. . Bytes live in the element’s git CAS repo at state/tabs/{tab_id}/{favicon.{ext}|last_dom.html.gz|last_frame.jpg}. state/ (sibling of .triform/) is the runtime-state namespace, matching the actions convention (code at element root, metadata under .triform/); pre-harmonization elements may still carry blobs at the legacy .triform/tabs/... path and readers (physics/src/api/handlers/last_frame.rs) fall back to that location automatically. . Distribution is via the existing GitEvent::CommitCreated → triform-events / platform-events Iggy fan-out — pods invalidate cached views and re-read the blob on commit. Designed to be the durable counterpart to the ephemeral browser-screencast-frames (active-tab JPEG) and browser-dom-snapshots (live DOM) Iggy streams. |
state_pointers | object | {} | Element-level CAS state-pointer map (Phase B harmonization, 2026-04-30). Mirrors tab_cas_state for kinds that span the whole element rather than one tab. Currently carries the cookies block; Phase C will add local_storage and session_storage keyed by origin. . Bytes live at state/cookies.json.gz (gzipped JSON of Vec<BridgeCookie>). Hydration (Phase D) reads the CAS blob + replays Iggy browser-cdp-events with seq > cookies_seq_watermark to catch deltas that arrived between the snapshot and the new session start. The portal’s freshness indicator reads _captured_at + _count for human-readable “42 cookies, synced 8s ago” rendering. . Producer: browser_bridge.rs::persist_cookie_jar_to_cas (called from CookieDelta + CookieSync handlers, throttled at MIN_COOKIES_WRITE_INTERVAL_SECS (60s) inside the unified cas_writer.rs::write_snapshot). |
closed_tabs | array | [] | Recently closed tabs (FIFO, max 50) — preserved for reopen |
history | array | [] | Navigation history for session resumption |
backend | string | "firecracker" | Which runtime hosts this element’s chromium target. “firecracker” is the only value: it routes through the isolation-executor microVM pool (ISOLATION_EXECUTOR_URL + BROWSER_USE_ISOLATION=1), allocating a per-element cb-chromium sandbox and returning a direct CDP-WS URL — the native-WebRTC product path. The URI scheme is chromeless://. |
is_primary_for_circle | boolean | false | User-pinned “default for agents” flag. When the platform’s primary-browser resolver runs (agents asking “which browser should I use?”), sessions with this flag set win over the legacy slug-based deterministic ordering. At most one browser element per circle should carry this; the UI ensures that when a user pins a new session, any previously-pinned session is unpinned in the same round-trip. |
engine | string | "sw" | Encoder profile for the cloud_browser_worker binary. “sw” (default) uses software VP8/VP9 and works on every cluster node. “x264” uses libx264 for better compression at the cost of CPU. “vaapi” and “nvenc” select hardware encoders on GPU-capable nodes. “all” lets the dispatcher pick the best available encoder on the target node. Takes effect on next session start. Has no effect when backend=personal (the extension manages its own capture path). |
default_visibility | string | "offscreen" | Default visibility applied to new tabs whose creating op didn’t specify one. Per-tab overrides live on tabs[].visibility. Applies only when backend=user-extension. |
device_fingerprint | string | "" | Stable fingerprint identifying this physical browser within a circle. blake3(circle_id || browser_name || os || machine_hint). Used to resolve the same element across reinstalls and auto-updates of the Triform Connect extension. |
os | string | "" | Operating system (macos, windows, linux, chromeos). user-extension backend only. |
browser_name | string | "" | Browser engine/brand (chrome, firefox, edge, brave, arc). user-extension backend only. |
extension_version | string | "" | Version of the installed Triform Connect extension (manifest.json). user-extension backend only. |
device_name | string | "" | Human-friendly device name (e.g. ‘Chrome on MacBook Air’). user-extension backend only; shown in Devices tab. |
machine_hint | string | "" | Opaque random string the extension generates once per install to stabilise the fingerprint across browser updates. |
first_seen_at | string | "" | UTC timestamp when this browser first paired. user-extension backend only. |
last_seen_at | string | "" | UTC timestamp of the most recent authenticate / keepalive. user-extension backend only. |
connected_replica | string | "" | Hostname of the physics replica currently holding the extension’s WebSocket. Cleared on disconnect. user-extension backend only. |
allowed_domains | array | [] | Only sync cookies for these domains (empty = all). user-extension backend only. |
sync_cookies | boolean | true | Automatically sync cookies from the connected browser to the server jar. user-extension backend only. |
cookies | array | [] | Durable mirror of the live cookie jar. The Triform Connect bridge writes here on every CookieSync / CookieDelta so agents keep working across WS drops, pod restarts, and cross-replica failovers. On circles with encryption_mode=‘platform’ the whole array is encrypted at rest — same guarantees as any other spec field. On fresh pod authenticate the bridge seeds the in-RAM jar from this field before marking the session ready. Not a replacement for the in-RAM jar — ops still read from that for sub-millisecond latency. |
cookies_updated_at | string | "" | UTC timestamp of the last durable cookie write. Written by the bridge alongside spec.cookies so the portal can surface a “cookies synced 42s ago” freshness indicator, and so the cookie- age guards in agent tooling can reason about staleness without walking every cookie’s expirationDate. |
history_analysis_enabled | boolean | false | User has granted the extension the history optional permission and opted into the browsing-analysis flow (REQ-MIGRATION-002). Drives whether the popup shows the “Analyze my browsing” CTA or the pre-permission consent path. Flipped by the extension itself on consent grant; revoked when the user removes the permission in chrome://settings. |
history_analysis_updated_at | string | "" | UTC timestamp of the last durable history-analysis write. Freshness marker for the report view; lets the portal render “Analyzed 3 hours ago” and drives the “refresh analysis” CTA. |
history_analysis | object | {} | Local aggregation of the user’s chrome.history for the past window_days days, computed entirely inside the Triform Connect extension and pushed via BridgeClientMessage::HistorySync. Bounded schema — full URLs, query strings, fragments, and finer-than-hour timestamps NEVER leave the device. What’s stored here is already the transmitted summary, not raw history. See REQ-MIGRATION-002 for the privacy contract. |
Operations
activity
Get /ops/activity | Auth: Read
Get activity events for this element
Scope depends on element capabilities: individual elements query by element_id, project-form elements with activity-scope-members include member activities, circle-level elements with activity-scope-all query the entire circle. Gracefully returns empty list if activities table is missing (old circles).
attach
Post /ops/attach | Auth: Read
Attach this tool to a target agent element
This is the critical operation that grants an agent access to tools. POST the tool’s attach endpoint with target_id=<agent_uuid>. The agent’s enabled_tools list is recomputed on every invocation, so attaching/detaching takes effect on the next agent run. The target must be an element listed in the tool’s contract.yaml attaches list (typically agents).
attach-jar
Post /ops/attach-jar | Auth: Write
Bond this browser to a cookie-jar element
Sugar that calls
cookie-jar/ops/attach(browser_id=self.id)on the target jar. After attach,save-cookies-to-jarandload-cookies-from-jarstart working. Rejects if another browser is already attached to that jar — detach that browser first.
attachments
Get /ops/attachments | Auth: Read
List all modifiers and resources attached to this element
Returns both modifiers (policy enforcement) and resources (data injection) with is_modifier flag to distinguish. Items in the generated MODIFIER_TYPES list are modifiers; everything else is a resource. Includes cascade_policy and version pin info.
batch_stats
Get /ops/batch_stats | Auth: Read
Get per-element statistics for all children of this element
Returns per-child stats plus an aggregate. Most meaningful on compound or manifest form elements (repositories, circles, projects); atoms have no children so the result is an empty children array with a zeroed aggregate. Uses efficient GROUP BY SQL. Weighted averages for eval scores.
capture
Post /ops/capture | Auth: Read
Capture a page screenshot (alias: screenshot)
Takes a screenshot of the current page via CDP. Returns the image as a base64-encoded WebP string (compressed for efficiency). For understanding page structure, prefer browser_snapshot over screenshots. For PNG/JPEG format, use a user-browser element instead. Also accessible under the human-friendly name
screenshot— both names dispatch to the same handler.
click
Post /ops/click | Auth: Write
Click an element by ref or CSS selector
Clicks an element via Playwright with auto-wait. Prefer using ref (from browser_snapshot) over CSS selectors. Playwright waits for the element to be actionable before clicking. Supports double-click, right-click, and modifier keys.
compose
Post /ops/compose | Auth: Execute
Batch add and remove modifiers on this element in a single call
Declarative composition: add modifiers by ref path (slug or path@version) and remove by attachment ID, all in one atomic call on the target element. Each ‘add’ entry resolves the source element, validates topology, attaches with optional priority and cascade policy. Each ‘remove’ entry deletes the attachment row. Returns a summary of what was added and removed. Example: compose({ add: [{ref: “my-prompt”}, {ref: “rate-limit/api@v2”, priority: 50}], remove: [{attachment_id: “uuid”}] })
context
Get /ops/context | Auth: Read
Get connected elements (graph traversal)
Graph traversal showing all connected elements with their relationship type (contains, contained_by, references, referenced_by, attaches, etc.). Use ?depth=N to control traversal depth (default 1) and ?types=actor,data to filter by element types.
create
Post /ops/create | Auth: Write
Create child element
POST to the parent path — element_type goes in the request body, NOT the URL. Both element_type and slug are required and must be non-empty. Name is derived from slug if omitted. Writes to both Git and PostgreSQL. All elements are stored flat under the circle — no intermediate library wrapper rows.
delete
Delete /ops/delete | Auth: Admin
Delete element (soft delete)
Soft delete — sets state to ‘deleted’ but retains the record. Cannot delete elements that have children (has_no_bond precondition) or active runs. Requires admin auth and confirmation.
detach
Post /ops/detach | Auth: Read
Detach this tool from a target agent element
Removes tool access from the agent. Takes effect on next agent invocation. Requires the same target_id used in attach.
detach-jar
Post /ops/detach-jar | Auth: Write
Unbond this browser from its attached cookie jar
Finds the cookie-jar whose
attached_browser_idequals this browser’s id (reverse lookup — there is at most one) and clears the attachment on it. Leaves both browser.spec.cookies and jar.spec.cookies intact; only the bond is severed.force=trueonly matters if the jar was already orphaned.
disable
Post /ops/disable | Auth: Admin
Disable element (hides and prevents use)
Idempotent — safe to call on already-disabled elements. Optionally pass a reason string. Disabled elements cannot be invoked or executed. Inverse of enable.
dom
Post /ops/dom | Auth: Read
Get the latest cached DOM snapshot for a tab
Returns the most recent outerHTML captured for
tab_idfrom the executor’s in-memory page cache, alongside the tab’s four DOM pointer fields (dom_version, dom_updated_at, dom_iggy_offset, dom_truncated). This is the cache-first read — sub-ms latency, no CDP round-trip, doesn’t churn the live session. Compared tosource:sourcecalls CDPpage.content()live every time, so it’s authoritative but expensive.domreads what was last captured by the backend’s publish pipeline (any navigate / click / render / content op) — perfect for agents that received adom_versionbump via SSE and want to pull the matching bytes without an extra CDP trip. Cache miss (cold pod, never-loaded tab) returns an emptydomstring anddom_version = 0. In that case, callsource(live fetch) orrender(cached + server injections) to populate the cache.
dom-stream
Get /ops/dom-stream | Auth: Read
Per-tab DOM-over-Iggy SSE; tails the browser-dom snapshots topic filtered to {element_id}.{tab_id}. Shared handler with browser.
enable
Post /ops/enable | Auth: Admin
Enable element (makes usable and visible)
Idempotent — safe to call on already-enabled elements. Transitions element to ready/enabled state. Cannot enable deleted elements. Inverse of disable.
export_bundle
Get /ops/export/bundle | Auth: Read
Export element as downloadable git bundle
On non-root-namespace elements, returns a binary git bundle. On root-namespace (circle) elements, dispatch hands off to the circle’s own export_bundle op, which returns a multi-element JSON envelope with one base64 bundle per child element — this is intentional, not an error.
fetch
Post /ops/fetch | Auth: Write
Execute an HTTP request using the browser’s session and cookies
Executes an HTTP request inside the browser context with credentials:include by default — inherits cookies, auth headers, session state. Most powerful on the user-extension backend (uses the user’s real authenticated session). 30s timeout. Supports any HTTP method, custom headers, and request body. Use this to call authenticated APIs that would reject server-side requests.
fill
Post /ops/fill | Auth: Write
Fill a form field by ref
Fills a form field identified by ref from browser_snapshot. Clears the field first. For checkboxes use true/false, for dropdowns use the option text.
get
Get /ops/get | Auth: Read
Get element details
Element is already resolved by the routing layer — this returns the cached element, not a fresh DB query. Use the path /api/{circle}/{slug} to address elements.
go-back
Post /ops/go-back | Auth: Write
Navigate back in history
Navigates back one page in the browser history (equivalent to pressing the browser Back button). Returns the URL and title of the page after navigation.
go-forward
Post /ops/go-forward | Auth: Write
Navigate forward in history
Navigates forward one page in the browser history (equivalent to pressing the browser Forward button). Returns the URL and title of the page after navigation.
goto
Post /ops/goto | Auth: Write
Navigate to a URL
Navigates the browser session to the URL via Playwright. Waits for DOMContentLoaded (30s timeout). Returns url and title. After navigating, call browser_snapshot to see the page structure. URL, title, and history are persisted to the element’s spec.
import_bundle
Post /ops/import/bundle | Auth: Write
Import git bundle into element
Accepts a base64-encoded git bundle in the JSON bundle_base64 field. Use overwrite=true to replace existing elements with same slug (default skips duplicates). Imported elements get new UUIDs. Returns counts of imported/skipped elements and any errors.
intention
Get /ops/intention | Auth: Read
Get element intention with full inheritance chain
Returns three levels: direct (this element’s intention), inherited (from category and root), and resolved (final merged intention). Useful for understanding an element’s purpose in context of its hierarchy.
invoke
Post /ops/invoke | Auth: Execute
Invoke the tool with input
Tools are primarily invoked indirectly by agents (the agent loop calls tools automatically). Direct invoke is for standalone testing. Input must match the tool’s port schema. Use ?async=true to get a run_id for polling. Browser tools return screenshots as base64 PNG.
last-favicon
Get /ops/last-favicon | Auth: Read
Durable last-known favicon for (element_id, tab_id) from the element’s git CAS. Shared handler with browser.
last-frame
Get /ops/last-frame | Auth: Read
Durable last-known JPEG screencast frame for (element_id, tab_id) from the element’s git CAS. Shared handler with browser.
list_attachments
Get /ops/targets | Auth: Read
List all agents this tool is attached to
Returns all agent elements where this tool is currently attached. Shows target_id, target_type, priority, and cascade_policy.
load-cookies-from-jar
Post /ops/load-cookies-from-jar | Auth: Write
Seed this browser’s cookies from the attached jar
Reverse-looks up the attached jar and copies its cookies into browser.spec.cookies. Use on fresh pod cold-start or when an agent wants to rehydrate an authenticated session from a jar that a sibling browser populated. Optional
domainfilter (suffix match) loads only cookies for that domain — useful when multiple domains live in the jar but this browser only needs one vertical’s auth.
logs
Get /ops/logs | Auth: Read
Get execution logs
mcp_tool_def
Get /ops/mcp/tool | Auth: Read
Get this tool’s MCP tool definition (name, description, inputSchema)
panel-state-stream
Get /ops/panel-state-stream | Auth: Read
Per-element panel-state SSE — snapshot on connect plus delta stream consuming browser-cdp-events. Transcodes CDP-flavored event types into portal-flavored deltas. Shared handler with browser.
playwright
Post /ops/playwright | Auth: Write
Run a playwright-cli command (stealthed via patchright in the sidecar)
Unified browser op — forwards {command, args} to the patchright-backed stealth sidecar and returns the raw {output, error} result. Use this to drive the browser with the full playwright-cli vocabulary: goto, click, fill, type, press, hover, select, eval, snapshot, screenshot, go-back, go-forward, reload, dialog-accept/dismiss, wait, tab-list, and any command the sidecar exposes on POST /exec. Security: goto / navigate commands are SSRF-checked server-side before the call reaches the sidecar. Cookie persistence and session hydration happen transparently via the executor’s existing cookie jar and restore-on-cold-pod logic — callers don’t need to manage state across ephemeral browser instances.
promote
Post /ops/promote | Auth: Admin
Promote element configuration to a target environment
Only for manifest-form elements (projects). Environments advance: dev → demo → live. dev→demo requires member+ role, demo→live requires admin. Freezes member versions at promotion time (creates snapshot). Persists environment config to spec.environments.
readme
Get /ops/readme | Auth: Read
Get element README.md content
Reads README.md from the element’s git repository. Returns empty content (not an error) if no README exists. Always returns markdown format.
readme_update
Post /ops/readme_update | Auth: Write
Update element README.md content
Creates or overwrites README.md in the element’s git repo. Commits to the draft branch. Content must be provided as a markdown string.
reload
Post /ops/reload | Auth: Write
Reload the current page
Reloads the current page in the browser. Waits for DOMContentLoaded before returning. Returns the URL and title of the reloaded page.
remove-modifier
Post /ops/remove-modifier | Auth: Execute
Remove an attached modifier from this element by attachment ID
Removes a modifier/resource attachment by its row ID. The ID comes from the attachments or context API. This is the reverse of attach — called on the target element, not the source.
reset
Post /ops/reset | Auth: Execute
Reset tool state to idle
Use when a tool is stuck in error state. Resets to idle so it can be invoked again. For browser tools, this also clears the CDP session — the next navigation starts fresh.
restore
Post /ops/restore | Auth: Admin
Restore element to a specific version
Automatically snapshots the current state before restoring (creates a ‘Before restore to vN’ version entry). Writes restored spec to git as .triform/spec.yaml. Git failures warn but don’t fail the operation — DB state is authoritative. Cannot restore deleted elements.
run_get
Get /ops/runs/{run_id} | Auth: Read
Get details of a specific run
runs
Get /ops/runs | Auth: Read
List execution runs
save-cookies-to-jar
Post /ops/save-cookies-to-jar | Auth: Write
Copy this browser’s live cookies into the attached jar
Reverse-looks up the attached jar (cookie-jar with attached_browser_id = self.id). Copies browser.spec.cookies → jar.spec.cookies wholesale; the jar bumps its cookies_version and recomputes derived fields. Supports optimistic concurrency via
expected_version— if passed and the jar’s current version differs, the write is rejected with JAR_VERSION_CONFLICT and the browser’s spec is left untouched. Use this after the bridge’s CookieDelta landed in browser.spec.cookies to make the jar durable.
schema
Get /ops/schema | Auth: Read
Get input/output port schemas (MCP tools/list compatible)
Returns the MCP-compatible tool schema. Useful for validating what inputs a tool expects before invoking. For platform tools, the schema reflects currently enabled capabilities.
screencast-stream
Get /ops/screencast-stream | Auth: Read
JPEG-over-Iggy screencast SSE. Decouples portal viewport from which pod owns the CDP session. Shared handler with browser.
screenshot
Post /ops/capture | Auth: Read
Capture a page screenshot (canonical alias: capture)
Human-friendly alias for
capture— both names POST to the same path and produce the same result. Returns the image as a base64-encoded WebP string. Prefer browser_snapshot for understanding page structure; use screenshot for visual capture.
snapshot
Post /ops/snapshot | Auth: Read
Capture page accessibility snapshot
Captures an accessibility tree of the current page via Playwright. Returns a structured text representation with interactive elements annotated with [ref=eN] identifiers. Use the ref values in click, fill, hover, and select-option operations. This is the primary way agents should observe page state – more compact and structured than raw HTML.
source
Post /ops/source | Auth: Read
Get the HTML source of the current page
Returns raw HTML of the current page via Playwright page.content(). Does not navigate; returns content of the current page. Requires a prior goto call to have loaded a page.
source_branches
Get /ops/source/branches | Auth: Read
List Source branches for this element
Returns the standard draft/demo/live Source branches, their current commits, and promotion relationships. Use GET /api/{element_path}/ops/source/branches.
source_promote
Post /ops/source/promote | Auth: Write
Promote Source branch forward
Promotes draft to demo or demo to live through the generated element op path. Direct Git pushes to demo/live are blocked by Source policy.
source_repair
Post /ops/source/repair | Auth: Write
Inspect or repair the element Source index
Runs Source repair through the element operation path. Defaults to dry_run=true; set dry_run=false only after reviewing a dry-run report.
source_status
Get /ops/source/status | Auth: Read
Get Source control status for this element
Returns the branch-aware clone URL, checkout commands, current draft commit, child source-link count, portable export summary, Source health, warnings, and auth hints for the addressed element. Use the element-first path: GET /api/{element_path}/ops/source/status.
source_validate
Post /ops/source/validate | Auth: Read
Validate Source branch contents
Validates a Source branch before accepting local Git workflow changes or promotion. Defaults to branch=draft and rejects runtime data, generated output, secret material, and unreadable CAS refs.
stats
Get /ops/stats | Auth: Read
Get aggregate statistics for this element
Health status is computed: error if errors_per_day > 5 or success_rate < 0.8, warning if errors_per_day > 0 or success_rate < 0.95. Firing alerts escalate health to error/warning. Default period is ‘day’. Returns runs_per_day, success_rate, avg_duration_ms, and more.
stop
Post /ops/stop | Auth: Write
Stop the in-flight page load
Cancels an in-flight navigation on the target tab via CDP Page.stopLoading. The URL and title remain whatever was committed before the stop. Use this when the reload button is showing the “Stop” affordance during a slow load.
storage
Post /ops/storage | Auth: Read
Read localStorage and sessionStorage for an origin
Reads localStorage and sessionStorage for a specific origin. Origin must be a full URL (e.g. “https://example.com”). On the user-extension backend, requires an open tab on that origin. On the browserless / stealth backends, navigates to the origin first if needed.
tab-list
Post /ops/tab-list | Auth: Read
List all open browser tabs (alias: tabs)
Returns a list of all open tabs in this browser session. Each tab includes its ID, URL, and title. All tabs share cookies and localStorage. Also accessible under the shorter name
tabs(matching the spec field of the same name) — both names dispatch to the same handler.
tab-new
Post /ops/tab-new | Auth: Write
Create a new browser tab
Creates a new tab in this browser session. Optionally navigate to a URL immediately. The new tab becomes the active tab. Returns the new tab’s stable ID.
tabs
Post /ops/tab-list | Auth: Read
List all open browser tabs (canonical alias: tab-list)
Human-friendly alias for
tab-list— matches thetabsfield in the element spec. Both names POST to the same path and return the same payload. Use whichever feels natural; the canonical action-style name istab-list.
tree
Get /ops/tree | Auth: Read
Get the element’s position in the graph — ancestors, children, references, and subtree statistics
Uses per-circle ElementGraph cache for O(1) lookups. Returns ancestors (containment chain), children (direct), members (references), referenced_by (reverse refs), attachments, and subtree stats. Default depth is 3, max is 10. Pass ?include_metadata=true for name/state on each node.
type
Post /ops/type | Auth: Write
Type text into an element by ref or CSS selector
Types text into an input/textarea via Playwright. Clears the field first, then enters text. Prefer using ref (from browser_snapshot) over CSS selectors. Optionally press Enter after typing with the submit flag.
update
Patch /ops/update | Auth: Write
Update element
Partial update — send only the fields you want to change.
spec,name, andintentionare all independently optional.specMUST be a JSON object when present; deep-merged into the existing spec by default. Empty{"spec":{}}preserves existing spec content but still records a new version (no-op for content, not for version state). To clear/replace the entire spec wholesale send{"spec":{...},"deep":false}. List-typed spec fields use replace semantics (the patch list replaces the existing list, no array merging). Coordinates Git + DB writes. Slug cannot be changed after creation.
update_meta
Patch /ops/update_meta | Auth: Write
Update element metadata (lightweight merge — does NOT bump version or snapshot spec)
Shallow JSONB merge into element.meta. Top-level keys in the provided value replace existing meta values; other keys are preserved. Used for UI metadata like canvas positions, panel state, viewer preferences. Wire-shape op_name is
update_meta(distinct fromupdate) so SSE subscribers + the cache auto-invalidator can distinguish lightweight metadata changes from spec edits without inspecting the payload. The MutatingElementStore wrapper stamps this op_name on the lifecycle event emitted byupdate_element_metastorage calls.
version
Get /ops/version | Auth: Read
Get current version or full history
Returns current version by default. Pass ?history=true for full version history (up to ?limit=N, default 50). Versions are backed by the element_versions table. Every spec update creates a new version entry.
ws-screencast
Get /ops/ws/screencast | Auth: Read
Live JPEG screencast over WebSocket (binary frames). ?mode=input skips frame streaming for lightweight hover tracking. ?transport=webrtc selects Pattern C native cb-chromium WebRTC. Shared handler with browser.
Error Codes
| Code | Class | Retryable | Description |
|---|---|---|---|
BROWSER_TIMEOUT | timeout | yes | Page load timed out |
BROWSER_NAVIGATION_FAILED | internal | yes | Navigation to URL failed |
Observability
Defined for this element
Metrics
- invocation_count
- duration_ms
- error_rate
- webrtc_session_count
- webrtc_session_closed_count
- webrtc_ice_connected_count
- webrtc_ice_failed_count
- webrtc_dc_opened_count
- webrtc_replay_hit_count
- webrtc_replay_miss_count
- webrtc_session_duration_ms
- webrtc_signaling_handshake_ms
Events
- chromeless.completed
- chromeless.failed
- chromeless.webrtc.session_created
- chromeless.webrtc.session_closed
- chromeless.webrtc.ice.connected
- chromeless.webrtc.ice.failed
- chromeless.webrtc.dc.opened
- chromeless.webrtc.replay.hit
- chromeless.webrtc.replay.miss
Pricing / cost
Inherited from tools
Operation costs
- tool_use: free
Set it up
- Start URLstring
- URL the chromeless navigates to on first open. Leave blank for https://triform.wtf.
- Viewportstring
- Initial viewport size. Resolves to spec.viewport_width and spec.viewport_height at submit time.
Skill pack
Bundled agent skill pack(s) for this element. Download the docs + skills kit:
Download kit.zip- browser
chromeless