# Configuration ## Overview Narratio loads three YAML files: - `pipeline.yml`: pipeline-level runtime settings. - `campaign.yml`: stable campaign identity and campaign-level input defaults. - `session.yml`: per-session metadata and input selection. Commands that load and validate all three files include: - `narratio run` - `narratio resume` - `narratio run-stage` - `narratio analyze` - `narratio publish` - `narratio session plan` - `narratio session status` - `narratio session validate` - `narratio session restore` - `narratio session artifacts` - `narratio session locks` - `narratio clean ` Validation behavior: - strict YAML decode is enabled (`KnownFields(true)`); unknown fields fail. - loaded `session.yml` files must be concrete YAML (no `{{ ... }}` placeholders). - defaults are applied for optional pipeline fields. - campaign/session identity mismatches fail load. ## File Discovery Pipeline discovery order when `--config` is omitted: 1. `/usr/local/etc/narratio/pipeline.yml` 2. `/etc/narratio/pipeline.yml` Session discovery order when `--session` is omitted: 1. `/usr/local/etc/narratio/session.yml` 2. `/etc/narratio/session.yml` Campaign discovery when `--campaign-file` is omitted: - if `--campaign ` is set: `{pipeline.campaigns.root}/{id}/campaign.yml` - otherwise: `{pipeline.campaigns.root}/{pipeline.campaigns.default_campaign_id}/campaign.yml` Remote `session.yml` fallback: - if local session discovery fails and storage is configured, Narratio can load: - `{root_prefix}/campaigns/{campaign}/sessions/{session_id}/session.yml` ## Minimal Working Config `pipeline.yml` ```yaml campaigns: root: /usr/local/share/narratio/campaigns default_campaign_id: sample-campaign whisperx: transcribe_url: "https://transcription.example.com/transcribe" ``` `campaign.yml` ```yaml campaign_id: sample-campaign inputs: speakers_file: ./speakers.yml autocorrect_file: ./autocorrect.yml glossary_file: ./glossary.yml ``` `session.yml` ```yaml session_id: 2026-05-03 inputs: audio_dir: ./audio ``` ## Publish Config Top-level publish settings live at `pipeline.publish`. ```yaml publish: enabled: true upload_run: true outputs: - source: narratio.transcript.final_trimmed dest: transcripts/final.trimmed.json required: true - source: narratio.artifact.session_recap dest: artifacts/session_recap.md required: true locks: - source: narratio.artifact.session_recap reason: Final recap was manually edited. ``` Rules: - `outputs[].source` is required. - `outputs[].dest` is optional; when omitted, Narratio derives destination from the source. - `outputs[].required` defaults to `true`. - static `publish.locks` and remote `{session_prefix}/locks.yml` are merged; static locks win on duplicates. - locks prevent overwrite of top-level published destinations. Supported publish source families: - built-ins: `narratio.transcript.base`, `narratio.transcript.polished`, `narratio.transcript.final`, `narratio.transcript.final_trimmed`, `narratio.bounds.session` - configured artifacts: `narratio.artifact.` ## Full Reference ### Pipeline | Path | Type | Required | Default | | --- | --- | --- | --- | | `pipeline.workspace.root` | string | No | `/var/lib/narratio` | | `pipeline.workspace.cleanup_after_publish` | bool | No | `false` | | `pipeline.campaigns.root` | string | No | `/usr/local/share/narratio/campaigns` | | `pipeline.campaigns.default_campaign_id` | string | No | empty | | `pipeline.secrets.env_dir` | string | No | empty | | `pipeline.storage.backend` | string | No | empty | | `pipeline.storage.s3.bucket` | string | Conditional | empty | | `pipeline.storage.s3.root_prefix` | string | No | `dnd` | | `pipeline.storage.s3.region` | string | No | empty | | `pipeline.storage.s3.endpoint` | string | No | empty | | `pipeline.storage.s3.force_path_style` | bool | No | `false` | | `pipeline.storage.s3.access_key_id_env` | string | No | `OBJECT_STORAGE_KEY_ID` | | `pipeline.storage.s3.secret_access_key_env` | string | No | `OBJECT_STORAGE_KEY` | | `pipeline.spool.root` | string | No | `/var/spool/narratio` | | `pipeline.spool.delete_audio_after_publish` | bool | No | `false` | | `pipeline.cache.root` | string | No | `/var/cache/narratio` | | `pipeline.cache.s3_audio` | bool | No | `true` | | `pipeline.publish.enabled` | bool | No | `true` | | `pipeline.publish.upload_run` | bool | No | `true` | | `pipeline.publish.outputs[]` | list | No | one final-trimmed output rule | | `pipeline.publish.outputs[].source` | string | Yes (per rule) | none | | `pipeline.publish.outputs[].dest` | string | No | derived from source | | `pipeline.publish.outputs[].required` | bool | No | `true` | | `pipeline.publish.locks[]` | list | No | empty | | `pipeline.publish.locks[].source` | string | Yes (per lock) | none | | `pipeline.publish.locks[].reason` | string | No | empty | | `pipeline.whisperx.transcribe_url` | string | Yes | none | | `pipeline.whisperx.language` | string | No | `en` | | `pipeline.whisperx.timeout` | duration string | No | `30m` | | `pipeline.whisperx.retries` | int | No | `3` | | `pipeline.whisperx.retry_delay` | duration string | No | `2s` | | `pipeline.whisperx.concurrency` | int | No | `2` | | `pipeline.seriatim.binary` | string | No | `seriatim` | | `pipeline.seriatim.timeout` | duration string | No | `10m` | | `pipeline.seriatim.output_schema` | string | No | `seriatim-intermediate` | | `pipeline.seriatim.coalesce_gap` | float | No | `3.0` | | `pipeline.seriatim.report` | bool | No | `true` | | `pipeline.seriatim.env.overlap_word_run_gap` | float | No | unset | | `pipeline.seriatim.env.overlap_word_run_reorder_window` | float | No | unset | | `pipeline.seriatim.env.backchannel_max_duration` | float | No | unset | | `pipeline.seriatim.env.filler_max_duration` | float | No | unset | | `pipeline.audita.binary` | string | No | `audita` | | `pipeline.audita.timeout` | duration string | No | `3h` | | `pipeline.audita.llm_api_key_env` | string | No | empty | | `pipeline.audita.modules[]` | list[string] | No | empty | | `pipeline.audita.base_url` | string | No | empty | | `pipeline.audita.model` | string | No | empty | | `pipeline.audita.total_llm_concurrency` | int | No | unset | | `pipeline.audita.proposal_llm_concurrency` | int | No | unset | | `pipeline.audita.validation_model` | string | No | empty | | `pipeline.audita.validation_llm_concurrency` | int | No | unset | | `pipeline.audita.transcript_description` | string | No | empty | | `pipeline.audita.config_path` | string | No | empty | | `pipeline.audita.output_schema` | string | No | empty | | `pipeline.audita.work_dir_retention` | string | No | empty | | `pipeline.audita.report` | bool | No | `true` | | `pipeline.normalize.output_path` | string | No | `transcripts/final.json` | | `pipeline.normalize.output_schema` | string | No | `seriatim-intermediate` | | `pipeline.normalize.report` | bool | No | `true` | | `pipeline.trim.enabled` | bool | No | `false` | | `pipeline.trim.output_path` | string | Conditional | none | | `pipeline.trim.bounds.prompt_id` | string | Conditional | none | | `pipeline.trim.bounds.profile_id` | string | No | empty | | `pipeline.trim.bounds.transcript_input_name` | string | Conditional | none | | `pipeline.trim.bounds.output_path` | string | Conditional | none | | `pipeline.trim.bounds.timeout` | duration string | No | `10m` | | `pipeline.trim.bounds.render_debug` | bool | No | `false` | | `pipeline.trim.bounds.render_output_path` | string | Conditional | none | | `pipeline.trim.seriatim.report` | bool | No | `false` | | `pipeline.scriptorium.binary` | string | No | `scriptorium` | | `pipeline.scriptorium.config_path` | string | No | empty | | `pipeline.scriptorium.timeout` | duration string | No | `10m` | | `pipeline.scriptorium.render_debug` | bool | No | `false` | | `pipeline.scriptorium.artifacts` | map | No | empty | | `pipeline.notification.backend` | string | No | empty | | `pipeline.notification.recipient` | string | No | empty | | `pipeline.notification.timeout` | duration string | No | `30s` | ### Campaign | Path | Type | Required | | --- | --- | --- | | `campaign_id` | string | Yes | | `session_template_file` | string | No | | `inputs.speakers_file` | string | Yes | | `inputs.autocorrect_file` | string | Yes | | `inputs.glossary_file` | string | Yes | ### Session | Path | Type | Required | | --- | --- | --- | | `session_id` | string | Yes | | `previous_session_id` | string | No | | `campaign` | string | No | | `date` | string | No | | `title` | string | No | | `inputs.speakers_file` | string | No | | `inputs.autocorrect_file` | string | No | | `inputs.glossary_file` | string | No | | `inputs.audio_dir` | string | Conditional | | `inputs.audio_files[]` | list[string] | Conditional | | `inputs.audio_s3.prefix` | string | Conditional | ## Maintained Examples - `examples/pipeline.minimal.yml` - `examples/pipeline.production.yml` - `examples/pipeline.full.annotated.yml` - `examples/campaigns/sample-campaign/campaign.yml` - `examples/session.local-audio.yml` - `examples/session.s3-audio.yml`