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

365 lines
20 KiB
Markdown

# 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](../../ansible.md) governs the role implementation,
its public configuration variables, and its template behavior.
- [The testing house style](../../testing.md) 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.