Skip to main content

Recording Configuration

Every recording gets its config from three layers. The same schema is used for shared workspace defaults and for the per-recording frontmatter block.

Override Order

OmegaFlow builds the final recording config in this order:

  1. Schema default values.
  2. <recording-dir>/config.yaml, the workspace defaults for recordings.
  3. The per-recording config block in <recording-dir>/<id>/index.md frontmatter.
  4. CLI rec.* overrides, such as rec.capture.headless=false.

Later layers override earlier layers. id and title are recording identity fields; they belong in frontmatter and are rejected in workspace config.yaml.

This page starts after OmegaFlow has selected the recording workspace. Tool-level settings such as which directory to use are documented in Project Configuration.

Command Line Overrides

Use rec.* CLI overrides for temporary changes to the resolved recording config:

omegaflow recording=hello rec.capture.headless=false
omegaflow recording=hello rec.style.typing=false
omegaflow recording=hello rec.audio.enabled=false

rec.* overrides are merged after frontmatter, so they can override values from both config.yaml and the recording header. They are best for scalar values and small config maps. For larger recording structure such as beats, commands, and narration, edit the recording Markdown file instead. Recording identity and generated fields such as id, title, and script cannot be overridden with rec.*.

Composition And Interpolation

OmegaFlow uses OmegaConf syntax for interpolations:

outputs:
dir: recordings/.omegaflow/videos
asset_dir: ${outputs.dir}/${id}

Interpolations are evaluated lazily when the composed config is accessed, not when an individual file or directive block is first parsed. This lets schema defaults, workspace defaults, frontmatter, and directive-derived values refer to the final composed recording object.

All fenced studio-directive blocks in the recording Markdown body are parsed as config fragments and folded into the same recording object. For example, beat directives contribute to beats, and the scene/narration directives contribute to generated narration config. Because directive blocks are combined before interpolation resolution, references can use values from the final recording config rather than only values from the local block.

Workspace Defaults

The workspace config.yaml is good for defaults that should apply to many recordings:

capture:
window_size: 80x20
headless: true
style:
color: true
typing: true
outputs:
dir: recordings/.omegaflow/videos
audio:
enabled: false
provider: openai
env: OPENAI_OMEGAFLOW_API_KEY
model: gpt-4o-mini-tts
voice: marin
format: mp3

capture.timeout limits how long OmegaFlow waits for one terminal request, including a command or check. The default is 30 seconds; increase it for an intentional realtime action that runs longer.

Enabling narration requires FFmpeg tools and OpenAI API access when generating new audio. Put the local key in .omegaflow/omegaflow-secret.env; CI may provide the same name in the parent process environment. env_file remains an explicit advanced override and is loaded without modifying the process environment.

Recording Frontmatter

Each <id>/index.md recording starts with YAML frontmatter:

---
kind: video
id: hello
title: Hello Video
description: A small narrated hello-world recording.
publish:
default: html
surfaces:
html:
type: standalone_html
file: ${outputs.asset_dir}/index.html
audio:
enabled: false
---

The frontmatter header is the right place for recording-specific config:

  • kind, id, title, and description
  • one-off output overrides
  • one-off audio settings
  • recording-local setup, cleanup, or configured beats
  • publish target choices for that recording

Structure

FieldTypeNotes
kindvideo or collectionSource type. Omitted values default to video; declare kind: collection for a collection. Collections use only id, title, and members.
idstringRequired per recording. Used by omegaflow recording=<id>. Nested ids such as tutorial/install are supported. Frontmatter only.
titlestringHuman-readable title for players and publish surfaces. Frontmatter only.
descriptionstringShort summary used when a collection renders its watch index. Frontmatter only.
parametersmappingScript parameters and defaults for script_params.
requirementsmappingRequired shell commands and tools.
capturemappingRecording settings such as window_size, headless, and idle_time_limit.
stylemappingRendering behavior such as color and typing simulation.
outputsmappingOutput paths for the per-recording asset and presentation-bundle directories.
timingmappingPresentation timing and playback controls.
environmentmappingWorking directory, literal values, declared application secrets, and path_prepend.
audiomappingNarration audio configuration.
browsermapping or nullDeterministic Playwright capture profile, viewport, context, authentication, timeouts, and redaction targets. Required when any beat has medium: browser.
presentationmappingRecording-wide browser window, chrome, transition, pointer, and typing presentation policy.
publishmappingPublish surfaces such as Docusaurus MDX and standalone HTML.
setuplistCommands that run before beats. See Recording schema.
cleanuplistCommands that run after recording. See Recording schema.
beatslistOptional configured beats. See Recording schema.

A collection replaces the video-specific fields with an ordered members list of full recording ids. Collection members must be videos; nested collections are not supported. Each member's title and description appear in the collection watch index.

Publishing surface details are covered in Publishing And Runtime.

Recorded command environment

OmegaFlow constructs a recording environment instead of copying the process environment from the machine running the build. This keeps terminal commands, setup, cleanup, checks, and browser capture independent from unrelated host settings.

Use the typed environment fields to provide deliberate application inputs:

environment:
working_directory: examples/demo
path_prepend:
- tools/bin
variables:
DEMO_MODE: tutorial
  • working_directory selects the directory used by recorded commands.
  • path_prepend adds project-relative command locations ahead of OmegaFlow's deterministic command path.
  • variables supplies literal, non-secret application settings.

Application secrets

Declare each secret application input by name:

environment:
secrets:
- DEMO_API_TOKEN

For local recording, put the value in app.secret.env beside that recording's index.md:

DEMO_API_TOKEN=local-token

For CI, omit the file and set the declared name in OmegaFlow's parent process environment instead. Exactly one source must provide each declared name. A missing value, a value present in both sources, or an undeclared entry in app.secret.env stops the build.

Inside a Git or Sapling repository, OmegaFlow requires a local app.secret.env to be ignored and untracked. It also rejects a symbolic link. Bootstrap adds **/app.secret.env to recordings/.gitignore. Secret values are available to recording setup, actions, checks, cleanup, and browser processes, but are registered for output redaction and publication validation. A secret-bearing recording is always captured again rather than reusing a previous capture.

Application secrets are inputs for the software being recorded. OmegaFlow service credentials such as OPENAI_OMEGAFLOW_API_KEY belong in .omegaflow/omegaflow-secret.env and cannot be declared here. Other host environment variables are not inherited by recorded commands.

OmegaFlow also sets OMEGAFLOW_VERSION to the version performing the recording. Values configured under environment.variables are not printed by OmegaFlow, but this mapping is not secret storage: recorded applications can read and display them.

Browser header configuration

Browser capture parameters are recording-wide because every browser beat uses one persistent page and deterministic viewport:

environment:
variables:
DEMO_STORAGE_STATE: .private/demo-storage-state.json
browser:
base_url: http://127.0.0.1:3000
viewport:
width: 1280
height: 720
device_scale_factor: 1
context:
locale: en-US
timezone: UTC
color_scheme: light
reduced_motion: reduce
auth:
storage_state_env: DEMO_STORAGE_STATE
timeouts:
action_ms: 10000
readiness_ms: 15000

storage_state_env names an explicitly configured environment.variables entry whose value is a private Playwright storage-state path. Use storage_state_path instead when the path is safe to keep directly in browser config. The file content remains private and its hash, not its secrets, participates in capture freshness.

Presentation framing in the recording header supplies the defaults for every browser beat:

presentation:
guided: true
pane_chrome:
style: framed
browser:
window:
mode: framed
theme: kde-breeze
title: Demo application
opening_transition: window-open
chrome:
mode: minimal
transitions:
default: fade

The captured viewport never changes during playback. The renderer scales and letterboxes it inside any selected window frame. Set guided: true to start the player in guided mode; beats with guide content then pause before the following beat is rendered. See Recording schema for checkpoint and toolbar-highlight authoring. Multi-pane layouts use pane_chrome.style: framed by default, which adds pane labels, borders, and renderer-colored accents. Set the style to none for an undecorated layout. Pane declarations and titles are body authoring rather than frontmatter configuration; see Multi-pane Beats. Individual browser beats can override the window and browser chrome modes; see Browser beats.

Config Schema

This schema block is generated from src/omegaflow/studio_config.py during the website build. Beat, command, and publish detail types are documented on the Recording schema and Publishing And Runtime pages.

Config schema
@dataclass
class RecordingCaptureConfig:
window_size: str = "100x28"
headless: bool = True
idle_time_limit: float | None = None
timeout: float = 30.0


@dataclass
class RecordingStyleConfig:
color: bool = True
typing: bool = True
typing_min_delay: float = 0.012
typing_max_delay: float = 0.045
typing_space_delay: float = 0.025
typing_punctuation_delay: float = 0.05
typing_newline_delay: float = 0.16
typing_seed: int = 17


@dataclass
class RecordingOutputsConfig:
dir: str = "recordings/.omegaflow/videos"
asset_dir: str = "${outputs.dir}/${id}"


@dataclass
class RecordingTimingConfig:
typing_char_delay: float = 0.035
typing_space_delay: float = 0.02
typing_punctuation_delay: float = 0.05
typing_newline_delay: float = 0.0
post_enter_pause: float = 0.35
post_command_pause: float = 0.85
minimum_section_spacing: float = 0.0


@dataclass
class RecordingEnvironmentConfig:
working_directory: str = "."
path_prepend: list[str] = field(default_factory=list)
variables: dict[str, str] = field(default_factory=dict)
secrets: list[str] = field(default_factory=list)


@dataclass
class RecordingAudioBillingConfig:
tts_usd_per_1m_characters: float = 15.0
transcription_usd_per_minute: float = 0.006


@dataclass
class RecordingAudioTranscriptionConfig:
model: str = "whisper-1"
timestamp_granularities: list[str] = field(default_factory=lambda: ["word"])


@dataclass
class RecordingAudioConfig:
enabled: bool = False
provider: str = "openai"
env: str = "OPENAI_OMEGAFLOW_API_KEY"
model: str = "gpt-4o-mini-tts"
voice: str = "marin"
format: str = "mp3"
cache_dir: str = "recordings/.omegaflow/cache/audio"
env_file: str | None = None
env_override: bool = False
instructions: str | None = None
billing: RecordingAudioBillingConfig = field(
default_factory=RecordingAudioBillingConfig
)
transcription: RecordingAudioTranscriptionConfig = field(
default_factory=RecordingAudioTranscriptionConfig
)


@dataclass
class BrowserViewportConfig:
width: int | None = None
height: int | None = None
device_scale_factor: float | None = None


@dataclass
class BrowserContextConfig:
locale: str | None = None
timezone: str | None = None
color_scheme: str | None = None
reduced_motion: str | None = None
permissions: list[str] | None = None


@dataclass
class BrowserAuthConfig:
storage_state_env: str | None = None
storage_state_path: str | None = None


@dataclass
class BrowserTimeoutsConfig:
action_ms: int = 10_000
readiness_ms: int = 15_000


@dataclass
class BrowserRedactionConfig:
target: BrowserTargetConfig = field(default_factory=BrowserTargetConfig)


@dataclass
class BrowserRecordingConfig:
profile: str = "desktop-v1"
base_url: str | None = None
viewport: BrowserViewportConfig | None = None
context: BrowserContextConfig | None = None
auth: BrowserAuthConfig = field(default_factory=BrowserAuthConfig)
timeouts: BrowserTimeoutsConfig = field(default_factory=BrowserTimeoutsConfig)
redactions: list[BrowserRedactionConfig] = field(default_factory=list)


@dataclass
class BrowserWindowModeConfig:
mode: str = "none"


@dataclass
class BrowserWindowPresentationConfig(BrowserWindowModeConfig):
theme: str = "kde-breeze"
title: str | None = None
opening_transition: str = "cut"


@dataclass
class BrowserChromePresentationConfig:
mode: str = "hidden"


@dataclass
class BrowserTransitionsPresentationConfig:
default: str = "cut"


@dataclass
class BrowserPointerPresentationConfig:
visible: bool = True


@dataclass
class BrowserTypingPresentationConfig:
policy: str = "natural-v1"


@dataclass
class BrowserPresentationConfig:
window: BrowserWindowPresentationConfig = field(
default_factory=BrowserWindowPresentationConfig
)
chrome: BrowserChromePresentationConfig = field(
default_factory=BrowserChromePresentationConfig
)
transitions: BrowserTransitionsPresentationConfig = field(
default_factory=BrowserTransitionsPresentationConfig
)
pointer: BrowserPointerPresentationConfig = field(
default_factory=BrowserPointerPresentationConfig
)
typing: BrowserTypingPresentationConfig = field(
default_factory=BrowserTypingPresentationConfig
)


class PaneChromeStyle(str, Enum):
none = "none"
framed = "framed"


@dataclass
class RecordingPaneChromeConfig:
style: PaneChromeStyle = PaneChromeStyle.framed


@dataclass
class RecordingPresentationConfig:
guided: bool = False
pane_chrome: RecordingPaneChromeConfig = field(
default_factory=RecordingPaneChromeConfig
)
browser: BrowserPresentationConfig = field(default_factory=BrowserPresentationConfig)


@dataclass
class RecordingFailureAnimationConfig:
regex: str = ""
replacement: str = ""


@dataclass
class RecordingFailureSummaryConfig:
terminal_animations: list[RecordingFailureAnimationConfig] = field(
default_factory=list
)


@dataclass
class RecordingRequirementsConfig:
commands: list[str] = field(default_factory=list)


@dataclass
class RecordingNarrationConfig:
id: str = "voiceover"


@dataclass
class RecordingDefaults:
parameters: dict[
str,
str | int | float | bool | dict[str, str | int | float | bool],
] = field(default_factory=dict)
requirements: RecordingRequirementsConfig = field(
default_factory=RecordingRequirementsConfig
)
capture: RecordingCaptureConfig = field(default_factory=RecordingCaptureConfig)
style: RecordingStyleConfig = field(default_factory=RecordingStyleConfig)
outputs: RecordingOutputsConfig = field(default_factory=RecordingOutputsConfig)
timing: RecordingTimingConfig = field(default_factory=RecordingTimingConfig)
environment: RecordingEnvironmentConfig = field(
default_factory=RecordingEnvironmentConfig
)
audio: RecordingAudioConfig = field(default_factory=RecordingAudioConfig)
browser: BrowserRecordingConfig | None = None
presentation: RecordingPresentationConfig = field(
default_factory=RecordingPresentationConfig
)
publish: RecordingPublishConfig = field(default_factory=RecordingPublishConfig)
failure_summary: RecordingFailureSummaryConfig = field(
default_factory=RecordingFailureSummaryConfig
)
narration: RecordingNarrationConfig = field(
default_factory=RecordingNarrationConfig
)
setup: list[RecordingStepConfig] = field(default_factory=list)
cleanup: list[RecordingStepConfig] = field(default_factory=list)
beats: list[RecordingBeatConfig] = field(default_factory=list)


@dataclass
class RecordingSourceSpec(RecordingDefaults):
kind: RecordingSourceKind = RecordingSourceKind.video
id: str = ""
title: str | None = None
description: str | None = None


@dataclass
class RecordingCollectionSourceSpec:
kind: RecordingSourceKind = RecordingSourceKind.collection
id: str = ""
title: str | None = None
members: list[str] = field(default_factory=list)