Download all docs
tools

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.

Cl
type

Chromeless

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

toolsmodifierdefinition

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_worker binary, 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 via engine), and FrameSinkVideoCapturer is used for screencast instead of the Browserless intermediary. When attached to an agent, injects the browser tool 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 standard browser element. Use browser when you want the stock Browserless pool; use chromeless when 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 browser element (Browserless pool, Pattern-A extension-hosted WebRTC, JPEG screencast intermediary). Migration 0119 wiped any remaining browser rows.

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:

  1. Standalone — Call goto, snapshot, click, fill, type, etc. directly via the API
  2. 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

PropertyTypeDefaultDescription
urlstring""Currently loaded URL (persisted across operations)
viewport_widthu321280Viewport width in pixels (320–3840)
viewport_heightu32720Viewport height in pixels (240–2160)
timeout_msu3230000Page load timeout (1000–120000 ms)
record_interactionsbooleantrueRecord interactions for replay automation
session_idstring""Explicit session ID for sharing (auto-generated if empty)
enginestring"sw"Encoder profile: sw / x264 / vaapi / nvenc / all
backendstring"cloud"Runtime binding: cloud = cloud_browser_worker, personal = Triform Connect
tool_namesstring[]["browser"]Tool names injected when attached to agents

Encoder Profiles

ProfileDescriptionNotes
swSoftware VP8/VP9 (default)Works on every cluster node
x264libx264 H.264Better compression, higher CPU cost
vaapiVAAPI hardware encodeIntel/AMD GPU nodes only
nvencNVENC hardware encodeNVIDIA GPU nodes (triform-5)
allAuto-pick best availableDispatcher resolves at session-create time

Operations

Same CDP / Playwright op surface previously shared with the retired browser element:

CanonicalAliasMethodDescription
gotoPOSTNavigate to a URL; returns { url, title }
snapshotPOSTCapture accessibility tree with [ref=eN] annotations
capturescreenshotPOSTTake a base64 WebP screenshot
sourcePOSTGet raw page HTML via page.content()
clickPOSTClick element by ref (preferred) or CSS selector
typePOSTType text into an input by ref or selector
fillPOSTFill a form field by ref
hoverPOSTHover over an element by ref
press-keyPOSTPress a keyboard key
select-optionPOSTSelect dropdown option by ref
evaluatePOSTRun JavaScript in the page context
wait-forPOSTWait for time / text / text_gone
go-backPOSTHistory back
go-forwardPOSTHistory forward
reloadPOSTReload current page
dialog-accept / dialog-dismissPOSTHandle dialogs
cookie-get / cookie-set / cookie-deletePOSTCookie jar management
tab-listtabsPOSTList all open tabs
tab-newPOSTCreate a new tab
tab-selectPOSTSwitch the active tab
tab-closePOSTClose a tab
tab-close-allPOSTClose all tabs (optionally keeping one)
favouritesPOSTManage saved bookmarks
network-requestsPOSTInspect captured network activity
console-messagesPOSTRead captured browser console output
fetchPOSTHTTP request using the browser’s session and cookies
storagePOSTRead localStorage and sessionStorage for an origin
attach-jar / detach-jarPOSTBond/unbond to a cookie-jar element
save-cookies-to-jar / load-cookies-from-jarPOSTCopy cookies to/from attached jar
playwrightPOSTRaw playwright-cli escape hatch
renderGETServe cached HTML for iframe display (portal-internal)
domPOSTGet latest cached DOM snapshot
interactionsPOSTHuman+agent interaction log

Modifier Behavior

FieldValue
applies_toactors, frontend
cascade_behaviornearest (closest chromeless wins)
fail_actionallow (chromeless failure doesn’t block execution)
evaluation_order50

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

CodeClassRetryableDescription
BROWSER_TIMEOUTtimeoutYesPage load timed out
BROWSER_NAVIGATION_FAILEDinternalYesNavigation 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

PropertyTypeDefaultDescription
tool_namesarray["browser"]Built-in tool names this element provides to attached agents
agent_instructionsstring""Core skill guide injected on first tool use. Teaches the agent how to use browser commands. Source: skills/SKILL.md
skill_referencesobject{}Reference guides for advanced topics. Keyed by topic name, served via the help command.
urlstring""Current URL (must be a valid URI)
start_urlstring"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_widthinteger1280Viewport width in pixels
viewport_heightinteger720Viewport height in pixels
viewerobject{}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_agentstringCustom user agent string
timeout_msinteger30000Page load timeout in milliseconds
record_interactionsbooleantrueRecord all browser interactions for replay automation
session_idstring""Explicit session ID for sharing sessions across elements (auto-generated if empty)
hydration_sourcestring"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 encrypted
cookies + per-origin localStorage from
browser_sessions.session_data (Postgres). What every
existing element gets; no behavioural change unless the
property is flipped explicitly.
. - cas — Phase D path. Reads the gzipped JSON blobs from
the element’s git CAS repo at
state/cookies.json.gz and the storage siblings,
restores cookies via Storage.setCookies BEFORE any
navigation, and registers a per-tab
Page.addScriptToEvaluateOnNewDocument hook that injects
per-origin storage BEFORE first-paint. Falls back to
session_data when the CAS blobs are missing or
unreadable 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_titlestring""Title of the current page (reflects active tab)
tabsarray[]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_idstring""ID of the currently active tab
dom_stateobject{}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_stateobject{}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_pointersobject{}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_tabsarray[]Recently closed tabs (FIFO, max 50) — preserved for reopen
historyarray[]Navigation history for session resumption
backendstring"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_circlebooleanfalseUser-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"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_visibilitystring"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_fingerprintstring""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.
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.
machine_hintstring""Opaque random string the extension generates once per install to stabilise the fingerprint across browser updates.
first_seen_atstring""UTC timestamp when this browser first paired. user-extension backend only.
last_seen_atstring""UTC timestamp of the most recent authenticate / keepalive. user-extension backend only.
connected_replicastring""Hostname of the physics replica currently holding the extension’s WebSocket. Cleared on disconnect. user-extension backend only.
allowed_domainsarray[]Only sync cookies for these domains (empty = all). user-extension backend only.
sync_cookiesbooleantrueAutomatically sync cookies from the connected browser to the server jar. user-extension backend only.
cookiesarray[]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_atstring""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_enabledbooleanfalseUser 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_atstring""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_analysisobject{}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-jar and load-cookies-from-jar start 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_id equals 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=true only 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_id from 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 to source: source calls CDP page.content() live every time, so it’s authoritative but expensive. dom reads what was last captured by the backend’s publish pipeline (any navigate / click / render / content op) — perfect for agents that received a dom_version bump via SSE and want to pull the matching bytes without an extra CDP trip. Cache miss (cold pod, never-loaded tab) returns an empty dom string and dom_version = 0. In that case, call source (live fetch) or render (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 domain filter (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 the tabs field 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 is tab-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, and intention are all independently optional. spec MUST 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 from update) 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 by update_element_meta storage 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

CodeClassRetryableDescription
BROWSER_TIMEOUTtimeoutyesPage load timed out
BROWSER_NAVIGATION_FAILEDinternalyesNavigation 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
  • browserchromeless