Files
ansible-workspace-dnd/docs/roadmap/features/0003-notarius-source-installation-and-configuration.md

20 KiB

Feature 0003: Notarius Source Installation and Configuration

Status: done

Summary

Provide a small, independently usable rakestraw.notarius role that installs Notarius from a pinned Git tag, builds its executable on the managed host, and manages its versioned YAML configuration. Reuse the established module-driven application-role pattern while leaving D&D pipeline policy to the suite-level role.

Problem or Opportunity

At feature selection, the ansible-role-notarius repository contained only its initial placeholder files. Notarius is one of the five required pipeline applications and needed an independently usable installation and native-configuration role before the suite role could compose it with Seriatim, Audita, and the remaining applications.

Seriatim and Audita have established the preferred source-build architecture. Notarius can use the same concise task flow, with only its native configuration and build contract differing.

Governing Policies

  • The Ansible house style governs the role implementation, its public configuration variables, and its template behavior.
  • The testing house style governs repository-local validation. Tests exercise role-owned behavior through the public interface without snapshotting current defaults, tracing task implementation, or rechecking postconditions owned by trusted Ansible modules.

Scope

  • Implement ansible-role-notarius as a conventional standalone role.
  • Support Ubuntu 26.04 on native amd64 and arm64 hosts.
  • 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 Notarius tag v0.6.0 by default under the root-owned path /usr/local/src/notarius.
  • Build Notarius with its upstream release build contract and install the native executable at /usr/local/bin/notarius.
  • Rebuild when the Git checkout changes or the binary is absent.
  • Converge the installed binary to root:root ownership and mode 0755.
  • Manage a versioned Notarius YAML configuration at /usr/local/etc/notarius/config.yml, owned by root:root with mode 0644.
  • Default the managed configuration to the minimal valid mapping {"version": 4}, allowing Notarius's built-in defaults to govern behavior.
  • Expose native configuration through field-specific role variables. Give the required schema version a concrete default and make every optional setting nullable so omitted settings remain owned by Notarius.
  • Keep defaults, argument specifications, README, metadata, dependency declaration, and focused local tests consistent with this contract.

Non-goals

  • A D&D pipeline definition, module selection, LLM profile, concurrency policy, output paths, cache policy, or other suite-specific configuration.
  • A raw mapping for the complete configuration document, a template override, or competing configuration interfaces.
  • Role defaults copied from Notarius merely to reproduce the application's built-in defaults in YAML.
  • Decomposing the open-ended pipelines map into role variables for every possible pipeline, step, lane, binding, validator, option, or reference.
  • API keys, secret values, environment files, or another secret-delivery mechanism.
  • PromptKit profiles or other externally maintained prompt content.
  • Creation or management of the narratio account, operator groups, campaign data, runtime output, debug, cache, checkpoint, or transcript directories.
  • Installation or configuration of another pipeline application.
  • Services, scheduling, removal behavior, or pipeline composition.
  • Prebuilt release artifacts or cross-compilation.
  • Production execution of Notarius to validate the build 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.
  • A shared build helper role or dedicated platform-matrix infrastructure.

Target End State

On an Ubuntu 26.04 amd64 or arm64 host, applying the role with defaults installs or confirms Git and Go, checks out Notarius v0.6.0 at /usr/local/src/notarius, builds /usr/local/bin/notarius, and writes /usr/local/etc/notarius/config.yml. Source and installed files have the stated root ownership and permissions, and the configuration contains version: 4.

When a caller sets an optional configuration variable, the corresponding native field appears in the correct YAML section. Null fields are absent, as are parent sections with no selected descendants. Explicit false, zero, empty string, empty collection, and other non-null values are rendered and left to Notarius's native validation. notarius_pipelines represents the native open-ended pipelines field as one mapping rather than attempting to mirror its extensible internal schema in Ansible variables. Callers may define pipeline-map fragments in separate inventory files and compose them into the final value with ordinary Ansible/Jinja expressions.

An immediate second application reports no changes. A changed Git checkout or missing binary causes one build; configuration changes converge independently through the ordinary ansible.builtin.template operation.

Repository-local tests exercise the real role through its public variables. They use the default invocation only as a smoke test, explicitly select immutable v0.5.0 to prove version selection, exercise missing-binary recovery with a caller-supplied Go executable, and render one representative configuration covering null omission, intentional falsey values, and caller-side pipeline composition. Application-native commands verify the selected release and configuration validity, and immediate repeated applications verify convergence.

Repository Ownership

  • ansible-role-notarius owns the role implementation, public interface, configuration serialization, metadata, dependency declaration, README, and repository-local validation.
  • ../notarius is the read-only authority for build, CLI, and configuration contracts.
  • rakestraw-ansible is a read-only style and deployment-policy reference.
  • ansible-role-dnd-pipeline will later own the D&D-specific configuration supplied through the standalone role's field-specific variables.
  • ansible-workspace-dnd owns this roadmap, the eventual staged plan, and aggregate status.
  • All other repositories remain unchanged.

Constraints and Settled Decisions

  • The selected release is tag v0.6.0, which resolves to commit 77ae132e5609916da40dcd4fcc749c000a63cbac.
  • The release declares Go 1.25.5. The suite-standard Go Snap channel remains 1.26/stable and satisfies that requirement.
  • The upstream release build uses package ./cmd/notarius, CGO_ENABLED=0, GOWORK=off, GOTOOLCHAIN=local, -trimpath, and linker flag -X gitea.maximumdirect.net/eric/notarius/internal/buildinfo.Override=<version>.
  • notarius --version prints notarius <version>. This is test evidence, not an additional production rebuild trigger or post-build convergence task.
  • Notarius configuration schema version 4 explicitly supports a document containing only version: 4, with omitted values supplied by application defaults.
  • notarius_config_version names the schema version so notarius_version remains unambiguously the selected application release.
  • Optional configuration variables are declared with YAML null defaults. Template conditions use is not none, and parent mappings are emitted only when at least one descendant is non-null.
  • The native pipelines field is exposed as the nullable notarius_pipelines mapping. It is one field-specific variable, not a raw mapping for the complete document; the role serializes its value without defining a parallel schema for pipeline internals.
  • Pipeline composition belongs to the caller. For example, inventory may define notarius_pipeline_group_one and notarius_pipeline_group_two as mappings and set notarius_pipelines to {{ notarius_pipeline_group_one | combine(notarius_pipeline_group_two) }}. The role accepts and renders the resulting mapping without its own merge or precedence mechanism.
  • Strings and mappings are serialized with Ansible or Jinja filters rather than hand-written YAML quoting.
  • notarius config validate --config <path> validates a managed configuration without requiring a pipeline selection. It is used only by tests.
  • Configuration discovery uses an explicit CLI path, then NOTARIUS_CONFIG, then /usr/local/etc/notarius/config.yml; the role manages the installed default path.
  • Configuration contains no secrets by default. Consumers arrange any secret environment variables outside this feature.
  • Tasks that install dependencies or manage root-owned state use task-level become: true.
  • community.general supplies Snap management and is pinned to version 13.3.0 in the role's collection requirements.
  • Tag v0.5.0 remains buildable with the same Go requirement, package, linker symbol, and configuration schema and is the explicit immutable release used for focused native version-selection coverage.

Fixed Public Interface

Provide these installation variables:

Variable Default Purpose
notarius_version v0.6.0 Git revision and release value supplied to the build
notarius_repository_url https://gitea.maximumdirect.net/eric/notarius.git Source repository
notarius_source_dir /usr/local/src/notarius Managed source checkout
notarius_binary_path /usr/local/bin/notarius Installed executable
notarius_manage_build_dependencies true Whether to install Git and Go
notarius_go_snap_channel 1.26/stable Go Snap channel
notarius_go_binary /snap/bin/go Go executable used for the build
notarius_config_path /usr/local/etc/notarius/config.yml Managed configuration file

Provide these configuration variables:

Variable Default Native field
notarius_config_version 4 version
notarius_promptkit_profile_dir null promptkit.profile_dir
notarius_promptkit_profile_file null promptkit.profile_file
notarius_promptkit_local_backend_endpoint null promptkit.local_backend.endpoint
notarius_promptkit_local_backend_concurrency_limit null promptkit.local_backend.concurrency_limit
notarius_pipelines null pipelines
notarius_concurrency_total_llm null concurrency.total_llm
notarius_concurrency_stage_workers_extract null concurrency.stage_workers.extract
notarius_output_directory null output.directory
notarius_cache_chunk_plans_mode null cache.chunk_plans.mode
notarius_cache_chunk_plans_directory null cache.chunk_plans.directory
notarius_cache_checkpoints_enabled null cache.checkpoints.enabled
notarius_cache_checkpoints_directory null cache.checkpoints.directory
notarius_debug_directory null debug.directory

The package, build flags, linker symbol, environment values, file ownership, and modes are fixed implementation details. The selected version is both the Git revision and the value supplied to the upstream linker flag.

Argument specifications type each value according to its native shape. The role does not reproduce semantic relationships such as the mutual exclusion of PromptKit profile sources or the requirement for a local-backend endpoint; Notarius owns those constraints. The notarius_pipelines value is a mapping whose nested structure is governed by Notarius's versioned configuration contract.

Simplest Viable Implementation

The expected implementation is approximately nine ordinary tasks in one readable task file:

  1. Assert Ubuntu 26.04 and architecture x86_64 or aarch64.
  2. Install Git with ansible.builtin.apt, including cache_valid_time: 3600, when dependency management is enabled.
  3. Install the classic Go Snap from notarius_go_snap_channel with community.general.snap when dependency management is enabled.
  4. Create source, binary, and configuration parent directories with one ansible.builtin.file loop.
  5. Check out notarius_version with ansible.builtin.git, force: true, and register the module result.
  6. Inspect whether notarius_binary_path exists with ansible.builtin.stat.
  7. Run the upstream Go build through notarius_go_binary when the checkout changed or the binary is absent.
  8. Converge recursive source ownership and binary ownership and mode with one item-driven ansible.builtin.file loop.
  9. Render the field-specific configuration with one ansible.builtin.template task, conditionally emitting optional fields and parent sections at notarius_config_path.

Apply task-level privilege escalation only where required. Use argv for the build command and put its two conditions directly on the build task. Do not add a rebuild fact, handler, handler flush, executable discovery, compiler or version interrogation, configuration assembly facts, preliminary configuration probes, or post-operation validation to production tasks.

Rationale and Alternatives

Notarius has the same installation mechanics as the completed Seriatim and Audita roles: declarative dependency and checkout management surrounding one imperative build command. Binary existence is the only state probe Ansible needs because it controls whether that command must run.

Field-specific variables are the ordinary ergonomic interface for inventory and composing roles. Nullable defaults expose and type that interface without freezing Notarius's built-in defaults into Ansible. Conditional rendering preserves the distinction between omission and an intentionally falsey value.

The pipeline definitions are the deliberate exception to leaf-level variables. pipelines is itself an open-ended map whose nested keys depend on registered modules and evolve with the application's native schema. Exposing that native field as one nullable mapping keeps the interface practical while still avoiding a raw complete-document mapping or merge semantics. The suite role can later supply its maintained D&D pipeline through notarius_pipelines and supply operating policy through the other variables.

This boundary also supports modular inventory. A consumer can keep one or more pipeline mappings in separate host_vars, group_vars, or included variable files and combine them when assigning notarius_pipelines. Because the native value is a mapping keyed by pipeline ID, dictionary composition uses Ansible's combine filter; list concatenation with + applies only to list-valued data. The role receives the already composed native mapping and does not need custom loading, merging, or precedence behavior.

Leaving optional variables undefined would produce the same minimal file but would weaken interface documentation and argument validation. Section-level dictionaries would be less ergonomic for ordinary inventory values. A raw complete configuration mapping would force consumers to replace the required version and unrelated sections together and would conflict with the workspace convention of one field-specific interface.

Managing /usr/local/etc/notarius/config.yml follows the application's own configuration discovery contract and aligns locally installed software with its configuration.

Acceptance Criteria

Required Role Behavior

  • The role rejects unsupported distributions, Ubuntu releases, and architectures before modifying application state.
  • On a supported host, the role installs the selected Notarius source release with its upstream build contract. The default selection for this feature is tag v0.6.0, resolving to commit 77ae132e5609916da40dcd4fcc749c000a63cbac.
  • The role converges the source tree, binary, and configuration to the paths, ownership, and modes defined above using the responsible Ansible modules.
  • The default template renders the minimal valid configuration containing the required schema version and leaves optional policy to Notarius.
  • A non-null optional variable renders its corresponding native field. Null values and parent sections without a selected descendant are omitted; intentional falsey values are preserved.
  • Independently defined inventory mappings can be combined into notarius_pipelines, and the resulting pipeline IDs and definitions are rendered without structural rewriting by the role.
  • 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 notarius_go_binary.
  • Tasks requiring elevated access use task-level privilege escalation, so the role does not require play-level escalation.
  • Defaults, argument specifications, template behavior, and documentation remain aligned with the public interface without introducing a parallel raw configuration or template-override interface.
  • Production tasks contain no role-private build state, redundant probes, application self-tests, or other behavior outside the selected invariants.
  • No repository other than ansible-role-notarius and the workspace control repository is modified.

Required Validation

  • Syntax checking and production-profile ansible-lint pass.
  • A default invocation provides a smoke test through the installed CLI and native configuration validator, followed immediately by a role application reporting no changes. The test does not assert that the role's current default release or complete default document remains unchanged.
  • A focused version-selection case explicitly requests immutable tag v0.5.0 and verifies that the installed CLI reports notarius v0.5.0.
  • 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 rendered values, selected omissions, intentional falsey values, and caller-side composition of two pipeline mappings. The native Notarius validator accepts the result, 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 file or package postconditions, or an exhaustive negative platform matrix.

Dependencies and Follow-up Work

The managed host requires access to the Notarius Git repository, Ubuntu Snap infrastructure, and Go module sources. Actual transcript processing also needs a configured pipeline, input artifacts, PromptKit assets and endpoints, and secret environment variables; those belong to consumers or later suite-level work.

The role was implemented in ansible-role-notarius commits 287fc4a and 733a194, then finalized by remediation commit eb4f511. Its complete integration suite passes. The pipeline role can now supply its D&D definitions through notarius_pipelines and select the other field-specific values.

Potential Future Hardening

  • Build into a temporary path before replacing the installed executable.
  • Verify source signatures or release checksums.
  • Detect compiler, build-command, or out-of-band binary changes.
  • Validate configuration during production convergence.
  • Manage a dedicated runtime account, secret environment, state paths, service, or schedule if later operating requirements select them.
  • Add broader Ubuntu release and architecture test coverage only if future platform behavior warrants it.