Files
narratio/docs/config.md

32 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, narratio config sources, and narratio config artifacts use the same --config, --campaign, --campaign-file, and --profile selection rules as pipeline commands, but do not select, discover, or load a session. They resolve configured typed credential references as part of the ordinary ConfigKit load but expose only their environment-variable names; they do not create runtime state. narratio config diff <left-profile> <right-profile> uses the same pipeline and campaign selectors, resolves each named profile with an independent ConfigKit load, 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 ConfigKit directives and family declarations omitted. This review projection is not promised to be a reloadable authoring document. 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. config artifacts lists the concrete artifact map, effective dependencies, and defining sources. 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. Keep production settings in the base, split them into explicit additive imports, and place testing differences in the fixed profiles/testing.yml overlay. Put ordinary artifacts and families in the recommended one-definition files shown by that bundle. Convert campaign rosters to canonical party input, then express character work as families. Inspect the result with config validate, config show, config sources, and config artifacts; use config diff default testing to review the overlay before running a session. Unversioned party documents are rejected.

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

  • ConfigKit performs strict typed YAML decoding and accepts exactly one document; unknown fields, duplicate keys, and trailing documents fail load.
  • A pipeline file may explicitly import additive YAML fragments through the root-only configkit.imports list. Imported files contribute fields to one logical pipeline document; they do not override fields supplied by the root or another import.
  • An omitted profile, an empty profile option, and the exact name default select the base configuration. Any other name selects profiles/<name>.yml relative to the pipeline root.
  • Campaign, session, and canonical-party documents are strict single-source ConfigKit schemas. They do not enable imports, profiles, or configkit directives.
  • 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.
  • Session and party inputs inspected before typed loading are limited to 8 MiB.
  • Pipeline defaults are applied before validation.
  • Defaults apply only to absent fields. Explicit empty strings, zero values, false, and null retain ConfigKit's typed meaning and are then checked by Narratio domain validation; they are not silently replaced by defaults. Explicit null optional stage sections stay absent, while required sections fail validation.
  • Campaign and session identities must agree.
  • Required stable files (speakers_file, autocorrect_file, glossary_file, party_file) are campaign-owned; the first three may be overridden by the session. The optional spell_catalog_file also inherits a campaign value when omitted by the session.
  • party_file is parsed when pipeline and campaign configuration are combined. It must be a versioned canonical party, is campaign-owned and derives the players input internally. players_file and session party_file are unsupported configuration keys.
  • Exactly one audio mode must be configured in session input:
    • local (audio_dir or audio_files), or
    • S3 (audio_s3.prefix).

Pipeline imports and profiles

Large pipeline configurations may be split into explicitly named fragments and may use fixed-path profile overlays:

configkit:
  imports:
    - path: config/storage.yml
    - path: config/integrations.yaml

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. Imported files cannot declare another configkit control section.

Base merging 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. Narratio additionally rejects an artifact or artifact-family key declared by more than one base file, even when those files contribute disjoint children.

Profile names are case-sensitive and use ConfigKit's safe fixed-path naming rules. Only the selected overlay is opened; a malformed unselected overlay does not affect a base or different-profile load. Maps merge recursively, overlay scalars replace base scalars, and overlay lists replace base lists completely. Explicit false, zero, empty strings, empty containers, and null values remain explicit inputs and are not refilled by defaults. Profiles cannot inherit, stack, import files, or declare another ConfigKit control section.

During ConfigKit loading, Narratio applies fresh typed defaults, recorded entry-level defaults and normalization, read-only file-local validation, and ordinary path resolution before computing a deterministic effective configuration digest. The digest represents the normalized, secret-free runtime pipeline mapping; it excludes ConfigKit directives, 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
  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.
  • Credential fields contain environment-variable names, represented internally as ConfigKit typed secret references (for example pipeline.notarius.llm_api_key: NOTARIUS_LLM_API_KEY). A configured reference must resolve to a nonempty environment value during load.
  • Every surviving reference is resolved during ordinary pipeline loading, regardless of whether its integration is disabled, outside the selected stage range, or eligible for resume. Omit the field or set it to null when that deployment should not require the credential.
  • Every command that loads a pipeline accepts --secrets-dir <path>. When the flag is omitted, Narratio uses a present secrets/ directory beside the resolved root pipeline.yml; an absent conventional directory is ignored. Explicit relative paths are resolved from the caller's working directory, and a missing or empty explicit selection is an error.
  • Each immediate regular file whose name is a valid environment-variable name supplies one value. ConfigKit removes trailing CR/LF, permits a symlink as the selected directory, rejects symlink entries, and limits input to 1 MiB per file, 8 MiB total, and 1024 entries.
  • An existing process environment value takes precedence over a credential file.
  • On POSIX, operators must provision the directory with no group/other access (normally 0700) and files with no group/other access (normally 0600); ConfigKit validates type and bounds but does not enforce permission modes.
  • Bootstrap happens once per command, before any configuration load or adapter construction. Safe reports contain filenames, paths, and statuses, never values.

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

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.
  • publish locks are independent remote session objects managed with narratio session locks; they are not pipeline configuration fields.

Full Schema

Pipeline

Field Type Required Default / Rule
configkit.imports[].path string No explicit additive pipeline fragment relative to the root pipeline directory; .yml or .yaml regular files only
pipeline.workspace.root string No /var/lib/narratio
pipeline.workspace.cleanup_after_publish bool No false; selects the published run's workspace target for final cleanup and requires effective pipeline.publish.enabled=true and pipeline.publish.upload_run=true
pipeline.campaigns.root string No /usr/local/share/narratio/campaigns
pipeline.campaigns.default_campaign_id 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 secret reference Conditional unset; must be paired with secret_access_key
pipeline.storage.s3.secret_access_key secret reference Conditional unset; must be paired with access_key_id
pipeline.spool.root string No /var/spool/narratio
pipeline.spool.delete_audio_after_publish bool No false; selects the published run's spool-audio target for final cleanup and requires effective pipeline.publish.enabled=true and pipeline.publish.upload_run=true
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.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 secret reference No unset
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.llm_api_key secret reference No unset; exact environment-variable name forwarded to 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 Credential

pipeline.notarius.llm_api_key optionally names the one environment variable to forward to the Notarius child process:

notarius:
  llm_api_key: OPENROUTER_API_KEY

The field contains the variable name, not a credential value. That name must match api_key_env in the Notarius/PromptKit profile selected by the deployed Notarius pipeline; Narratio does not infer or remap provider names. Omission or null forwards nothing. A non-null selected reference must resolve at pipeline load time through the process environment or existing secrets-directory bootstrap, even when Notarius is disabled, extract is outside the requested range, or a completed extraction can be reused.

The Notarius child receives only Narratio's minimal execution environment plus this exact selected name and loaded value. Other credentials are not forwarded. Changing the value, name, presence, or absence does not invalidate extract's semantic resume evidence. The public name remains part of safe effective inspection, so renaming it may appear in config diff and change the effective configuration digest; rotating only its value does neither.

See the Notarius integration contract for the delivery and redaction boundary.

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 explicit ordering-only edges; must reference configured artifact keys, may not self-reference, and join inferred input edges in one acyclic graph
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 the effective dependency graph: a current prerequisite is reused, while a stale, missing, or failed 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.

Each narratio.artifact.<name> input source automatically contributes an effective dependency on that configured artifact. Narratio unions these inferred edges with explicit depends_on entries, then trims, deduplicates, and sorts the result during final configuration resolution. Authors use depends_on only for ordering constraints that are not already expressed by an artifact input. Use narratio config artifacts to inspect keys, definition sources, outputs, and effective dependencies without loading a session or creating runtime state.

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. Party parsing rejects unsupported document schemas before family expansion.

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>. Resolution rewrites the source to the corresponding ordinary narratio.artifact.<family>_<character_id> source and infers the concrete member dependency. Use member_dependencies only for additional same-character ordering constraints that are not already inputs. 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.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.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.