Files
narratio/docs/config.md

19 KiB

Configuration

1. Overview

Narratio loads three YAML files:

  • pipeline.yml: pipeline-level runtime configuration.
  • campaign.yml: stable campaign identity and campaign-level input defaults.
  • session.yml: per-session metadata and input selection, loaded locally or from the configured S3 backend.

These commands load and validate all three files before running:

  • narratio run
  • narratio plan
  • narratio resume
  • narratio run-stage
  • narratio restore

Behavior:

  • strict YAML decode is enabled (KnownFields(true)): unknown fields fail.
  • session templates render before session YAML decode.
  • remote session.yml uses the same strict decode and template behavior as local session.yml.
  • defaults are applied for optional pipeline fields.
  • campaign-level stable input paths fill missing session input paths.
  • session-level stable input paths override campaign-level input paths.
  • validation enforces required fields, value formats, and cross-field constraints.

2. Config file discovery

These commands use the same config discovery behavior:

  • narratio run
  • narratio plan
  • narratio resume
  • narratio run-stage
  • narratio restore

Pipeline config lookup:

  • if --config <path> is provided, that path is used.
  • if omitted, Narratio searches in order:
    1. /usr/local/etc/narratio/pipeline.yml
    2. /etc/narratio/pipeline.yml
  • first existing file wins.

Campaign config lookup:

  • if --campaign <path> is provided, that path is used.
  • if omitted, Narratio searches in order:
    1. /usr/local/etc/narratio/campaign.yml
    2. /etc/narratio/campaign.yml
  • first existing file wins.

Session config lookup:

  • if --session <path> is provided, that path is used.
  • if --session is omitted, Narratio searches locally in order:
    1. /usr/local/etc/narratio/session.yml
    2. /etc/narratio/session.yml
  • first existing local file wins.
  • if no local session file is found, --session-id <value> is present, storage is configured, and campaign identity is resolved, Narratio loads remote session.yml from:
    • {root_prefix}/campaigns/{campaign}/sessions/{session_id}/session.yml
  • local discovery always runs before remote fallback.
  • local files in the current working directory are used only when passed explicitly, for example --config ./pipeline.yml --campaign ./campaign.yml --session ./session.yml.

3. Session templating

Template behavior for local and remote session.yml:

  • supported placeholders:
    • {{session_id}}
    • {{ session_id }}
    • {{previous_session_id}}
    • {{ previous_session_id }}
  • --session-id <value> supplies the placeholder value.
  • --previous-session-id <value> supplies the previous-session placeholder value.
  • unresolved placeholders fail load.
  • if rendered session_id mismatches --session-id, load fails.
  • if rendered previous_session_id mismatches --previous-session-id, load fails.

4. Minimal config set

pipeline.yml

whisperx:
  transcribe_url: "https://transcription.example.com/transcribe"

Why this is sufficient:

  • whisperx.transcribe_url is required.
  • workspace.root defaults to /var/lib/narratio.
  • optional sections (seriatim, audita, archive, scriptorium, trim, normalize, etc.) receive defaults or stay inactive.

campaign.yml

campaign: sample-campaign
inputs:
  speakers_file: ./speakers.yml
  autocorrect_file: ./autocorrect.yml
  glossary_file: ./glossary.yml

Why this is sufficient:

  • campaign supplies the stable campaign identity.
  • stable input files are required and resolve relative to campaign.yml when copied during prepare.

session.yml

session_id: "{{ session_id }}"
inputs:
  audio_dir: ./audio

Why this is sufficient:

  • session_id is required and can be rendered from --session-id.
  • campaign can be omitted because it is supplied by campaign.yml.
  • stable input paths can be omitted because campaign.yml supplies defaults.
  • local audio_dir resolves relative to session.yml.

Minimal local-file usage:

narratio run --config /path/to/pipeline.yml --campaign ./campaign.yml --session ./session.yml --session-id 2026-05-03

Previous-session-enabled variant:

session_id: "{{ session_id }}"
previous_session_id: "{{ previous_session_id }}"
inputs:
  audio_dir: ./audio
narratio run --config /path/to/pipeline.yml --campaign ./campaign.yml --session ./session.yml --session-id 2026-05-03 --previous-session-id 2026-04-26

5. Production-oriented config set

pipeline.yml

workspace:
  root: /var/lib/narratio/workspace
  cleanup_after_archive: true

storage:
  backend: s3
  s3:
    bucket: my-dnd-archive
    root_prefix: dnd
    region: us-east-1
    access_key_id_env: OBJECT_STORAGE_KEY_ID
    secret_access_key_env: OBJECT_STORAGE_KEY

spool:
  root: /var/spool/narratio
  delete_audio_after_archive: true

archive:
  enabled: true
  upload_run: true
  promote_artifacts:
    - source: narratio.transcript.trimmed
      dest: transcripts/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.

whisperx:
  transcribe_url: "https://transcription.example.com/transcribe"

scriptorium:
  artifacts:
    session_recap:
      enabled: true
      prompt_id: dnd.session_recap
      output_path: artifacts/session_recap.md
      inputs:
        transcript:
          source: narratio.transcript.trimmed
          required: true
        previous_recap:
          source: narratio.previous_session.artifact.session_recap
          required: false

campaign.yml

campaign: forsaken
inputs:
  speakers_file: /srv/narratio/campaigns/forsaken/speakers.yml
  autocorrect_file: /srv/narratio/campaigns/forsaken/autocorrect.yml
  glossary_file: /srv/narratio/campaigns/forsaken/glossary.yml

Local session.yml

session_id: "{{ session_id }}"
previous_session_id: "{{ previous_session_id }}"
date: 2026-05-03
title: The Black Cabin
inputs:
  audio_s3:
    prefix: audio/

S3-first session config

For S3-first operation, upload the same session.yml content to:

{root_prefix}/campaigns/{campaign}/sessions/{session_id}/session.yml

Then run with explicit or discovered pipeline/campaign config and no --session:

narratio run --config /usr/local/etc/narratio/pipeline.yml --campaign /usr/local/etc/narratio/campaign.yml --session-id 2026-05-03 --previous-session-id 2026-04-26

Operational notes:

  • archive promotion is explicit and source-based via archive.promote_artifacts.
  • source is required; dest is optional and derived when omitted.
  • archive.locks skips top-level promotion overwrites for static locked sources while preserving run-local uploads.
  • operator-created mutable locks are stored at {root_prefix}/campaigns/{campaign}/sessions/{session_id}/locks.yml and are merged with static locks.
  • Narratio does not auto-promote all generated analyze artifacts.
  • restore reads the same config/campaign/session inputs and restore scope is bounded by committed archive current state.

6. Full pipeline reference

Path Type Required Default
pipeline.workspace.root string No /var/lib/narratio
pipeline.workspace.cleanup_after_archive bool No false
pipeline.secrets.env_dir string Conditional none
pipeline.storage.backend string No empty
pipeline.storage.bucket string No empty
pipeline.storage.prefix 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_archive bool No false
pipeline.archive.enabled bool No true
pipeline.archive.upload_run bool No true
pipeline.archive.promote_artifacts[] list No trimmed transcript rule
pipeline.archive.promote_artifacts[].source string Yes (per rule) none
pipeline.archive.promote_artifacts[].dest string No derived from source
pipeline.archive.promote_artifacts[].required bool No true
pipeline.archive.locks[] list No empty
pipeline.archive.locks[].source string Yes (per lock) none
pipeline.archive.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/normalized.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.scriptorium.artifacts.<name>.enabled bool No false
pipeline.scriptorium.artifacts.<name>.depends_on[] list[string] No empty
pipeline.scriptorium.artifacts.<name>.render_debug bool No unset
pipeline.scriptorium.artifacts.<name>.prompt_id string Conditional none
pipeline.scriptorium.artifacts.<name>.profile_id string No empty
pipeline.scriptorium.artifacts.<name>.output_path string Conditional none
pipeline.scriptorium.artifacts.<name>.timeout duration string No empty
pipeline.scriptorium.artifacts.<name>.inputs.<key>.source string Conditional none
pipeline.scriptorium.artifacts.<name>.inputs.<key>.artifact string No empty
pipeline.scriptorium.artifacts.<name>.inputs.<key>.path string No empty
pipeline.scriptorium.artifacts.<name>.inputs.<key>.required bool No false
pipeline.scriptorium.artifacts.<name>.vars.<key> map value No empty
pipeline.analyzer.binary_path string No empty
pipeline.analyzer.timeout duration string No empty
pipeline.analyzer.artifacts.output_dir string No empty
pipeline.analyzer.artifacts.types[] list[string] No empty
pipeline.notification.backend string No empty
pipeline.notification.recipient string No empty
pipeline.notification.timeout duration string No empty

Scriptorium artifact-key and dependency rules:

  • artifact keys must match ^[a-z][a-z0-9_]*$.
  • enabled artifacts require prompt_id and output_path.
  • output_path must be relative, traversal-safe, and under artifacts/.
  • configured artifact input sources use narratio.artifact.<name>.
  • if input source references narratio.artifact.<name>, artifact <name> must exist and must be listed in depends_on.
  • every depends_on entry must be a configured artifact key.
  • self-dependency is rejected.
  • enabled dependency cycles are rejected.
  • any artifact referenced by depends_on or narratio.artifact.<name> source must define output_path (even if not enabled).

Allowed pipeline.scriptorium.artifacts.<name>.inputs.<key>.source values:

  • narratio.previous_session.artifact.<configured_artifact_key>
  • narratio.transcript.merged
  • narratio.transcript.polished
  • narratio.transcript.full
  • narratio.transcript.trimmed
  • narratio.bounds.session
  • narratio.artifact.<configured_artifact_key>
  • previous_session_artifact (legacy path-based source; uses inputs.<key>.path)

pipeline.archive.promote_artifacts[].source values:

  • narratio.transcript.merged
  • narratio.transcript.polished
  • narratio.transcript.full
  • narratio.transcript.trimmed
  • narratio.bounds.session
  • narratio.artifact.<configured_artifact_key>

pipeline.archive.locks[].source accepts the same source values as pipeline.archive.promote_artifacts[].source.

Archive promotion destination rules:

  • dest must be a clean relative path (not absolute, no traversal).
  • duplicate dest values are rejected.
  • if dest is omitted:
    • built-in sources derive their canonical destination path;
    • configured sources derive from pipeline.scriptorium.artifacts.<name>.output_path;
    • derivation failure is a config validation error.

Archive lock rules:

  • locks are source-based and do not accept dest.
  • duplicate lock sources are rejected.
  • static pipeline.archive.locks win over remote mutable locks for the same source.
  • locked promotions are recorded as intentional skips in archive metadata.
  • locked required promotions do not fail archive by default.
  • ordinary --force reruns do not override locks.

Remote mutable lock store:

  • path: {root_prefix}/campaigns/{campaign}/sessions/{session_id}/locks.yml.
  • strict YAML shape: top-level locks, each with source and optional reason.
  • narratio lock and narratio unlock mutate only the remote lock store.
  • writes use existence checks plus --force for updates; they are not compare-and-swap atomic.

Restore-related implications:

  • restore remote identity requires archive S3 identity to resolve (pipeline.storage.s3.bucket and session prefix derivation inputs).
  • restore scope considers committed current state and durable paths (manifest.json, transcripts/**, artifacts/**, previous/**, optional audio/**).

7. Full campaign reference

Path Type Required Default
campaign.campaign string Yes none
campaign.inputs.speakers_file string Yes none
campaign.inputs.autocorrect_file string Yes none
campaign.inputs.glossary_file string Yes none

Campaign input paths may be absolute or relative. Relative paths resolve from the directory containing campaign.yml.

8. Full session reference

Path Type Required Default
session.session_id string Yes none
session.previous_session_id string No empty
session.campaign string No campaign.campaign
session.date string No empty
session.title string No empty
session.inputs.audio_dir string Conditional empty
session.inputs.audio_files[] list[string] Conditional empty
session.inputs.audio_s3.prefix string Conditional none
session.inputs.speakers_file string No campaign.inputs.speakers_file
session.inputs.autocorrect_file string No campaign.inputs.autocorrect_file
session.inputs.glossary_file string No campaign.inputs.glossary_file

Session input paths may be absolute or relative. Relative audio paths and session-level stable input overrides resolve from the directory containing session.yml. If both campaign.yml and session.yml specify campaign identity, the values must match.

Audio-source rule:

  • configure exactly one mode:
    • audio_dir, or
    • audio_files (at least one), or
    • audio_s3.prefix
  • audio_s3 cannot be combined with local audio fields.

Previous-session rule:

  • if session.previous_session_id is set, it must not equal session.session_id.
  • canonical previous-session sources (narratio.previous_session.artifact.<name>) are hydrated during prepare from archive current state when required by enabled configured artifacts.

9. Secrets

Narratio supports filesystem-based secret injection via pipeline.secrets.env_dir.

Behavior:

  • env_dir may be absolute or relative.
  • relative env_dir resolves from current working directory.
  • files with valid env-var names ([A-Za-z_][A-Za-z0-9_]*) are loaded.
  • values are loaded from file contents with trailing newline trimming.
  • existing process env vars are preserved.
  • invalid names and subdirectories are skipped.
  • missing/unreadable env_dir fails command execution.

Guidance:

  • do not put secret values directly in YAML.
  • configure env var names in config and provide values via env/secrets files.

10. Examples

Maintained examples:

  • examples/pipeline.minimal.yml
  • examples/pipeline.production.yml
  • examples/pipeline.full.annotated.yml
  • examples/campaign.yml
  • examples/session.template.yml
  • examples/session.local-audio.yml
  • examples/session.s3-audio.yml

These examples are validated by internal/config tests.