Recorder
A receive-only camera on a live Chromeless browser session: it ingests the cb-chromium WebRTC track verbatim, muxes it into durable WebM segments in a `files` store, and projects the session's audit timeline into a replayable `events.jsonl` — so what an agent or user saw can be watched back later.
Working with it
Selecting a Recorder reveals its settings in the properties panel; it has no dedicated full-screen workbench.
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
- Capturing what a Chromeless / cb-chromium browser actually rendered during an automated or agent-driven session, for replay or audit.
- Bracketing a flow step with start/stop so a specific stretch of browser work lands as a durable recording you can list and read back.
- Producing a timestamped event timeline (clicks, navigations, marks) alongside the video, via append-event / append-events into the recording's audit slice.
- Attaching to a browser-facing element (chromeless, user-browser, view, spa) as a fail-open modifier whose failure never blocks the work it observes.
When not to use
- Driving or scripting the browser itself — recorder only observes; use chromeless (or user-browser) to navigate and act.
- Storing arbitrary blobs or general project artifacts — write those straight to a files element; recorder owns only the recordings/ prefix.
- Capturing a still frame or a one-off screenshot — recorder produces continuous WebM segments, not a single image.
Topology
Attaches to another element as a modifier, shaping that element's behaviour rather than running on its own.
Properties
segment_target_duration_msinteger- Target wall-clock duration of a single WebM segment, in milliseconds. The muxer closes the current segment and opens a new one whenever elapsed time crosses this threshold AND it lands on a keyframe boundary, so actual durations skew slightly larger on low-keyframe content. Lower values shrink the "last segment lost on crash" window at the cost of more files in the recordings store.
size_cap_bytesinteger- Soft cap on the total bytes occupied by `recordings/` in this recorder's files store. When exceeded, the reaper evicts the oldest finalized recordings (whole sessions) until the prefix is back under the cap. In-flight `recording` sessions are NEVER evicted — only `finalized` or `failed` ones. Default 5 GiB at runtime (per `recorder-reaper-defaults.md` — sized below the ENOSPC blast radius demonstrated by reform-receiver PVC fill incidents).
ttl_daysinteger- Maximum age in days for a finalized recording. The reaper removes any recording whose `ended_at` is older than this. Combined with `size_cap_bytes` via OR — the older of the two wins, so a recording evicts as soon as EITHER its age exceeds ttl_days OR the cap pushes it off the tail. Placeholder default (14 days); coordinate with store-builder before tuning.
files_element_refstring- Default files element (slug or UUID) this recorder writes recordings into. When empty, every `start` op falls back to the circle's primary files element. Per-op `files_element_ref` overrides this.
Capabilities
Defined for this element
- Webrtc
- Compute
Operations
- activityGET
- append-eventPOST
- append-eventsPOST
- attachPOST
- attachmentsGET
- batch_statsGET
- composePOST
- contextGET
- createPOST
- deleteDELETE
- detachPOST
- disablePOST
- enablePOST
- exportPOST
- export_bundleGET
- getGET
- import_bundlePOST
- intentionGET
- invokePOST
- listPOST
- list_attachmentsGET
- logsGET
- mcp_tool_defGET
- promotePOST
- readmeGET
- readme_updatePOST
- remove-modifierPOST
- resetPOST
- restorePOST
- run_getGET
- runsGET
- schemaGET
- sourceGET
- source_branchesGET
- source_promotePOST
- source_repairPOST
- source_statusGET
- source_validatePOST
- startPOST
- statsGET
- statusPOST
- stopPOST
- treeGET
- updatePATCH
- update_metaPATCH
- versionGET
Ports
Inputs
- startrequest
- manifestrequest
Composition
Recorder (recorder)
Category: tools | Form: | Symbol: Rc
Records native Chromeless WebRTC browser sessions to durable WebM segments + a finalized event timeline in a files store, replayable later.
Tool element shaped like
chromeless— simultaneously an attachable modifier AND a thing with invokable ops. Records what an agent or user saw inside a chromeless session: a receive-only webrtc-rs peer joins the existing Pattern C broker as theclientrole, ingests the cb-chromium browser track verbatim (no decode, no re-encode), and muxes WebM segments into the circle’sfilesstore underrecordings/{session_id}/. Finalize writes a manifest +events.jsonlprojection of the recording’s Iggy audit slice. v1 ships explicitstart/stopops only — no auto-start-on-attach. Theappend-event/append-eventsops auto-emit PlatformEvents to the Iggy spine via the dispatcher chokepoint (per universal-events.md). MP4 export is async and out of band. fail_action is “allow” so a recorder failure never blocks the attached element.
Guide
Records native Chromeless WebRTC browser sessions to durable WebM segments + a finalized event timeline in a
filesstore, replayable later
What It Does
Recorder is a tool element shaped like chromeless — it is simultaneously an attachable modifier and a thing with invokable ops. It records what an agent or user saw inside a Chromeless session: a receive-only webrtc-rs peer joins the existing Pattern C broker as the client role, ingests the cb-chromium browser track verbatim (no decode, no re-encode), and muxes WebM segments into the circle’s files store under recordings/{session_id}/.
You bracket a recording with the start and stop ops from a flow step. start allocates a session_id derived from (circle_id, browser_element_id), resolves the target files element, writes an initial RecordingManifest (status='recording'), and begins muxing the VP8/Opus track into segments under recordings/{session_id}/segments/. stop closes the receiver peer, finalizes the current segment, sets status='finalized', and writes events.jsonl — a timestamp-ordered projection of the recording’s Iggy audit slice. v1 ships explicit start/stop only; there is no auto-start-on-attach.
The append-event / append-events ops push entries onto the recording’s Iggy audit slice; the dispatcher chokepoint auto-emits a PlatformEvent on every op completion, which is exactly what feeds the audit_stream that stop projects into events.jsonl. MP4 export is async and out of band via the export op. As a modifier, fail_action is allow, so a recorder failure never blocks the attached element.
Element Definition
| Property | Value |
|---|---|
| Type | recorder |
| Category | tools |
| Form | modifier (modifier_type: recorder) |
| Symbol | Rc / #EF4444 |
| Icon | fiber_manual_record |
As a modifier it applies_to: [actors, frontend], cascades to the nearest instance, evaluates in the request phase (evaluation_order: 50), and on failure takes fail_action: allow. It attaches to: chromeless, user-browser, view, spa, ssr, function, triformer, hitl. It uses the files and chromeless elements.
Properties
| Field | Type | Default | Description |
|---|---|---|---|
segment_target_duration_ms | integer | 4000 | Target wall-clock duration of a single WebM segment, in ms (500–60000). The muxer rotates on a keyframe boundary past this threshold. |
size_cap_bytes | integer | 0 | Soft cap on total bytes under recordings/ in this recorder’s files store. 0 = use the runtime ReaperPolicy default. In-flight recording sessions are never evicted. |
ttl_days | integer | 14 | Maximum age in days for a finalized recording (1–365). The reaper removes recordings whose ended_at is older than this. Combined with size_cap_bytes via OR. |
files_element_ref | string | "" | Default files element (slug or UUID) recordings are written into. Empty falls back to the circle’s primary files element. Per-op files_element_ref overrides it. (maxLength 128) |
active_session_ids | array | [] | Session IDs currently in recording status. Read-only; the executor maintains it. |
last_started_at | string (date-time) | "" | ISO 8601 timestamp of the most recent start op. |
last_finalized_at | string (date-time) | "" | ISO 8601 timestamp of the most recent stop (finalize) op. |
Ports
| Direction | Port | Required | Description |
|---|---|---|---|
| Input | start | — | Start a recording of a chromeless WebRTC session (StartInput). |
| Output | manifest | — | Current or finalized recording manifest (RecordingManifest). |
Capabilities
| Capability | Description |
|---|---|
executable | Element can be invoked to execute operations. |
webrtc-receive | Receive-only webrtc-rs peer joins the Pattern C broker as the client role. |
webm-mux | Mux received VP8/Opus RTP samples into WebM segments without decode/re-encode. |
files-backed-artifact | Persists recordings into a files element store under recordings/{session_id}/. |
event-timeline | Finalize projects the recording’s Iggy audit slice into a durable events.jsonl. |
size-cap-reaper | Enforces a configurable size cap + TTL on the recordings prefix. |
mp4-export | Async out-of-band MP4 export from finalized WebM segments. |
Error Codes
| Code | Class | Retryable | Description |
|---|---|---|---|
RECORDER_NO_LIVE_BROWSER | not_found | No | No live chromeless session was found for the given browser_element_id. |
RECORDER_BROKER_BUSY | conflict | No | The Pattern C broker is strictly 1:1; another client peer is already attached to this session. |
RECORDER_FILES_RESOLVE_FAILED | not_found | No | Could not resolve a target files element (neither provided ref nor circle default). |
RECORDER_STORE_FULL | resource_exhausted | No | The files store hit its size cap; the reaper could not free room for new segments. |
RECORDER_FINALIZE_FAILED | internal | Yes | Manifest finalize + events.jsonl projection write failed. |
Operations
start
POST start — auth: write. Starts a recording of a chromeless WebRTC session. Allocates a session_id from (circle_id, browser_element_id), resolves the target files element, writes the initial manifest (status='recording', empty segments), joins the Pattern C broker as client, and begins muxing into recordings/{session_id}/segments/. Idempotent on session_id — re-calling start for an already-recording session returns the existing session_id without disrupting capture.
- Input:
browser_element_id(required),files_element_ref(optional). - Output:
session_id,started_at.
stop
POST stop — auth: write. Stops a recording and finalizes the manifest + events.jsonl. Sends Bye {from:"client"}, closes the receiver peer, finalizes the current segment, sets ended_at, status='finalized', and audit_stream.offset_end, then writes events.jsonl. Safe to call on an already-finalized session — returns the persisted manifest unchanged.
- Input:
session_id(required). - Output: the finalized
RecordingManifest.
status
POST status — auth: read. Reads the current or finalized RecordingManifest for a session exactly as persisted at recordings/{session_id}/manifest.json. While status='recording', the segments array grows as segments close.
- Input:
session_id(required). - Output: the
RecordingManifest.
list
POST list — auth: read. Lists recordings in this recorder’s files store, newest first, returning the full RecordingManifest for each entry.
- Input:
limit(optional, default50, max500),since(optional, RFC 3339 — only recordings withstarted_atstrictly greater). - Output:
recordings(array ofRecordingManifest).
export
POST export — auth: write. Kicks off an async post-finalize export (currently MP4 only). Returns immediately with export_status='pending' and a stable path under recordings/{session_id}/exports/. Only callable on status='finalized' sessions — errors on still-recording or failed sessions.
- Input:
session_id(required),format(required, enum["mp4"]). - Output:
export_status(pending|in_progress|ready|failed),path.
append-event
POST append-event — auth: write. Appends a single event entry to the recording’s audit timeline (Iggy slice, subject recorder.append-event.completed). The dispatcher chokepoint auto-emits the matching PlatformEvent on op completion, feeding the audit_stream that finalize projects into events.jsonl. (UI section: hidden.)
- Input:
session_id,kind(e.g.'click','navigation','mark','annotation'),payload(free-form object),ts(RFC 3339, preserved verbatim) — all required. - Output:
ok(true when published to the Iggy spine; the Iggy publish is the durability boundary).
append-events
POST append-events — auth: write. Batch variant of append-event. All items are published atomically into the recording’s Iggy audit slice, in order. Cap on batch size is enforced by the platform’s PlatformEvent batcher. (UI section: hidden.)
- Input:
session_id(required),items(required array, minItems 1; each itemkind,payload,ts). - Output:
ok,accepted(number of items the platform accepted).
Quick Start
Create a recorder
POST /api/{circle}/{project}/
Content-Type: application/json
{
"element_type": "recorder",
"slug": "session-recorder",
"name": "Session Recorder",
"spec": {
"segment_target_duration_ms": 4000,
"ttl_days": 14
}
}
Start, then stop a recording
POST /api/{circle}/session-recorder/ops/start
Content-Type: application/json
{
"browser_element_id": "<chromeless-element-uuid>"
}
The response carries the session_id and started_at. Bracket the work you want captured, then finalize:
POST /api/{circle}/session-recorder/ops/stop
Content-Type: application/json
{
"session_id": "<session-id-from-start>"
}
stop returns the finalized RecordingManifest, including the segments[] list and the events_jsonl_path. Read it back any time with status, or browse all recordings with list.
Common Mistakes
Passing a session_id to start.
start takes browser_element_id, not session_id — it allocates the session id from (circle_id, browser_element_id) and returns it. Every other op (stop, status, export, append-event, append-events) takes that returned session_id.
Recording a browser with no live session.
start requires a live chromeless WebRTC session for the given browser_element_id; otherwise it returns RECORDER_NO_LIVE_BROWSER. The Pattern C broker is strictly 1:1 — a second client peer on the same session returns RECORDER_BROKER_BUSY.
Exporting before finalize.
export is only callable on a status='finalized' session and supports format: "mp4" only. Calling it on a still-recording or failed session returns an error.
Expecting events.jsonl mid-recording.
events.jsonl is written only at finalize (stop). While status='recording', observe progress by tailing the status op or watching the recorder’s PlatformEvents on the Iggy spine — the manifest’s segments[] grows as segments close.
Relationships
- Attaches to: chromeless, user-browser, view, spa, ssr, function, triformer, hitl
- Uses: files, chromeless
Capabilities
- executable: Element can be invoked to execute operations
- webrtc-receive: Receive-only webrtc-rs peer joins the Pattern C broker as the
clientrole - webm-mux: Mux received VP8/Opus RTP samples into WebM segments without decode/re-encode
- files-backed-artifact: Persists recordings into a
fileselement store under recordings/{session_id}/ - event-timeline: Finalize projects the recording’s Iggy audit slice into a durable events.jsonl
- size-cap-reaper: Enforces a configurable size cap + TTL on the recordings prefix
- mp4-export: Async out-of-band MP4 export from finalized WebM segments
Properties
| Property | Type | Default | Description |
|---|---|---|---|
segment_target_duration_ms | integer | 4000 | Target wall-clock duration of a single WebM segment, in milliseconds. The muxer closes the current segment and opens a new one whenever elapsed time crosses this threshold AND it lands on a keyframe boundary, so actual durations skew slightly larger on low-keyframe content. Lower values shrink the “last segment lost on crash” window at the cost of more files in the recordings store. |
size_cap_bytes | integer | 0 | Soft cap on the total bytes occupied by recordings/ in this recorder’s files store. When exceeded, the reaper evicts the oldest finalized recordings (whole sessions) until the prefix is back under the cap. In-flight recording sessions are NEVER evicted — only finalized or failed ones. Default 5 GiB at runtime (per recorder-reaper-defaults.md — sized below the ENOSPC blast radius demonstrated by reform-receiver PVC fill incidents). |
ttl_days | integer | 14 | Maximum age in days for a finalized recording. The reaper removes any recording whose ended_at is older than this. Combined with size_cap_bytes via OR — the older of the two wins, so a recording evicts as soon as EITHER its age exceeds ttl_days OR the cap pushes it off the tail. Placeholder default (14 days); coordinate with store-builder before tuning. |
active_session_ids | array | [] | Session IDs currently in recording status. Cleared as each session finalizes. Read-only from the user surface; the executor maintains it. |
last_started_at | string | "" | ISO 8601 timestamp of the most recent start op for this recorder. |
last_finalized_at | string | "" | ISO 8601 timestamp of the most recent stop op (finalize) for this recorder. |
files_element_ref | string | "" | Default files element (slug or UUID) this recorder writes recordings into. When empty, every start op falls back to the circle’s primary files element. Per-op files_element_ref overrides this. |
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).
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.
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.
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.
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
Post /ops/export | Auth: Write
Kick off an async post-finalize export (currently MP4 only)
Schedules an out-of-band export from the finalized WebM segments into the requested container/codec. v1 supports
format: "mp4"only. Returns immediately withexport_status='pending'and a stablepathunder recordings/{session_id}/exports/ once the worker has reserved a slot. Subsequent calls tostatusreflect export progress in the manifest’sexports[]array. Only callable onstatus='finalized'sessions; returns an error on still-recording or failed sessions.
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.
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.
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.
list
Post /ops/list | Auth: Read
List recordings in this recorder element’s files store
Walks the recordings/ prefix in the resolved files store and returns the full RecordingManifest for each entry, newest first.
limitcaps the number of returned manifests (default 50, max 500).sincefilters to recordings whosestarted_atis strictly greater than the provided RFC 3339 timestamp.
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.
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)
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.
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
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.
source
Get /ops/source | Auth: Read
Get any file’s content from the element’s git repository
Reads an arbitrary file from the element’s CAS-backed git tree by its relative path. Same store as
readme, just generalized. Path safety: rejects..traversal, leading/, and null bytes. Use this to viewmain.pyfor action elements, asset files for SPAs, etc. Returns empty content (not an error) if the file doesn’t exist.
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.
start
Post /ops/start | Auth: Write
Start a recording of a chromeless WebRTC session
Allocates a session_id derived from (circle_id, browser_element_id), resolves the target files element (
files_element_refor circle default), writes the initial RecordingManifest (status=‘recording’, empty segments), joins the Pattern C broker as theclientrole, and begins muxing the VP8/Opus track into WebM segments under recordings/{session_id}/segments/. Idempotent on session_id: re-calling start for an already-recording session returns the existing session_id without disrupting capture.
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.
status
Post /ops/status | Auth: Read
Read the current or finalized RecordingManifest for a session
Returns the full manifest exactly as persisted in the files store under recordings/{session_id}/manifest.json. While
status='recording'the segments array grows as segments close; consumers tail this op or watch the recorder’s PlatformEvents on the Iggy spine.
stop
Post /ops/stop | Auth: Write
Stop a recording and finalize the manifest + events.jsonl
Sends
Bye {from:"client"}to the broker, closes the receiver peer, finalizes the current segment, setsended_at+status='finalized'+audit_stream.offset_end, then writesevents.jsonlas the projection of the recording’s Iggy audit slice. Returns the finalized RecordingManifest. Safe to call on an already-finalized session — returns the persisted manifest unchanged.
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.
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.
Error Codes
| Code | Class | Retryable | Description |
|---|---|---|---|
RECORDER_NO_LIVE_BROWSER | not_found | no | No live chromeless session was found for the given browser_element_id |
RECORDER_BROKER_BUSY | conflict | no | The Pattern C broker is strictly 1:1; another client peer is already attached to this session |
RECORDER_FILES_RESOLVE_FAILED | not_found | no | Could not resolve a target files element (neither provided ref nor circle default) |
RECORDER_STORE_FULL | resource_exhausted | no | The files store hit its configured size cap; the reaper could not free room for new segments |
RECORDER_FINALIZE_FAILED | internal | yes | Manifest finalize + events.jsonl projection write failed |
Observability
Defined for this element
Metrics
- invocation_count
- duration_ms
- error_rate
- recorder_session_started_count
- recorder_session_finalized_count
- recorder_session_failed_count
- recorder_session_duration_ms
- recorder_segment_count
- recorder_segment_bytes
- recorder_segment_duration_ms
- recorder_segment_write_latency_ms
- recorder_append_event_count
- recorder_finalize_latency_ms
- recorder_reaper_evicted_count
- recorder_store_bytes
Events
- recorder.completed
- recorder.failed
- recorder.start.completed
- recorder.stop.completed
- recorder.session.failed
- recorder.segment.closed
- recorder.append-event.completed
- recorder.append-events.completed
- recorder.reaper.evicted
Pricing / cost
Inherited from tools
Operation costs
- tool_use: free