Files
narratio/docs/config.md

29 KiB

Configuration Reference

Purpose

Narratio resolves three YAML documents:

  • pipeline.yml: pipeline/runtime settings
  • campaign.yml: campaign identity and stable input defaults
  • session.yml: session identity, metadata, and audio source selection

Discovery and Selection

pipeline.yml

When --config is omitted, search order is:

  1. /usr/local/etc/narratio/pipeline.yml
  2. /etc/narratio/pipeline.yml

campaign.yml

Selection rules:

  • if --campaign-file is set, use that path;
  • else if --campaign <id> is set, use {pipeline.campaigns.root}/{id}/campaign.yml;
  • else use {pipeline.campaigns.root}/{pipeline.campaigns.default_campaign_id}/campaign.yml.

session.yml

When --session is omitted, local search order is:

  1. /usr/local/etc/narratio/session.yml
  2. /etc/narratio/session.yml

If local session discovery fails and a session_id is known, Narratio attempts remote session loading from:

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

using configured object storage.

The downloaded remote session file is command-scoped: Narratio removes it after the command finishes and records only the remote object provenance alongside the durable copied session input.

Read-only effective pipeline inspection

narratio config validate, narratio config show, and narratio config sources use the same --config, --campaign, --campaign-file, and --profile selection rules as pipeline commands, but do not select, discover, or load a session. They do not read credential values or create runtime state. narratio config diff <left-profile> <right-profile> uses the same pipeline and campaign selectors, resolves each named profile independently from one parsed root source set, and does not accept a separate --profile flag.

Campaign selection is optional only when the resolved pipeline has no scriptorium.artifact_families. When families are declared, Narratio selects a campaign through an explicit flag or pipeline.campaigns.default_campaign_id, then parses the campaign-owned party and expands concrete artifacts and any family publish rules before validation. config validate prints the resulting root, profile, and effective digest. config show emits the normalized effective pipeline YAML, with defaults and concrete expansion included but composition and family declarations omitted. config sources prints a stable source projection instead of effective values: root/import/profile/default ownership plus campaign/party and generated-family records. Canonical derived players trace to the party; a legacy configured players file is explicitly marked as a legacy player source. The CLI reference owns command syntax and output conventions.

config diff compares normalized field values rather than YAML text or source ownership. It emits sorted added, removed, and changed records, uses compact deterministic JSON values, treats lists atomically, and reports no differences when the complete effective configurations are equal. Concrete family members and generated publish rules participate after expansion; moving an equal value between eligible root/import sources does not create a difference.

Migrating to the maintained bundle

Use the production/testing bundle as the complete copyable migration reference. Split stable pipeline settings into explicit additive imports, place production/testing differences in one selected overlay, and retain a production default_profile. Convert campaign rosters to canonical party input, remove a separate players_file, then express character work as families. Inspect the result with config validate, config show, and config sources; use config diff to review profiles before running a session. Unversioned parties and their players_file remain a clearly bounded legacy compatibility path.

Identity segments

Campaign IDs (campaign_id and default_campaign_id), session IDs, previous session IDs, and Narratio run IDs are opaque portable segments. They must use only ASCII letters, digits, ., _, and -; empty values, ./.., path separators, drive forms, whitespace, control characters, and non-ASCII text are rejected. Narratio does not trim or rewrite these values. Existing manifests or remote state with an unsafe legacy identity must be migrated before use.

Validation and Merge Rules

  • YAML decode is strict (KnownFields(true)) and accepts exactly one document: unknown fields or trailing documents fail load.
  • A pipeline file may explicitly import additive YAML fragments through the root-only composition.imports list. Imported files contribute fields to one logical pipeline document; they do not override fields supplied by the root or another import.
  • A root pipeline may declare named profiles. Exactly one profile is selected by an option-aware caller or by composition.default_profile; a caller's explicit selection takes precedence. Declaring profiles without either form of selection is an error.
  • Configured timeout and retry-delay durations must be positive. An omitted artifact timeout continues to inherit its configured Scriptorium timeout.
  • Session files must be concrete; unresolved {{ ... }} placeholders fail load.
  • Pipeline defaults are applied before validation.
  • Campaign and session identities must agree.
  • Required stable files (speakers_file, autocorrect_file, glossary_file, party_file) and the optional spell_catalog_file resolve from session overrides when provided, otherwise from campaign defaults. An empty or omitted session spell-catalog value inherits the campaign value.
  • party_file is classified when pipeline and campaign configuration are combined. A versioned canonical party is campaign-owned, derives the players input internally, and forbids both a separate players_file and a session party_file override. An unversioned party remains a bounded legacy input and requires players_file; its normal campaign/session overrides continue to apply.
  • Exactly one audio mode must be configured in session input:
    • local (audio_dir or audio_files), or
    • S3 (audio_s3.prefix).

Pipeline composition

Large pipeline configurations may be split into explicitly named fragments and may declare one overlay per selectable profile:

composition:
  imports:
    - config/storage.yml
    - config/integrations.yaml
  default_profile: production
  profiles:
    production:
      overlay: profiles/production.yml
    testing:
      overlay: profiles/testing.yml

campaigns:
  root: /usr/local/share/narratio/campaigns

The maintained production/testing bundle is a complete copyable example of this structure, including canonical-party artifact families.

Imports are resolved relative to the directory containing the root pipeline file and are loaded in declaration order. Narratio does not scan directories or infer fragments. Each import must be a confined regular .yml or .yaml file: absolute paths, traversal, symlinks, directories, duplicate files, and an import of the root pipeline itself are rejected. Only the root pipeline may contain composition; nested composition is rejected.

Composition is additive. A map may be extended by multiple files when every leaf is distinct, but a scalar, list, or map/list/scalar kind cannot be claimed more than once, even when the repeated values are identical. Conflict errors name the full field path and every source that claimed it. The assembled YAML is then decoded against the normal strict pipeline schema and defaults are applied once.

Profile names are case-sensitive, non-empty, trimmed, and cannot contain control characters. If profiles is present, it must contain at least one entry and every entry must contain only an overlay path. An explicit profile selection overrides default_profile; unknown and explicitly empty selections fail. Narratio never selects the first profile implicitly and does not read a profile selection from the environment.

Every declared overlay is resolved relative to the root pipeline directory and must satisfy the same confined regular-YAML-file rules as an import. Narratio parses every declared overlay even when it is not selected, then applies only the selected one. Maps merge recursively, overlay scalars replace base scalars, and overlay lists replace base lists completely. Explicit false, zero, empty lists, and empty maps remain meaningful. YAML null cannot delete a value, and kind changes are rejected. Profiles cannot inherit from or stack with other profiles, and overlays cannot import files or declare profiles.

After composition, Narratio strictly decodes the result, applies centralized defaults once, resolves ordinary paths, and computes a deterministic effective configuration digest. The digest represents the normalized, secret-free runtime pipeline mapping; it excludes composition declarations, source provenance, profile identity, and raw environment secret values. Equivalent effective mappings therefore have the same digest regardless of how fields are split among the root and imports.

An imported field has the same meaning it would have in a monolithic root pipeline. In particular, ordinary relative pipeline paths continue to resolve from the root pipeline directory, not from the importing fragment's directory.

Minimal Working Configuration

pipeline.yml

campaigns:
  root: /usr/local/share/narratio/campaigns
  default_campaign_id: sample-campaign
whisperx:
  transcribe_url: https://transcription.example.com/transcribe

campaign.yml

campaign_id: sample-campaign
inputs:
  speakers_file: ./speakers.yml
  autocorrect_file: ./autocorrect.yml
  glossary_file: ./glossary.yml
  players_file: ./players.yml
  party_file: ./party.yml

session.yml (local audio)

session_id: 2026-05-03
inputs:
  audio_dir: ./audio

Secrets Handling

  • Do not place raw secrets in YAML.
  • Use env var names in config (for example pipeline.audita.llm_api_key_env).
  • Optionally load credential files from pipeline.secrets.env_dir. Each valid environment-variable filename supplies one value; trailing CR/LF is removed.
  • An existing process environment value takes precedence over a credential file.
  • Credential directories and files must not be symlinks and must be regular, bounded files (at most 8 KiB per value). On POSIX, provision the directory with no group/other access (normally 0700) and files with no group/other access (normally 0600).
  • Commands that need storage/auth load filesystem secrets before constructing adapters.

Publish Configuration Summary

Publish rules live under pipeline.publish.

publish:
  enabled: true
  upload_run: true
  outputs:
    - source: narratio.transcript.final_trimmed
      dest: transcripts/final.trimmed.json
      required: true
    - source: narratio.transcript.final_markdown
      dest: transcripts/final.md
      required: true
    - source: narratio.transcript.final_trimmed_markdown
      dest: transcripts/final.trimmed.md
      required: true
    - source: narratio.artifact.session_recap
      dest: artifacts/session_recap.md
      required: true
  locks:
    - source: narratio.artifact.session_recap
      reason: manual post-publish edits

Rules:

  • outputs[].source is required.
  • outputs[].dest may be omitted when derivable from source.
  • extraction sources require an explicit outputs[].dest and publish only when a rule names that source; the Notarius index and complete bundle are not publish sources.
  • outputs[].required defaults to true.
  • static locks (pipeline.publish.locks) merge with remote locks ({session_prefix}/locks.yml), with static locks taking precedence on duplicates.

Full Schema

Pipeline

Field Type Required Default / Rule
composition.imports[] list of strings No explicit additive pipeline fragments relative to the root pipeline directory; .yml or .yaml regular files only
composition.default_profile string Conditional selected when profiles exist and no caller explicitly selects one; must name a declared profile
composition.profiles.<name>.overlay string Conditional required for every declared profile; one confined .yml or .yaml overlay relative to the root pipeline directory
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 local; supported values are local and s3 (case-insensitive)
pipeline.storage.s3.bucket string Conditional required when backend is s3 and S3 session-audio or publish upload is enabled
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 defaults to final trimmed JSON plus final and final-trimmed Markdown outputs
pipeline.publish.outputs[].source string Yes (per rule) must reference built-in or configured artifact source
pipeline.publish.outputs[].dest string Conditional derived if omitted and source supports derivation
pipeline.publish.outputs[].required bool No true
pipeline.publish.locks[] list No empty
pipeline.publish.locks[].source string Yes (per lock) must reference supported publish source
pipeline.publish.locks[].reason string No empty
pipeline.whisperx.transcribe_url string Yes absolute http or https URL
pipeline.whisperx.language string No en
pipeline.whisperx.timeout duration No 30m
pipeline.whisperx.retries int No 3
pipeline.whisperx.retry_delay duration No 2s
pipeline.whisperx.concurrency int No 2
pipeline.seriatim.binary string No seriatim
pipeline.seriatim.timeout duration 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 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 true
pipeline.trim.output_path string No transcripts/final.trimmed.json
pipeline.trim.bounds.prompt_id string No dnd.session_bounds
pipeline.trim.bounds.profile_id string No empty
pipeline.trim.bounds.transcript_input_name string No transcript
pipeline.trim.bounds.output_path string No artifacts/session_bounds.json
pipeline.trim.bounds.timeout duration No 10m
pipeline.trim.bounds.render_debug bool No false
pipeline.trim.bounds.render_output_path string Conditional required when render_debug is true
pipeline.trim.seriatim.report bool No false
pipeline.notarius.enabled bool No false
pipeline.notarius.binary string No notarius
pipeline.notarius.config_path string Conditional required when enabled; relative paths resolve from the pipeline file directory
pipeline.notarius.pipeline_id string Conditional required when enabled
pipeline.notarius.timeout duration No 3h; must be positive
pipeline.notarius.working_directory string No directory containing resolved config_path; relative paths resolve from the pipeline file directory
pipeline.notarius.references map[string]string No empty; maps normalized Notarius selectors to supported prepared Narratio source IDs; maximum 256 entries
pipeline.notarius.outputs map Conditional at least one entry when enabled
pipeline.render.enabled bool No true
pipeline.render.format string No markdown (only supported value)
pipeline.render.title string No empty (falls back to session.title when set)
pipeline.render.include_timestamps bool No true
pipeline.render.include_segment_ids bool No true
pipeline.render.include_metadata bool No false
pipeline.scriptorium.binary string No scriptorium
pipeline.scriptorium.config_path string No empty
pipeline.scriptorium.timeout duration No 10m
pipeline.scriptorium.render_debug bool No false
pipeline.scriptorium.artifacts map No empty
pipeline.scriptorium.artifact_families map No empty; expands one ordinary artifact per canonical party character
pipeline.notification.mode string No noop; the only supported notification mode until a provider is implemented

Notarius Reference Bindings

pipeline.notarius.references maps a Notarius CLI selector to a prepared Narratio source, not to a filesystem path:

notarius:
  references:
    glossary: narratio.input.glossary
    party: narratio.input.party
    players: narratio.input.players
    spell_catalog: narratio.input.spell_catalog

Supported sources are narratio.input.party, narratio.input.players, narratio.input.glossary, and narratio.input.spell_catalog. Each map entry is required by its presence: omit a binding when the selected Notarius pipeline does not need it. A spell-catalog binding additionally requires an effective campaign or session spell_catalog_file.

Selectors accept Notarius's slot, chunk.slot, lane.slot, lane.extract.slot, lane.merge.slot, and lane.normalize.slot forms. Narratio trims whitespace around selectors and their dot-separated components, rejects empty components and =, rejects duplicate normalized selectors, and limits the map to 256 entries. It validates only selector structure and the prepared source vocabulary; Notarius owns target-slot declarations and media compatibility.

Before extraction, Narratio resolves every binding from the current prepared session manifest and streams it into a verified invocation-local snapshot whose absolute path is passed to Notarius. Missing, unsafe, empty, changed-during-copy, or checksum-inconsistent prepared evidence fails with guidance to force prepare. Bindings are sorted by normalized selector and are part of extraction fingerprint and resume identity. See the Notarius integration contract for the subprocess boundary and the complete example for a copyable configuration.

Notarius Output Entries

For each pipeline.notarius.outputs.<name>:

Field Type Required Rule
lane_id string Yes unique Notarius lane ID
media_type string Yes exact accepted descriptor media type
schema_id string Yes exact accepted descriptor schema ID
schema_version string Yes exact accepted descriptor schema version
module_key string No exact accepted module key when set

Output names must match ^[a-z][a-z0-9_]*$ and become selectable sources named narratio.extraction.<name>. Lane IDs must be unique. Every declared output is required from a successful Notarius result; a missing, rejected, duplicate, or contract-incompatible lane fails extraction. See the complete maintained example for the current ten-lane D&D mapping and the Notarius contract for compatibility ownership.

Scriptorium Artifact Entries

For each pipeline.scriptorium.artifacts.<name>:

Field Type Required Rule
enabled bool No false if omitted
depends_on[] list[string] No must reference configured artifact keys; no self-reference; configured graph must be acyclic
render_debug bool No per-artifact override
prompt_id string Conditional required when artifact is enabled
profile_id string No empty
output_path string Conditional required when enabled; also required when referenced by publish/output/input rules
timeout duration No artifact override
inputs map No input key names must be non-empty
vars map No values must be string or bool; session_id is reserved and overwritten by Narratio

Narratio adds session_id=narratio-session-<session_id> to every Scriptorium request for sticky upstream LLM routing. If an artifact config sets vars.session_id, Narratio replaces that value before invoking Scriptorium. Use a different variable name if a prompt needs the raw Narratio session ID as content.

Without --artifacts, analyze executes enabled configured artifacts. With an explicit --artifacts list, the exact named configured artifacts are the one-invocation targets even if their enabled values are false. Analyze closes those targets over depends_on: a current prerequisite is reused, while a stale, missing, failed, or legacy prerequisite is rebuilt before its dependent. Unrelated artifacts are not executed. Named targets and any prerequisite that may require rebuilding must therefore have valid executable fields. This override affects analyze planning only; publish uses the list only to filter configured narratio.artifact.<name> output rules.

For each artifact input pipeline.scriptorium.artifacts.<name>.inputs.<input_name>:

Field Type Required Rule
source string Yes built-in runtime source, prepared input source, narratio.extraction.<name>, narratio.artifact.<name>, or narratio.previous_session.artifact.<name>
required bool No optional input requirement

artifact and path are obsolete and rejected by strict configuration loading. Use the canonical source identifier to select the input; Narratio does not provide adapter-specific input passthrough fields.

Scriptorium Artifact Families

pipeline.scriptorium.artifact_families declares a shared artifact template for every canonical campaign character. Configuration resolution expands each family into ordinary pipeline.scriptorium.artifacts entries before analyze planning or Scriptorium invocation. A legacy party cannot be used for a family.

For each pipeline.scriptorium.artifact_families.<name>:

Field Type Required Rule
enabled, prompt_id, profile_id, timeout, render_debug, depends_on, inputs, vars ordinary artifact fields No copied to each generated artifact under the corresponding ordinary rules
for_each string Yes exactly party.characters
output_path_pattern string Yes safe path beneath artifacts/ with exactly one {character_id} token and no other brace syntax
member_vars map No maps an ordinary Scriptorium variable name to a supported canonical character selector
member_dependencies list No unique family keys; each generated member depends on the corresponding generated member of each listed family
publish map No typed family publish policy (enabled, required, dest_pattern) expanded into concrete publish outputs when enabled

Generated keys are <family>_<character_id> and generated output paths must not collide with explicit artifacts or another generated artifact. Families expand even when disabled; normal analyze selection still omits disabled artifacts unless they are explicitly selected by their concrete key.

Supported member_vars selectors are character_id, player.name, character.name, character.class_summary, and character.alias_summary. Their resolved values are strings. A member variable may not reuse a static vars name; session_id remains owned and overwritten by Narratio as for any other Scriptorium artifact.

Within a family only, an input source may use narratio.member_artifact.<family>. The referenced family must be named in that family's member_dependencies; resolution rewrites the source to the corresponding ordinary narratio.artifact.<family>_<character_id> source. This syntax is rejected in explicit artifacts and never reaches runtime stages or Scriptorium.

Notifications

Narratio currently supports only notification.mode: noop, which is also the default when the section is omitted. The notify stage performs no delivery in this mode. Backend, recipient, timeout, and other provider settings are rejected by strict configuration loading until Narratio has a provider integration.

Campaign

Field Type Required Notes
campaign_id string Yes canonical opaque campaign identity
session_template_file string No used by session init when set
inputs.speakers_file string Yes stable input default
inputs.autocorrect_file string Yes stable input default
inputs.glossary_file string Yes stable input default
inputs.players_file string Conditional required only with an unversioned legacy party_file; forbidden for a canonical party
inputs.party_file string Yes stable campaign party source; relative paths resolve from campaign.yml
inputs.spell_catalog_file string No optional spell-catalog overlay default; required when a Notarius reference selects narratio.input.spell_catalog

Session

Field Type Required in session file Notes
session_id string Yes opaque identity; must match CLI session target when provided
previous_session_id string No opaque identity; must not equal session_id
campaign string No opaque identity; filled from campaign_id during resolve if omitted
date string No metadata
title string No metadata
inputs.speakers_file string No overrides campaign stable input
inputs.autocorrect_file string No overrides campaign stable input
inputs.glossary_file string No overrides campaign stable input
inputs.players_file string No legacy-party override; forbidden for a canonical party
inputs.party_file string No legacy-party override; forbidden for a canonical campaign party
inputs.spell_catalog_file string No overrides the optional campaign spell catalog; empty or omitted inherits the campaign value
inputs.audio_dir string Conditional local audio mode
inputs.audio_files[] list[string] Conditional local audio mode
inputs.audio_s3.prefix string Conditional S3 audio mode

Audio rules:

  • configure local mode (audio_dir or audio_files) or S3 mode (audio_s3.prefix), not both.
  • audio_s3 requires pipeline.storage.backend: s3 and a configured S3 bucket.

Storage backend selection

local is the default and disables remote object-store operations. Configure s3 explicitly before supplying storage.s3; a populated S3 block does not select a backend on its own. Unknown backend names and an S3 block paired with local are rejected during configuration validation.

Previous-session expectation

previous_session_id is optional in a session file. When a command supplies --previous-session-id, however, the session file must contain the same value; an omitted or different value is rejected before the command performs work.

Maintained Examples

See the maintained examples index for complete pipeline, campaign, session, template, and input fixtures. Keep complete copyable files there rather than duplicating them in this reference.