From a3af153895d81d3185e4759e942943d54fbe21de Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Tue, 8 Sep 2026 14:03:14 +0000 Subject: [PATCH] Complete the Audita amendment and resume Notarius planning --- docs/conventions.md | 41 ++ ...a-source-installation-and-configuration.md | 391 +++++++++--------- ...s-source-installation-and-configuration.md | 253 ++++++++++++ docs/roadmap/implementation.md | 10 +- docs/roadmap/remediation.md | 150 ++----- docs/status.md | 9 +- 6 files changed, 528 insertions(+), 326 deletions(-) create mode 100644 docs/roadmap/features/0003-notarius-source-installation-and-configuration.md diff --git a/docs/conventions.md b/docs/conventions.md index 8e1a294..fc1d3bb 100644 --- a/docs/conventions.md +++ b/docs/conventions.md @@ -65,3 +65,44 @@ - Treat `rakestraw-ansible` as a style reference for direct tasks, shallow control flow, and concise variable use, while retaining current practices such as fully qualified collection names. + +## Application Configuration Interfaces + +- Expose application configuration through documented, field-specific role + variables suitable for `host_vars`, `group_vars`, play variables, and + composing roles. +- Give structurally required fields concrete defaults. Define optional + application settings as `null` unless the role intentionally owns the + corresponding policy. +- Treat a null optional variable as "omit this field from the rendered + configuration." Do not copy documented upstream defaults into role defaults + merely to write them back to the application. +- Let the application supply its built-in defaults for omitted fields. This + avoids freezing a duplicate defaults database in Ansible and permits upstream + default changes to take effect naturally. +- Use `is not none` when conditionally rendering optional values. Do not use + truthiness, because false, zero, an empty string, or an empty collection may + be an intentional value. +- Emit a nested configuration section only when at least one of its descendant + variables is non-null. +- Serialize strings and collections with appropriate Ansible or Jinja filters. + Do not construct YAML quoting manually. +- Keep argument specifications aligned with the public variables. Optional + nullable variables are not required; use a permissive type only when the + native application accepts more than one scalar type. +- Prefer one field-specific interface. Do not provide a parallel raw mapping, + template override, or merge mechanism unless a concrete requirement defines + its precedence and behavior. +- Application roles expose native configuration choices. The suite-level role + supplies cross-application and deployment-specific policy through those + variables. +- Manage the resulting file with one ordinary `ansible.builtin.template` task. + Do not add configuration assembly facts, preliminary file inspection, or + production validation unless independently required. +- Tests should prove the minimal default document, representative rendering + across all sections, omission of null fields, preservation of intentional + falsey values, native configuration validity, and repeated convergence. + +Role defaults define what Ansible intends to manage, not a mirror of every +application default. A concrete default means the role deliberately owns that +policy; a null default leaves the policy with the application. diff --git a/docs/roadmap/features/0002-audita-source-installation-and-configuration.md b/docs/roadmap/features/0002-audita-source-installation-and-configuration.md index 7ef2c33..1a79286 100644 --- a/docs/roadmap/features/0002-audita-source-installation-and-configuration.md +++ b/docs/roadmap/features/0002-audita-source-installation-and-configuration.md @@ -6,243 +6,244 @@ Status: done Provide a small, independently usable `rakestraw.audita` role that installs Audita from a pinned Git tag, builds its executable on the managed host, and -manages its versioned YAML configuration. Follow the module-driven Seriatim -pattern while adding only the configuration behavior that distinguishes -Audita. +offers an ergonomic field-specific interface for its versioned YAML +configuration. Unspecified optional fields are omitted so Audita retains +ownership of its application defaults. ## Problem or Opportunity -The `ansible-role-audita` repository currently contains only its initial -placeholder files. Audita needs a standalone installer before the suite role -can compose the transcript-processing pipeline and supply D&D-specific runtime -policy. +The completed source-installation flow is concise and correct, but the initial +configuration interface exposes only one `audita_config` mapping. That requires +every caller to know and reproduce Audita's native nested schema, and it does +not provide the ordinary role ergonomics expected for inventory variables. -An earlier implementation direction depended on the superseded Seriatim -fingerprint, sidecar-state, staged-build, and corruption-recovery architecture. -This roadmap replaces that direction with a concise implementation that trusts -Ansible modules, rebuilds only for required conditions, and keeps production -role tasks separate from integration verification. +The role should expose documented variables for individual settings and render +only values expressly selected by the caller. This permits straightforward +`host_vars`, `group_vars`, play variables, and suite-role overrides while +allowing upstream default changes to take effect automatically when a setting +is not managed by Ansible. ## Scope -- Implement `ansible-role-audita` 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 Audita tag `v0.9.1` by default under the root-owned path - `/usr/local/src/audita`. -- Build Audita with its upstream release build contract and install the native - executable at `/usr/local/bin/audita`. -- 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 Audita YAML configuration at - `/usr/local/etc/audita/config.yml`, owned by `root:root` with mode `0644`. -- Default the managed configuration to the minimal valid mapping - `{"version": 1}`, allowing Audita's own defaults to govern application - behavior. -- Keep role defaults, argument specifications, README, metadata, dependency - declaration, and focused local tests consistent with this contract. +- Retain the completed Ubuntu 26.04 `amd64` and `arm64` source installation. +- Retain default tag `v0.9.1`, Go Snap channel `1.26/stable`, source path + `/usr/local/src/audita`, and binary path `/usr/local/bin/audita`. +- Retain rebuild triggers of changed Git checkout or absent binary. +- Replace the complete `audita_config` mapping with typed, field-specific + configuration variables. +- Always render the required configuration schema version, defaulting to `1`. +- Define every optional configuration variable as `null` by default and omit + its YAML field when it remains null. +- Render nested YAML sections only when at least one setting in that section is + non-null. +- Manage `/usr/local/etc/audita/config.yml` from a Jinja template as + `root:root` mode `0644`. +- Keep defaults, argument specifications, template, README, metadata, + dependency declaration, and focused tests consistent with this contract. ## Non-goals +- Copying Audita's built-in values into role defaults or the default rendered + file. - D&D-specific model selection, concurrency, thresholds, transcript context, - or other suite policy. The suite-level role will provide those overrides. -- A separate Ansible variable for every field in Audita's configuration - schema. -- API keys, secret values, environment files, or another secret-delivery - mechanism. Configuration may name environment variables but must not contain - their secret values. -- Creation or management of the `narratio` account, operator groups, campaign - data, glossaries, transcript data, or diagnostics directories. -- Installation or configuration of another pipeline application. -- Services, scheduling, removal behavior, or pipeline composition. -- Prebuilt release artifacts or cross-compilation. -- Production execution of Audita 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. + or other suite policy. +- A parallel mapping-based configuration escape hatch or merge/precedence + rules between two configuration interfaces. +- API keys or other secret values. Variables may select environment-variable + names, but the role does not manage their values. +- Creation of the `narratio` account or management of runtime, campaign, + glossary, transcript, diagnostics, or secret-delivery infrastructure. +- Services, scheduling, removal behavior, pipeline composition, prebuilt + artifacts, or cross-compilation. +- Production execution of Audita for build or configuration validation. +- Fingerprints, checksums, sidecar state, staged replacement, manual Git + cleanup, compiler inspection, or out-of-band drift detection. ## 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 Audita `v0.9.1` at -`/usr/local/src/audita`, builds `/usr/local/bin/audita`, and writes -`/usr/local/etc/audita/config.yml`. Source and installed files have the stated -root ownership and permissions, and the configuration contains `version: 1`. +With no configuration overrides, the role writes the minimal valid document: -An immediate second application reports no changes. A changed Git checkout or -missing binary causes one build; configuration changes converge through the -ordinary file-management module without rebuilding Audita. Repository-local -tests execute the installed CLI to verify the build and validate the managed -configuration. +```yaml +version: 1 +``` + +When a caller sets an optional role variable, the corresponding native Audita +field appears in the correct YAML section. Fields whose variables remain null +are absent, including their parent sections when no child is selected. Empty +lists, empty strings, zero, and other expressly supplied non-null values are +rendered rather than mistaken for omission and are left for Audita to validate. + +Installation and configuration converge independently. A configuration-only +change does not rebuild the binary, and an immediate repeat reports no changes. +Repository-local tests execute Audita to verify the installed version and +validate both minimal and representative managed configurations. ## Repository Ownership -- `ansible-role-audita` owns the role implementation, public interface, - configuration rendering, metadata, dependency declaration, README, and - repository-local validation. -- `../audita` is the read-only authority for the build, CLI, and configuration - contracts. -- `rakestraw-ansible` is a read-only style and configuration-policy reference. -- `ansible-role-dnd-pipeline` will later own the D&D-specific values supplied - through the standalone role's configuration interface. -- `ansible-workspace-dnd` owns this roadmap, the eventual staged implementation - plan, and aggregate status. +- `ansible-role-audita` owns installation, the public configuration interface, + template, metadata, README, and repository-local tests. +- `../audita` is the read-only authority for build, CLI, configuration schema, + and application-default contracts. +- `rakestraw-ansible` is a read-only style reference for field-specific role + variables and conditional templates; its historical values are not + authoritative defaults. +- `ansible-role-dnd-pipeline` will later own D&D-specific overrides supplied + through the standalone role variables. +- `ansible-workspace-dnd` owns this roadmap, the staged plan, and aggregate + status. - All other repositories remain unchanged. ## Constraints and Settled Decisions -- The selected release is tag `v0.9.1`, which resolves to commit - `a84941d68168a1ed9ea7423b6477ae8218be73f9`. -- Tags `v0.9.0` and `v0.9.1` resolve to the same commit, so `v0.9.0` cannot - exercise checkout-change rebuilding. Focused integration may temporarily - select buildable commit `46b7356a3b54a2771ce9a617e4fa3e8d13728cac` - before restoring the default tag. -- The tag declares Go 1.24.0. The suite-standard Go Snap channel remains - `1.26/stable`. -- The upstream release build uses package - `gitea.maximumdirect.net/eric/audita/cmd/audita`, `CGO_ENABLED=0`, - `GOWORK=off`, `GOTOOLCHAIN=local`, `-trimpath`, and linker flags `-s -w -X - gitea.maximumdirect.net/eric/audita/internal/buildinfo.Version=`. -- Audita exposes no release-version command needed by production convergence. - The role uses only checkout change and binary absence as build triggers. -- Audita configuration schema version `1` is strict. The native command - `audita config validate --config ` reports `config is valid` for a - valid file and is used only by tests. -- Audita's default configuration search order prefers - `/usr/local/etc/audita/config.yml` over `/etc/audita/config.yml`; the role - manages the preferred path. -- Configuration is exposed as one mapping rather than many field-specific - variables. This preserves Audita's schema and gives the suite role one clear - override boundary. -- Configuration contains no secrets by default. Consumers use Audita's - `api_key_env` fields and arrange secret environment variables outside this - feature. -- Tasks that install dependencies or manage root-owned application state use - task-level `become: true`. -- `community.general` is an approved dependency and supplies Snap management; - pin version `13.3.0` in the role's collection requirements. +- Tag `v0.9.1` resolves to + `a84941d68168a1ed9ea7423b6477ae8218be73f9` and retains the build contract + already implemented and validated through role commit `3a8c21f`. +- Audita's strict schema version is `1`; all other top-level fields are + optional and overlay application defaults. +- `audita_config_version` names the schema version so `audita_version` remains + unambiguously the selected application release. +- Optional variables are present in role defaults with YAML null values. This + documents the interface and enables argument validation without emitting + upstream defaults. +- Template conditions use `is not none`, not truthiness, so expressly supplied + falsey values are rendered. +- String and list values must be safely encoded as YAML values rather than + interpolated with hand-written quoting. +- A parent mapping is emitted only when at least one corresponding child value + is non-null. +- The role does not validate Audita's semantic constraints during production + convergence. Native validation remains integration-test behavior. +- Task-level `become: true`, `community.general` 13.3.0, and all existing + installation decisions remain unchanged. ## Fixed Public Interface -| Variable | Default | Purpose | -| --- | --- | --- | -| `audita_version` | `v0.9.1` | Git revision and release value supplied to the build | -| `audita_repository_url` | `https://gitea.maximumdirect.net/eric/audita.git` | Source repository | -| `audita_source_dir` | `/usr/local/src/audita` | Managed source checkout | -| `audita_binary_path` | `/usr/local/bin/audita` | Installed executable | -| `audita_manage_build_dependencies` | `true` | Whether to install Git and Go | -| `audita_go_snap_channel` | `1.26/stable` | Go Snap channel | -| `audita_go_binary` | `/snap/bin/go` | Go executable used for the build | -| `audita_config_path` | `/usr/local/etc/audita/config.yml` | Managed configuration file | -| `audita_config` | `{"version": 1}` | Complete versioned configuration mapping | +Retain these installation variables: -The package, build flags, linker symbol, environment values, file ownership, -and file modes are fixed implementation details. The selected version is both -the Git revision and the value supplied to the upstream linker flag. +| Variable | Default | +| --- | --- | +| `audita_version` | `v0.9.1` | +| `audita_repository_url` | `https://gitea.maximumdirect.net/eric/audita.git` | +| `audita_source_dir` | `/usr/local/src/audita` | +| `audita_binary_path` | `/usr/local/bin/audita` | +| `audita_manage_build_dependencies` | `true` | +| `audita_go_snap_channel` | `1.26/stable` | +| `audita_go_binary` | `/snap/bin/go` | +| `audita_config_path` | `/usr/local/etc/audita/config.yml` | + +Provide these configuration variables: + +| Variable | Default | Native field | +| --- | --- | --- | +| `audita_config_version` | `1` | `version` | +| `audita_pipeline_modules` | `null` | `pipeline.modules` | +| `audita_output_schema` | `null` | `output.schema` | +| `audita_llm_proposal_base_url` | `null` | `llm.proposal.base_url` | +| `audita_llm_proposal_model` | `null` | `llm.proposal.model` | +| `audita_llm_proposal_api_key_env` | `null` | `llm.proposal.api_key_env` | +| `audita_llm_proposal_timeout` | `null` | `llm.proposal.timeout` | +| `audita_llm_proposal_max_retries` | `null` | `llm.proposal.max_retries` | +| `audita_llm_validation_base_url` | `null` | `llm.validation.base_url` | +| `audita_llm_validation_model` | `null` | `llm.validation.model` | +| `audita_llm_validation_api_key_env` | `null` | `llm.validation.api_key_env` | +| `audita_llm_validation_timeout` | `null` | `llm.validation.timeout` | +| `audita_llm_validation_max_retries` | `null` | `llm.validation.max_retries` | +| `audita_concurrency_total_llm` | `null` | `concurrency.total_llm` | +| `audita_concurrency_proposal_llm` | `null` | `concurrency.proposal_llm` | +| `audita_concurrency_validation_llm` | `null` | `concurrency.validation_llm` | +| `audita_chunking_target_sections` | `null` | `chunking.target_sections` | +| `audita_chunking_max_section_tokens` | `null` | `chunking.max_section_tokens` | +| `audita_chunking_min_section_tokens` | `null` | `chunking.min_section_tokens` | +| `audita_normalization_max_segment_gap` | `null` | `normalization.max_segment_gap` | +| `audita_normalization_ellipsis_gap` | `null` | `normalization.ellipsis_gap` | +| `audita_normalization_max_segment_duration` | `null` | `normalization.max_segment_duration` | +| `audita_normalization_max_segment_tokens` | `null` | `normalization.max_segment_tokens` | +| `audita_thresholds_glossary` | `null` | `thresholds.glossary` | +| `audita_thresholds_homophones` | `null` | `thresholds.homophones` | +| `audita_thresholds_spoken_word` | `null` | `thresholds.spoken_word` | +| `audita_thresholds_grammar` | `null` | `thresholds.grammar` | +| `audita_context_description` | `null` | `context.description` | +| `audita_diagnostics_work_dir` | `null` | `diagnostics.work_dir` | +| `audita_diagnostics_retention` | `null` | `diagnostics.retention` | + +The argument specification types values according to the native field shape. +Duration-like fields accept strings or numeric seconds as supported by Audita; +do not narrow the application contract unnecessarily. ## Simplest Viable Implementation -The expected implementation is approximately nine ordinary tasks in one -readable task file: +Retain the existing nine-task installation flow. Make only these production +changes: -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 `audita_go_snap_channel` with - `community.general.snap` when dependency management is enabled. -4. Create the source, binary, and configuration parent directories with one - `ansible.builtin.file` loop. -5. Check out `audita_version` with `ansible.builtin.git`, `force: true`, and - register the module result. -6. Inspect whether `audita_binary_path` exists with `ansible.builtin.stat`. -7. Run the upstream Go build through `audita_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. Serialize `audita_config` as YAML and manage `audita_config_path` with - `ansible.builtin.copy`. +1. Replace `audita_config` in defaults and argument specifications with the + field-specific variables above. +2. Add `templates/config.yml.j2`, always rendering `version` and conditionally + rendering every optional field and necessary parent mapping. +3. Replace the final `ansible.builtin.copy` task with one + `ansible.builtin.template` task using the existing destination, ownership, + and mode. -Apply task-level privilege escalation only to tasks that require it. 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 interrogation, configuration preflight, or post-operation validation -to production tasks. +Do not add configuration assembly facts, nested merge filters, validation +commands, preliminary file probes, or multiple configuration tasks. The +template is the direct expression of the selected configuration state. ## Rationale and Alternatives -Audita's installation mechanics are intentionally the same as Seriatim's: -declarative dependency and checkout management surrounding one imperative -build command. The binary existence probe is justified because Ansible cannot -infer whether that command's output exists; no other build-state model is -needed. +Field-specific variables are the ordinary ergonomic interface for inventory +and composing roles. Nullable defaults make that interface discoverable and +typed without freezing a duplicate copy of Audita's application defaults into +Ansible. Conditional rendering also preserves the distinction between an +unset field and a caller intentionally supplying a falsey value. -A single configuration mapping is smaller and more durable than duplicating -Audita's evolving schema as dozens of role variables. A minimal default mapping -produces a valid configuration while preserving upstream defaults. The future -suite role can supply its complete D&D-specific mapping without coupling the -standalone role to one deployment. - -Managing the preferred `/usr/local/etc` path follows the application's own -discovery order and keeps locally installed software and its configuration -together. `/etc/audita/config.yml` remains an unmanaged application fallback. +Leaving variables entirely undefined would produce the same file but weaken +interface documentation and argument validation. Section-level dictionaries +would shorten the template but require callers to understand more of the +native nested schema. Retaining `audita_config` beside field variables would +require unneeded merge and precedence semantics. None is preferable to the +single field-specific interface. ## Acceptance Criteria -- Unsupported distributions, Ubuntu releases, and architectures fail before - application state is modified. -- Default execution on the local Ubuntu 26.04 host installs or confirms Git - and Go and checks out Audita tag `v0.9.1` at commit - `a84941d68168a1ed9ea7423b6477ae8218be73f9`. -- The role builds with the fixed upstream contract and installs - `/usr/local/bin/audita` as `root:root` mode `0755`. -- The managed source checkout is recursively owned by `root:root`. -- The default configuration is a valid schema-version `1` document at - `/usr/local/etc/audita/config.yml`, owned by `root:root` with mode `0644`. -- Repository-local tests successfully execute the installed CLI and receive - `config is valid` when validating the managed configuration. -- An immediate second role run reports no changes. -- A missing binary causes one rebuild followed by an unchanged run. -- A changed checkout causes one rebuild followed by an unchanged run. -- A configuration change updates only configuration state and does not rebuild - the binary. -- Dependency opt-out accepts a caller-supplied `audita_go_binary`. -- A play connecting as an ordinary sudo-capable user can apply the role without - play-level privilege escalation. -- Defaults and argument specifications contain exactly the nine variables in - the Fixed Public Interface. -- The implementation contains no production CLI validation, fingerprints, - checksums, sidecar build state, manual Git reset or clean commands, or probes - before state already managed by APT, Snap, `file`, `git`, or `copy`. -- Syntax checking, production-profile ansible-lint, focused local convergence, - configuration validation, and clean repeated runs pass. -- Role documentation describes the actual behavior, dependency opt-out, - complete configuration mapping, privilege use, and local test effects. -- No repository other than `ansible-role-audita` and the workspace control - repository is modified. +- All existing source-installation and platform acceptance criteria continue + to pass without production task-flow expansion. +- Defaults and argument specifications contain exactly the eight installation + variables and thirty configuration variables in the Fixed Public Interface. +- No `audita_config` compatibility variable remains. +- Default convergence writes a YAML mapping containing exactly `version: 1`. +- Every optional field is absent when its variable is null. +- Setting a representative value in every configuration section renders the + correct nested native fields and passes `audita config validate`. +- Explicit falsey non-null values are rendered rather than omitted; semantic + validity remains Audita's responsibility. +- Configuration changes run the template task but not the build task and + reach an unchanged immediate repeat. +- The installed configuration remains a regular file owned by `root:root` + with mode `0644`. +- Production tasks do not invoke Audita, inspect the existing configuration, + or construct intermediate configuration state. +- The README documents each variable, null-as-unmanaged behavior, safe secret + environment-variable naming, and the distinction between application and + suite defaults. +- Syntax checking, production-profile ansible-lint, complete integration + testing, native minimal and representative configuration validation, and + clean repeated runs pass. +- Only `ansible-role-audita` and the workspace control repository change. ## Dependencies and Follow-up Work -The managed host requires access to the Audita Git repository, Ubuntu Snap -infrastructure, and Go module sources. Runtime processing also requires a -glossary, transcript inputs, LLM endpoints, and secret environment variables; -those are consumers' or later suite-level features, not installation -dependencies. +The existing Audita role through commit `3a8c21f` is the implementation +baseline. The managed host and test prerequisites are unchanged. -Implementation and focused integration coverage were completed in -`ansible-role-audita` commits `47f1852`, `d37f590`, and `3a8c21f`. The D&D -pipeline role can now select concrete `audita_config` values through the -standalone role's complete-configuration interface. +The configuration amendment was completed in `ansible-role-audita` commits +`e61f02f` and `847c4c3` and validated by its complete integration suite. The +proposed Notarius roadmap should use the same nullable field-specific approach +where appropriate. ## 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, diagnostics path, - service, or schedule if later operating requirements select them. -- Add dedicated Ubuntu release and architecture test matrices. +- Validate configuration during production convergence if later operating + requirements explicitly select that behavior. +- Manage secret environment delivery, diagnostics paths, runtime accounts, or + services as separate suite-level or application-level features. +- Detect source signatures, compiler drift, or out-of-band binary changes if + the threat model changes. diff --git a/docs/roadmap/features/0003-notarius-source-installation-and-configuration.md b/docs/roadmap/features/0003-notarius-source-installation-and-configuration.md new file mode 100644 index 0000000..dd65cac --- /dev/null +++ b/docs/roadmap/features/0003-notarius-source-installation-and-configuration.md @@ -0,0 +1,253 @@ +# Feature 0003: Notarius Source Installation and Configuration + +Status: proposed + +Planning note: the installation decisions are ready for review, but the +configuration interface was drafted before the nullable field-specific +convention was adopted. Revise that interface to follow +`docs/conventions.md` before approving this roadmap. + +## 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 + +The `ansible-role-notarius` repository currently contains only its initial +placeholder files. Notarius is one of the five required pipeline applications +and needs an independently usable installation and native-configuration role +before the suite role can 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. + +## 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. +- 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 separate Ansible variable for every Notarius configuration field. +- 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`. + +An immediate second application reports no changes. A changed Git checkout or +missing binary causes one build; configuration changes converge independently +through the ordinary file-management module. Repository-local tests execute +the installed CLI to verify the selected build and validate the managed +configuration. + +## 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 mapping interface. +- `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=`. +- `notarius --version` prints `notarius `. 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 validate --config ` 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 is exposed as one complete mapping. The suite role will later + supply the full D&D pipeline and operating policy through that boundary. +- 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`, commit + `61436d7c186af57b06004e090ceac898e83cdb71`, remains buildable with the same + Go requirement, package, linker symbol, and configuration schema and may be + used for focused checkout-change coverage before restoring `v0.6.0`. + +## Fixed Public Interface + +| 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 | +| `notarius_config` | `{"version": 4}` | Complete versioned configuration mapping | + +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. + +## 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. Serialize `notarius_config` as YAML and manage `notarius_config_path` with + `ansible.builtin.copy`. + +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 preflight, 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. + +A single configuration mapping preserves Notarius's strict native schema and +avoids duplicating a large, evolving configuration surface as role variables. +The minimal valid document keeps the standalone role useful without embedding +D&D policy. The suite role can later provide the maintained pipeline, +PromptKit profile selection, concurrency, state paths, and other integrated +values as one complete override. + +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 + +- Unsupported distributions, Ubuntu releases, and architectures fail before + application state is modified. +- Default execution on the local Ubuntu 26.04 host installs or confirms Git + and Go and checks out tag `v0.6.0` at commit + `77ae132e5609916da40dcd4fcc749c000a63cbac`. +- The role builds with the fixed upstream contract and installs + `/usr/local/bin/notarius` as a regular file owned by `root:root` with mode + `0755`. +- The managed source checkout is recursively owned by `root:root`. +- The default schema-version 4 configuration is installed at + `/usr/local/etc/notarius/config.yml` as a regular file owned by `root:root` + with mode `0644`. +- Repository-local tests receive `notarius v0.6.0` from the installed CLI and + successfully validate the managed configuration. +- An immediate second role run reports no changes. +- A missing binary causes one rebuild followed by an unchanged run. +- A changed checkout causes one rebuild followed by an unchanged run. +- A configuration change updates only configuration state and does not rebuild + the binary. +- Dependency opt-out accepts a caller-supplied `notarius_go_binary`. +- A play connecting as an ordinary sudo-capable user can apply the role without + play-level privilege escalation. +- Defaults and argument specifications contain exactly the nine variables in + the Fixed Public Interface. +- Production tasks contain no CLI validation, fingerprints, checksums, sidecar + state, manual Git reset or clean commands, or probes before state already + managed by APT, Snap, `file`, `git`, or `copy`. +- Syntax checking, production-profile ansible-lint, focused local convergence, + CLI verification, configuration validation, and clean repeated runs pass. +- Role documentation describes actual behavior, dependency opt-out, the + complete configuration mapping, privilege use, and integration-test effects. +- No repository other than `ansible-role-notarius` and the workspace control + repository is modified. + +## 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. + +After this roadmap is approved, replace the pending notice at +`docs/roadmap/implementation.md` with a numbered staged Notarius implementation +plan. The pipeline role can select concrete `notarius_config` values after the +standalone interface is implemented and reviewed. + +## 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 dedicated Ubuntu release and architecture test matrices. diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md index 1c6f231..d8424b5 100644 --- a/docs/roadmap/implementation.md +++ b/docs/roadmap/implementation.md @@ -2,11 +2,11 @@ Status: pending -No implementation plan is currently active. Feature 0002 was completed in -`ansible-role-audita` through commit `3a8c21f`; its durable scope and rationale -remain in +No implementation plan is currently active. The Feature 0002 configuration +amendment was completed in `ansible-role-audita` commits `e61f02f` and +`847c4c3`. Its durable scope remains in [the Feature 0002 roadmap](features/0002-audita-source-installation-and-configuration.md), and its review history remains in [the remediation record](remediation.md). -Select and approve the next feature roadmap before replacing this notice with -a new staged implementation plan. +Feature 0003 is the next proposed unit of work. Review and approve its durable +roadmap before replacing this notice with a staged implementation plan. diff --git a/docs/roadmap/remediation.md b/docs/roadmap/remediation.md index f964411..619e0a9 100644 --- a/docs/roadmap/remediation.md +++ b/docs/roadmap/remediation.md @@ -1,134 +1,40 @@ -# Feature 0002 Remediation Review +# Feature 0002A Remediation Review Status: resolved -The Audita production task flow implements the approved architecture and the -complete integration suite passes. The following residual issues should be -corrected before Feature 0002 is accepted. +The field-specific Audita configuration implementation has the intended +architecture and its complete integration suite passes. -## Finding 1: Correct the Declared Role License - -Severity: medium - -`ansible-role-audita/meta/main.yml` declares `BSD-3-Clause`, but the -repository's `LICENSE` file is CC0 1.0 Universal. Published role metadata must -identify the license actually granted by the repository. - -### Required Remediation - -- Change `galaxy_info.license` to the Galaxy-compatible SPDX identifier - `CC0-1.0`. -- Do not replace or otherwise alter the existing license text. - -### Validation - -- `meta/main.yml` and `LICENSE` identify the same license. -- Production-profile ansible-lint passes. - -## Finding 2: Describe All Integration-Test Host Effects +## Finding 2: Synchronize Amendment Progress Tracking Severity: low -`ansible-role-audita/README.md` says that the integration suite mutates only -the three Audita paths and its private temporary directory. Default -convergence also enables dependency management, so on a fresh supported host -the suite may install Git through APT, refresh the APT cache, and install the -Go Snap. The statement is therefore incomplete. +The configuration amendment was completed in `ansible-role-audita` commits +`e61f02f` and `847c4c3`, while the implementation plan and workspace status +still described it as awaiting execution. -### Required Remediation +### Resolution -- State that the suite may install or converge Git and the Go Snap in addition - to managing the listed Audita paths. -- Retain the statement that temporary test-specific files are confined to the - private temporary directory. +- Recorded the completed amendment commits in the durable Feature 0002 + roadmap. +- Marked Feature 0002 complete in workspace status. +- Replaced the completed fixed-location implementation plan with a pending-plan + notice for Feature 0003. +- Preserved Feature 0003 as the next proposed unit of work and identified its + configuration-interface revision as the next roadmap-review step. -### Validation +## Review Conclusion -- The README accurately describes package/Snap effects and managed filesystem - paths. -- The description agrees with `tests/integration` and the role defaults. +No code remediation was required. A proposed assertion that the configuration +was a regular file was rejected because a successful +`ansible.builtin.template` operation already establishes the specified file +state. Adding another assertion would re-prove the module's documented +postcondition and conflict with the workspace convention to trust Ansible's +abstraction boundary. -## Finding 3: Complete Binary-Type Verification - -Severity: low - -`tests/verify.yml` checks binary ownership, mode, and executability, but does -not assert that `/usr/local/bin/audita` is a regular file. Stage 1 explicitly -requires baseline verification of binary type and metadata. The production -`file` task establishes this invariant, but the focused verification does not -provide the planned evidence for it. - -### Required Remediation - -- Add an assertion for `audita_binary_metadata.stat.isreg` to the existing - binary metadata check. -- Do not add another probe or production task. - -### Validation - -- `tests/verify.yml` confirms regular-file type, ownership, mode, and - executability from its existing `stat` result. -- The integration suite passes. - -## Finding 4: Synchronize Implementation Progress Tracking - -Severity: low - -Both implementation stages are annotated complete in -`docs/roadmap/implementation.md`, at role commits `47f1852` and `d37f590`, but -the plan remains `ready`, its progress notes still say execution is ready to -begin, and `docs/status.md` still describes execution as the current activity. -The recorded state should show that implementation is complete and remediation -review is in progress. - -The two workspace commits that added stage-completion annotations also diverge -from the plan's instruction that the out-of-band executor leave workspace -planning files unchanged. No implementation code was placed in the wrong -repository, so this requires tracking cleanup rather than code movement. - -### Required Remediation - -- Set the plan to `in_progress` while findings remain open and replace its - stale progress note with both completed stage commits and the current - remediation state. -- Update workspace status so Feature 0002 is `in_progress`, with implementation - complete and remediation pending, and make review/remediation the current - focus. -- Do not mark Feature 0002 `done` until remediation is implemented and - reviewed. - -### Validation - -- The plan's status, stage annotations, and progress notes describe one - consistent state. -- Workspace status agrees with the implementation and remediation records. -- `git diff --check` passes in the workspace repository. - -## Review Validation Evidence - -The review ran the complete `ansible-role-audita/tests/integration` suite on -Ubuntu 26.04 with Ansible Core 2.21.3 and ansible-lint 26.8.0. It passed syntax -checking, production-profile lint, baseline and repeated convergence, -missing-binary rebuilding, dependency opt-out with a caller-supplied Go -executable, metadata repair without rebuilding, configuration-only -convergence, alternate-checkout rebuilding, default restoration, native -configuration validation, and all three platform-rejection cases. - -The final state was default commit -`a84941d68168a1ed9ea7423b6477ae8218be73f9`, a `root:root` mode `0755` binary, -a `root:root` mode `0644` configuration, and native validation output -`config is valid`. `bash -n tests/integration` and `git diff --check` also -passed. All nested repository working trees remained clean. - -## Resolution - -All four findings were implemented and validated on 2026-09-08. The role now -declares `CC0-1.0`, its README identifies dependency-management effects, and -its existing binary `stat` result proves regular-file type. Workspace tracking -records the completed and accepted feature. Role remediation was committed as -`3a8c21f`. - -Validation passed with the complete `tests/integration` suite, -production-profile ansible-lint, `bash -n tests/integration`, and -`git diff --check` in both changed repositories. The production task flow was -not changed. +The review ran the complete `tests/integration` suite on Ubuntu 26.04 with +Ansible Core 2.21.3 and ansible-lint 26.8.0. It passed default minimal +rendering, representative nested rendering, explicit falsey values, native +configuration validation, configuration-only convergence, both rebuild +triggers, dependency opt-out, metadata repair, restored default state, +repeated convergence, and platform rejection. `git diff --check` also passed. diff --git a/docs/status.md b/docs/status.md index 73a29e2..34e36c8 100644 --- a/docs/status.md +++ b/docs/status.md @@ -4,7 +4,7 @@ Last updated: 2026-09-08 Current milestone: MVP standalone application roles -Current focus: Select and define the next unit of work. +Current focus: Resume review of the proposed Feature 0003 Notarius roadmap. | ID | Deliverable | Owner | State | Evidence or next step | | --- | --- | --- | --- | --- | @@ -13,12 +13,13 @@ Current focus: Select and define the next unit of work. | M0-03 | Tracked MVP roadmap | workspace | done | `docs/roadmap/mvp.md` established | | M0-04 | Aggregate repository status | workspace | done | `scripts/status` created | | M0-05 | Suite-level role repository | ansible-role-dnd-pipeline | done | Repository created and added to `workspace.yml` | -| M1-01 | Remaining MVP design decisions | workspace | ready | Select the next design topic or application role | -| M1-02 | Standalone application roles | role repositories | in_progress | Select the next standalone application role | +| M1-01 | Remaining MVP design decisions | workspace | ready | Resolve the Notarius configuration interface during roadmap review | +| M1-02 | Standalone application roles | role repositories | in_progress | Review and approve the Notarius feature roadmap | | M1-03 | Suite-level pipeline role | ansible-role-dnd-pipeline | proposed | Begin after standalone role interfaces stabilize | | M1-04 | Collection assembly | ansible-collection-dnd | proposed | Select and implement the pipeline-role incorporation mechanism | | F0001 | Install Seriatim from pinned source | ansible-role-seriatim | done | Implementation, integration coverage, and final remediation completed through `fb8dc41` | -| F0002 | Install and configure Audita | ansible-role-audita | done | Implementation and remediation completed through `3a8c21f`; full integration suite passed | +| F0002 | Install and configure Audita | ansible-role-audita | done | Field-specific configuration completed in `e61f02f` and `847c4c3`; full suite passed | +| F0003 | Install and configure Notarius | ansible-role-notarius | proposed | Align its configuration interface with `docs/conventions.md`, then approve the roadmap | ## Updating This File