26 KiB
Feature 0005: Narratio Source Installation and Configuration
Status: done
Summary
Provide a small, independently usable rakestraw.narratio role that installs
Narratio from a pinned Git tag, builds its executable on the managed host,
creates the application account and runtime directories, and manages its
native pipeline configuration. Keep the role application-specific so the
later suite role can supply D&D policy through its public interface without
making the Narratio role responsible for the other applications or campaign
content.
Problem or Opportunity
The ansible-role-narratio repository contains only its initial README and
license. Narratio is the command-line orchestrator for the transcript pipeline,
so the complete suite cannot be composed until Narratio has an independently
usable installation and configuration role.
The four completed downstream application roles establish the concise source-build pattern. Narratio can reuse that pattern, but its native contract also requires a deployment account, shared writable runtime areas, a campaign registry root, and a substantially larger pipeline configuration interface. This feature fixes those boundaries before suite composition begins.
Governing Policies
- The Ansible house style governs the role implementation, public configuration interface, and template.
- The testing house style governs repository-local validation. Tests exercise role-owned behavior through public inputs and native application outcomes without snapshotting defaults, tracing tasks, or rechecking trusted-module postconditions.
- The MVP roadmap fixes the common source-build architecture, supported platform, filesystem layout, and standalone-role boundary.
Scope
- Implement
ansible-role-narratioas a conventional standalone role. - Support Ubuntu 26.04 on native
amd64andarm64hosts. - Install Git and the official Go Snap with classic confinement when build dependency management is enabled.
- Default the Go Snap channel to
1.26/stable. - Check out immutable Narratio tag
v2.0.0by default under/usr/local/src/narratio. - Build the native executable with Narratio's release build contract and
install it at
/usr/local/bin/narratio. - Rebuild when the Git checkout changes or the binary is absent.
- Converge the source tree and installed binary to root ownership, with the
binary at mode
0755. - Create the
narratiosystem group and system account with/var/lib/narratioas its home and a non-login shell. - Create the workspace, spool, and cache roots as group-writable,
setgid directories owned by
narratio:narratio. - Create the campaign registry as a root-managed, operator-readable content root; leave its contents to the suite role or another caller.
- Manage
/usr/local/etc/narratio/pipeline.ymlasroot:narratiowith mode0640using one role-owned template. - Require the caller to supply the deployment-specific WhisperX transcription URL because Narratio has no valid universal value for this required field.
- Give filesystem roots concrete role-owned defaults. Default other optional application settings to null and omit them until a caller supplies a non-null value, allowing Narratio to retain ownership of its built-in defaults.
- Expose scalar fields individually. Expose native open-ended collections as one variable each, including publish outputs, Notarius references and outputs, and Scriptorium artifacts and artifact families.
- Permit callers to compose those collection variables in inventory before assigning the final value; do not add role-owned merge or precedence logic.
- Expose ConfigKit's explicit import list in the root document without taking ownership of the imported fragments or named profile overlays.
- Keep defaults, argument specifications, README, metadata, collection dependency declaration, template, and focused integration tests aligned with this contract.
Non-goals
- Installing or configuring Audita, Notarius, Seriatim, Scriptorium, WhisperX, or the complete D&D suite.
- Populating campaign data, prompt libraries, profiles, schemas, or session documents.
- Selecting D&D-specific pipelines, campaigns, models, prompts, artifacts, extraction lanes, publish rules, storage providers, or application paths.
- Managing ConfigKit import fragments or named profile overlay files.
- Managing credentials, secret values, a secrets directory, environment injection, or the pipeline-wide secret-delivery policy.
- Adding human operator accounts to the
narratiogroup; the suite role owns operator selection and membership. - A Narratio daemon, service unit, scheduler, or timer.
- A raw mapping for the complete pipeline document, a template override, or a second parallel configuration interface.
- Copying Narratio's built-in defaults into role defaults merely to render them back into YAML.
- Decomposing open-ended artifact, artifact-family, reference, or output maps into variables for caller-defined keys.
- Installing from published binary artifacts, cross-compiling, or supporting platforms outside the MVP.
- Production execution of Narratio to validate its binary or configuration.
- Detection of out-of-band binary modification, compiler changes, or fixed build-command changes.
- Build fingerprints, checksums, sidecar state, staged replacement, manual Git cleanup, or special recovery for malformed or adversarial host state.
Target End State
On a supported host, applying the role with the required
narratio_whisperx_transcribe_url installs or confirms Git and Go, creates the
Narratio account and filesystem roots, checks out Narratio v2.0.0 at
/usr/local/src/narratio, builds /usr/local/bin/narratio, and manages
/usr/local/etc/narratio/pipeline.yml.
The baseline managed pipeline document contains only the caller's required WhisperX URL and the filesystem roots the role deliberately owns. Narratio continues to supply all other application defaults. When a caller sets an optional configuration variable, the corresponding native YAML field appears in the correct section. Null fields and parent mappings with no selected descendants are absent. Explicit false, zero, empty string, empty list, empty mapping, and other non-null values are serialized rather than silently omitted, leaving their native semantics to Narratio.
Native open-ended collections pass through without structural rewriting. Callers and composing roles may define fragments in separate inventory files and combine them with ordinary Ansible/Jinja expressions before assigning the final collection variable. The role does not merge fragments or define precedence among them.
The narratio account owns the mutable workspace, spool, and cache roots.
Their setgid group-write permissions support later direct invocation by human
operators whom the suite role adds to the narratio group. The root-managed
campaign registry remains readable but not writable by those operators.
An immediate repeated application reports no changes. A changed checkout or a missing binary causes the build command to run. Account, directory, and configuration changes converge through their responsible Ansible modules and do not become build triggers.
Repository Ownership
ansible-role-narratioowns the role implementation, application account, runtime and campaign-root state, public variables, native root pipeline serialization, metadata, dependency declaration, documentation, and repository-local tests.../narratiois the read-only authority for release, build, CLI, configuration, filesystem, and integration contracts.rakestraw-ansibleis a read-only style and historical deployment reference.ansible-role-dnd-pipelinewill later own compatible application versions, D&D-specific Narratio values, campaign and prompt content, secret delivery, and human operator membership through the standalone roles' public interfaces.ansible-workspace-dndowns this roadmap, the eventual staged implementation plan, and aggregate status.- All other repositories remain unchanged.
Constraints and Settled Decisions
- The selected default release is lightweight tag
v2.0.0, resolving to commited4623bc415901134c765fe9cf06d59aa7891500. - The release declares Go 1.26.0. The suite-standard
1.26/stableGo Snap channel satisfies that requirement. - The native build uses package
./cmd/narratio,CGO_ENABLED=0,GOWORK=off,GOTOOLCHAIN=local,-trimpath, and linker flags-s -w -X gitea.maximumdirect.net/eric/narratio/internal/buildinfo.Version=<version>. narratio versionreports the embedded selected tag and provides the native observable for an explicitly requested version in integration tests.- Narratio discovers
/usr/local/etc/narratio/pipeline.ymlbefore/etc/narratio/pipeline.yml; the role manages the first path by default. whisperx.transcribe_urlis the only pipeline field without an application default that is required for pipeline validation. The role therefore exposesnarratio_whisperx_transcribe_urlas a required public input rather than inventing a deployment endpoint.- Workspace, campaign, spool, and cache roots are concrete role defaults because the role creates and owns the corresponding filesystem state. These are intentional deployment policy, not a duplicate database of application defaults.
- All other optional configuration variables use YAML null defaults and
template conditions use
is not none. Parent mappings are emitted only when at least one descendant is non-null. - Strings and collections are serialized with Ansible or Jinja filters rather than hand-written YAML quoting.
narratio_publish_outputs,narratio_notarius_references,narratio_notarius_outputs,narratio_scriptorium_artifacts, andnarratio_scriptorium_artifact_familiesrepresent their complete native fields. Caller-side composition is permitted, but the role supplies no merge behavior.- Credential configuration contains environment-variable names only. Secret values remain outside the role and managed pipeline document.
- The configuration directory is
root:narratiomode0750; the pipeline file isroot:narratiomode0640. - Mutable runtime roots are
narratio:narratiomode2775. The campaign root isroot:narratiomode0755because version-controlled content is populated by a separate owner and is not operator-edited on the host. - Tasks installing dependencies or managing privileged state use task-level
become: true. community.generalsupplies Snap management and is pinned to version13.3.0in the role's collection requirements.
Fixed Public Interface
Installation and Managed Filesystem
| Variable | Default | Purpose |
|---|---|---|
narratio_version |
v2.0.0 |
Selected Git revision and embedded build version |
narratio_repository_url |
https://gitea.maximumdirect.net/eric/narratio.git |
Source repository |
narratio_source_dir |
/usr/local/src/narratio |
Managed source checkout |
narratio_binary_path |
/usr/local/bin/narratio |
Installed executable |
narratio_manage_build_dependencies |
true |
Whether to install Git and Go |
narratio_go_snap_channel |
1.26/stable |
Go Snap channel |
narratio_go_binary |
/snap/bin/go |
Go executable used for the build |
narratio_user |
narratio |
System account owning mutable runtime state |
narratio_group |
narratio |
System group sharing mutable runtime state |
narratio_config_path |
/usr/local/etc/narratio/pipeline.yml |
Managed root pipeline document |
narratio_workspace_root |
/var/lib/narratio |
Managed workspace root and account home |
narratio_campaigns_root |
/usr/local/share/narratio/campaigns |
Managed campaign registry root |
narratio_spool_root |
/var/spool/narratio |
Managed spool root |
narratio_cache_root |
/var/cache/narratio |
Managed cache root |
Changing a managed root changes both its rendered pipeline value and the directory the role manages. The role does not infer additional arbitrary paths embedded inside caller-supplied open-ended collections.
Pipeline Structure and Storage
Except where a default is shown, every variable below defaults to null and is omitted when null.
| Variable | Default | Native field |
|---|---|---|
narratio_configkit_imports |
null |
configkit.imports |
narratio_workspace_cleanup_after_publish |
null |
workspace.cleanup_after_publish |
narratio_campaigns_default_campaign_id |
null |
campaigns.default_campaign_id |
narratio_storage_backend |
null |
storage.backend |
narratio_storage_s3_bucket |
null |
storage.s3.bucket |
narratio_storage_s3_root_prefix |
null |
storage.s3.root_prefix |
narratio_storage_s3_region |
null |
storage.s3.region |
narratio_storage_s3_endpoint |
null |
storage.s3.endpoint |
narratio_storage_s3_force_path_style |
null |
storage.s3.force_path_style |
narratio_storage_s3_access_key_id |
null |
storage.s3.access_key_id environment-variable name |
narratio_storage_s3_secret_access_key |
null |
storage.s3.secret_access_key environment-variable name |
narratio_spool_delete_audio_after_publish |
null |
spool.delete_audio_after_publish |
narratio_cache_s3_audio |
null |
cache.s3_audio |
narratio_publish_enabled |
null |
publish.enabled |
narratio_publish_upload_run |
null |
publish.upload_run |
narratio_publish_outputs |
null |
Complete native publish.outputs list |
WhisperX, Seriatim, and Audita
| Variable | Default | Native field |
|---|---|---|
narratio_whisperx_transcribe_url |
required | whisperx.transcribe_url |
narratio_whisperx_language |
null |
whisperx.language |
narratio_whisperx_timeout |
null |
whisperx.timeout |
narratio_whisperx_retries |
null |
whisperx.retries |
narratio_whisperx_retry_delay |
null |
whisperx.retry_delay |
narratio_whisperx_concurrency |
null |
whisperx.concurrency |
narratio_seriatim_binary |
null |
seriatim.binary |
narratio_seriatim_timeout |
null |
seriatim.timeout |
narratio_seriatim_output_schema |
null |
seriatim.output_schema |
narratio_seriatim_coalesce_gap |
null |
seriatim.coalesce_gap |
narratio_seriatim_report |
null |
seriatim.report |
narratio_seriatim_env_overlap_word_run_gap |
null |
seriatim.env.overlap_word_run_gap |
narratio_seriatim_env_overlap_word_run_reorder_window |
null |
seriatim.env.overlap_word_run_reorder_window |
narratio_seriatim_env_backchannel_max_duration |
null |
seriatim.env.backchannel_max_duration |
narratio_seriatim_env_filler_max_duration |
null |
seriatim.env.filler_max_duration |
narratio_audita_binary |
null |
audita.binary |
narratio_audita_timeout |
null |
audita.timeout |
narratio_audita_llm_api_key |
null |
audita.llm_api_key environment-variable name |
narratio_audita_modules |
null |
audita.modules |
narratio_audita_base_url |
null |
audita.base_url |
narratio_audita_model |
null |
audita.model |
narratio_audita_total_llm_concurrency |
null |
audita.total_llm_concurrency |
narratio_audita_proposal_llm_concurrency |
null |
audita.proposal_llm_concurrency |
narratio_audita_validation_model |
null |
audita.validation_model |
narratio_audita_validation_llm_concurrency |
null |
audita.validation_llm_concurrency |
narratio_audita_transcript_description |
null |
audita.transcript_description |
narratio_audita_config_path |
null |
audita.config_path |
narratio_audita_output_schema |
null |
audita.output_schema |
narratio_audita_work_dir_retention |
null |
audita.work_dir_retention |
narratio_audita_report |
null |
audita.report |
Transcript Stages and Rendering
| Variable | Default | Native field |
|---|---|---|
narratio_normalize_output_path |
null |
normalize.output_path |
narratio_normalize_output_schema |
null |
normalize.output_schema |
narratio_normalize_report |
null |
normalize.report |
narratio_trim_enabled |
null |
trim.enabled |
narratio_trim_output_path |
null |
trim.output_path |
narratio_trim_bounds_prompt_id |
null |
trim.bounds.prompt_id |
narratio_trim_bounds_profile_id |
null |
trim.bounds.profile_id |
narratio_trim_bounds_transcript_input_name |
null |
trim.bounds.transcript_input_name |
narratio_trim_bounds_output_path |
null |
trim.bounds.output_path |
narratio_trim_bounds_timeout |
null |
trim.bounds.timeout |
narratio_trim_bounds_render_debug |
null |
trim.bounds.render_debug |
narratio_trim_bounds_render_output_path |
null |
trim.bounds.render_output_path |
narratio_trim_seriatim_report |
null |
trim.seriatim.report |
narratio_render_enabled |
null |
render.enabled |
narratio_render_format |
null |
render.format |
narratio_render_title |
null |
render.title |
narratio_render_include_timestamps |
null |
render.include_timestamps |
narratio_render_include_segment_ids |
null |
render.include_segment_ids |
narratio_render_include_metadata |
null |
render.include_metadata |
Scriptorium, Notarius, and Notification
| Variable | Default | Native field |
|---|---|---|
narratio_scriptorium_binary |
null |
scriptorium.binary |
narratio_scriptorium_config_path |
null |
scriptorium.config_path |
narratio_scriptorium_timeout |
null |
scriptorium.timeout |
narratio_scriptorium_render_debug |
null |
scriptorium.render_debug |
narratio_scriptorium_artifacts |
null |
Complete native scriptorium.artifacts map |
narratio_scriptorium_artifact_families |
null |
Complete native scriptorium.artifact_families map |
narratio_notarius_enabled |
null |
notarius.enabled |
narratio_notarius_binary |
null |
notarius.binary |
narratio_notarius_config_path |
null |
notarius.config_path |
narratio_notarius_pipeline_id |
null |
notarius.pipeline_id |
narratio_notarius_timeout |
null |
notarius.timeout |
narratio_notarius_working_directory |
null |
notarius.working_directory |
narratio_notarius_references |
null |
Complete native notarius.references map |
narratio_notarius_outputs |
null |
Complete native notarius.outputs map |
narratio_notification_mode |
null |
notification.mode |
Simplest Viable Implementation
The production task flow is:
- Assert the supported platform and architecture.
- Install Git and Go when dependency management is enabled.
- Create the Narratio system group and account.
- Create the source and configuration parent directories.
- Create the mutable runtime roots and read-only campaign registry root.
- Check out the selected Narratio revision with
ansible.builtin.git. - Inspect only whether the installed binary exists, because that result is needed to decide whether the imperative build command must run.
- Build directly to the selected binary path when the checkout changed or the binary is absent.
- Converge binary ownership and mode with
ansible.builtin.file. - Render the root pipeline document with one
ansible.builtin.templatetask.
The build command uses narratio_go_binary and runs only under the two stated
conditions. Ordinary modules own all other state checks. No role-private state,
pre-build version interrogation, post-build validation, configuration
validation, or redundant filesystem inspection belongs in production tasks.
The template is necessarily larger than those of the downstream roles because
Narratio's native pipeline schema is larger. Its logic remains mechanical:
serialize the required URL and role-owned roots, test each optional field with
is not none, emit a parent section only when needed, and pass native
collections through unchanged.
Rationale and Alternatives
Narratio's configuration is broad, but a field-specific interface preserves discoverability, argument validation, stable inventory names, and a clean composition boundary for the suite role. A raw complete-document variable would make the template shorter but would discard those benefits and conflict with the established configuration policy. Open-ended native collections are kept whole because decomposing caller-defined keys would create a second schema and make inventory composition harder.
Requiring the WhisperX URL is preferable to installing a configuration that Narratio cannot validate or inventing an endpoint that might silently direct work to the wrong service. The baseline integration invocation supplies a non-contacted test URL; production callers supply their actual endpoint.
The standalone role creates the application account and directory permissions because those are Narratio runtime requirements. It does not select human operators, because membership is deployment-wide policy and belongs in the suite role.
ConfigKit imports and profiles remain available to advanced callers, but the role manages only the canonical root document. This avoids a parallel raw-file deployment subsystem. A caller that selects imports or profiles is responsible for installing those additional files by another role or play.
Acceptance Criteria
Required Role Behavior
- The role rejects unsupported platforms before modifying application state.
- On a supported host, it installs the selected Narratio source release and builds the binary with the upstream release contract.
- The system account, source, binary, configuration, runtime roots, and campaign root converge to the paths, ownership, and modes selected above through the responsible Ansible modules.
- With only
narratio_whisperx_transcribe_urlsupplied, the rendered pipeline contains that URL and the four role-owned roots, omits unselected optional policy, and is accepted by Narratio's native configuration validator. - Each non-null configuration variable renders its corresponding native field. Null fields and empty parent sections are omitted, while intentional falsey values are retained.
- Caller-supplied open-ended collections are rendered without structural rewriting, and independently defined values can be composed before assignment.
- A changed checkout or missing binary causes the imperative build to run. An immediate repeated application with unchanged inputs reports no changes.
- Dependency opt-out accepts a caller-supplied
narratio_go_binary. - Root-owned operations use task-level privilege escalation; play-level escalation is not required.
- Defaults, argument specifications, template behavior, README, and metadata remain aligned with the public interface.
- Production tasks contain no role-private state, redundant probes, application self-tests, or speculative guarantees.
- No repository other than
ansible-role-narratioand the workspace control repository is modified.
Required Validation
- Syntax checking and production-profile ansible-lint pass.
- A baseline invocation supplies only the required WhisperX URL, exercises the installed application by validating the managed pipeline as the Narratio account, and is followed immediately by a role application reporting no changes. It does not snapshot the role's current default tag or complete default document.
- One explicitly selected-version case requests
v2.0.0and verifies thatnarratio versionreportsv2.0.0. The assertion proves caller-controlled version selection, not thatv2.0.0remains the role default. - One recovery case removes the binary, disables dependency management, supplies a caller-selected Go executable, confirms that executable is used, and finishes with a clean repeated application.
- One representative configuration case verifies selected scalar and nested
values, selected omissions, intentional falsey values, and composed
open-ended collections. Narratio's native
config validateaccepts the managed pipeline, and an immediate repeated application reports no changes. - Tests do not assert task names or order, individual task results, exact current defaults, complete configuration snapshots, public-variable counts, trusted-module account, directory, file, package, checkout, or template postconditions, or an exhaustive negative platform matrix.
Dependencies and Follow-up Work
The managed host requires access to the Narratio Git repository, Ubuntu Snap infrastructure, and Go module sources. Useful pipeline execution also requires a reachable WhisperX service, the four downstream application binaries and their configuration, campaign and session inputs, prompt content, and any selected object-storage or model credentials. Those remain caller responsibilities for this standalone feature.
With this feature complete, all five standalone application roles have stable public interfaces. The next unit can design the suite-level pipeline role, including dependency composition, compatible version selection, D&D pipeline policy, campaign and prompt deployment, secrets, and operator group membership.
The role was implemented in ansible-role-narratio commits 3be78d8 and
0e8ad9f, then finalized after independent review by remediation commit
7feff47. Its complete integration suite passes.
Potential Future Hardening
- Build into a temporary path before replacing the installed executable.
- Verify source signatures or release artifacts.
- Detect compiler, build-command, or out-of-band binary changes.
- Validate configuration during production convergence.
- Manage imported fragments or profile overlays if a concrete standalone-role use case requires them.
- Add a scheduler, service, or non-interactive execution environment if later operating requirements select one.
- Support Narratio's published binary artifacts as part of a future suite-wide installation-model change.
- Add broader platform coverage only when supported-platform behavior expands.