Files
ansible-workspace-dnd/docs/roadmap/features/0009-shared-pipeline-filesystem-and-execution.md

13 KiB

Feature 0009: Shared Pipeline Filesystem and Execution

Status: done

Summary

Establish one coherent filesystem and invocation policy for the D&D suite. Standalone application roles will expose sensible generic configuration ownership and mode controls, while the pipeline role will use those controls to make configuration service-owned and world-readable and to make writable state safely shareable by the service account and existing human members of its group.

Problem or Opportunity

The current standalone roles hard-code their configuration ownership and modes. Those defaults are reasonable in isolation, but the pipeline cannot select its intended narratio:narratio ownership without adding a second convergence pass that would fight the dependency roles and report changes on every run.

Narratio's writable roots already use setgid mode 2775, but directory mode alone does not preserve group write access when different invocation identities have different umasks. Notarius v0.7.0 has removed its application-enforced private cache modes, so deployment provisioning can now establish a consistent shared policy before Notarius integration begins.

Governing Policies

  • The Ansible house style governs the generic role interfaces, direct module-driven convergence, and suite overrides.
  • The testing house style governs behavioral access tests and clean repeated convergence.
  • The MVP roadmap assigns operator access and cross-application policy to the pipeline role while keeping standalone roles independently useful.
  • Decision 0001 requires direct idempotent modules without preliminary state inspection.
  • Decision 0002 defines the service ownership, shared writable state, world-readable content, and restricted credential boundary implemented by this feature.

Scope

  • Add configuration owner, group, directory-mode, and file-mode variables to the Audita, Notarius, Narratio, and Scriptorium roles.
  • Preserve standalone defaults appropriate to each application rather than embedding D&D suite policy in those roles.
  • Add suite-level runtime identity and configuration-mode variables to the pipeline role and pass them explicitly to all four configuration-bearing dependencies.
  • Run the Narratio dependency before the other application dependencies so it creates the selected service account and group before they write service-owned configuration.
  • Converge suite application configuration as narratio:narratio, with directory mode 0755 and file mode 0644 by default.
  • Keep /var/lib/narratio, /var/spool/narratio, and /var/cache/narratio as narratio:narratio mode 2775 shared writable roots.
  • Install the Ubuntu acl package, declare a pinned ansible.posix collection dependency, and manage access and default ACL entries that retain owner/group write and world read/traverse access beneath shared roots.
  • Support invocation as either the service account or an existing human member of the selected runtime group.
  • Document the standalone defaults, pipeline overrides, expected group membership, world-readable non-secret boundary, and reserved restricted secrets/ policy.
  • Add focused integration coverage for public configuration-mode overrides, cross-identity access, and immediate clean repeated convergence.

Non-goals

  • Upgrading Notarius, adding its ConfigKit import interface, deploying its D&D pipeline, creating /var/cache/notarius, or enabling Narratio's extraction stage; those belong to Feature 0010.
  • Changing any application's native configuration fields or runtime behavior.
  • Selecting human operators or changing their group membership.
  • Creating or populating a secrets/ directory, delivering credentials, or defining secret injection.
  • Making application configuration group-writable or supporting manual edits to Ansible-managed configuration.
  • Changing ownership of root-managed source checkouts, installed binaries, campaign assets, or prompt assets.
  • Recursively migrating pre-existing runtime content with incompatible modes or ACLs.
  • Running transcript processing, calling external services, or adding scheduling and service management.

Target End State

Each standalone role exposes the same four concepts using its application prefix. Its defaults remain appropriate for an independent installation:

Role Configuration owner Configuration group Directory mode File mode
Audita root root 0755 0644
Notarius root root 0755 0644
Scriptorium root root 0755 0644
Narratio root narratio 0750 0640

For example, the Notarius variables are notarius_config_owner, notarius_config_group, notarius_config_directory_mode, and notarius_config_file_mode; the other roles use their corresponding prefixes. Each role uses these variables in the ordinary directory and template tasks that already own its configuration. No role adds a follow-up permission-repair task or preliminary inspection.

The pipeline role defines dnd_pipeline_runtime_user and dnd_pipeline_runtime_group, both defaulting to narratio, plus dnd_pipeline_config_directory_mode: "0755" and dnd_pipeline_config_file_mode: "0644". It passes those values as the four roles' configuration owner, group, and mode inputs. Narratio executes first in dependency order and creates the selected identity before another dependency references it.

The resulting application configuration beneath /usr/local/etc/{audita,notarius,narratio,scriptorium} is owned by narratio:narratio and is world-readable. It remains under Ansible control; neither the service account nor human operators are expected to edit managed files in place.

The three existing Narratio writable roots are narratio:narratio mode 2775. Their access and default ACLs are equivalent to u::rwx, g::rwx, o::r-x, and m::rwx. Inherited permissions remain subject to the mode requested for each new file type, so ordinary files are non-executable while remaining group-writable and world-readable. A later feature applies the same policy when it introduces another shared root such as /var/cache/notarius.

A human account already belonging to narratio can use the same configuration and writable roots as the service account. This feature does not choose that account. Credential files are absent; the reserved future /usr/local/etc/narratio/secrets subtree uses the distinct 0750 directory and 0640 file policy when a credential-delivery feature creates it.

Repository Ownership

  • ansible-role-audita, ansible-role-notarius, and ansible-role-scriptorium own their generic configuration ownership and mode variables, standalone defaults, documentation, and focused integration coverage.
  • ansible-role-narratio owns the equivalent generic controls while retaining its existing standalone relationship to the service group it creates.
  • ansible-role-dnd-pipeline owns dependency ordering, the suite runtime identity and configuration-mode defaults, explicit dependency overrides, the acl package and ansible.posix dependency, shared-root ACL convergence, consumer documentation, and cross-identity integration coverage.
  • ansible-workspace-dnd owns this roadmap, Decision 0002, the later staged implementation plan, and aggregate status.
  • The five application source repositories are unchanged; their runtime contracts are not needed to implement configuration-file ownership.

Constraints and Settled Decisions

  • Standalone application roles remain independently usable. Their defaults do not assume that a narratio account exists unless the role itself creates it.
  • The pipeline role selects suite policy by passing public role variables; it does not reapply ownership after dependencies have completed.
  • Configuration owner and group variables are strings, while mode variables use the quoted four-digit strings expected by Ansible file-management modules.
  • The pipeline's selected runtime user and group are the configuration owner and group. Supporting different configuration and runtime identities would require a concrete later use case.
  • Application configuration is world-readable but not group-writable. Shared writable roots and their ordinary content are group-writable and world-readable.
  • The role uses ansible.builtin.apt for the acl package and ansible.posix.acl for access/default ACL state. The collection dependency is pinned in the pipeline role's manifest and installed by its integration driver.
  • ACL management covers only the three existing writable roots and their inheritance policy. It does not recursively rewrite existing descendants.
  • A secrets/ subtree is a documented future boundary, not state created by this feature.
  • Tests exercise the public operating contract and one representative cross-identity workflow. They do not enumerate every path or restate every module argument.

Simplest Viable Implementation

  1. Add four defaults, four argument-specification entries, and direct task substitutions to each configuration-bearing standalone role; update its README and focused integration inputs without adding new task flow.
  2. Reorder the pipeline dependencies so Narratio creates the service identity first, add four suite policy variables, and pass them directly to each configuration-bearing role.
  3. Add the pinned ansible.posix requirement, install acl, and apply the selected access/default ACL entries to the three existing writable roots with ordinary state-management modules.
  4. Update pipeline consumer documentation and extend its integration driver with one representative human group member, native configuration inspection, shared-write behavior, and a clean repeat.

No task gathers preliminary filesystem or account state. Existing file, template, apt, and acl modules own convergence and change reporting.

Rationale and Alternatives

Public ownership and mode variables let each standalone role express its own sensible installation while allowing a composing role to select different policy through normal Ansible precedence. A corrective pipeline task would fight the dependency templates and make every otherwise unchanged run report changes.

Setgid alone preserves group identity but does not guarantee group-write bits for files created under an arbitrary human user's umask. A managed default ACL expresses the required inherited state directly. A wrapper command that sets a umask would add another invocation path and would not protect direct use of the installed applications.

Mode 2775, rather than 2770, is deliberate: D&D transcript content is not treated as secret in this deployment. Only API keys and similar credentials belong in the separately restricted future subtree.

Acceptance Criteria

  • Each configuration-bearing standalone role exposes and documents owner, group, directory-mode, and file-mode variables with the standalone defaults listed above.
  • Each role applies those inputs through the tasks that already create its configuration directory and file, without a second convergence pass.
  • The pipeline role runs Narratio first and explicitly selects narratio:narratio, 0755 directories, and 0644 files for all application configuration.
  • The deployed configuration for Audita, Notarius, Narratio, and Scriptorium is owned by narratio:narratio, is readable by other local users, and contains no credential values.
  • The three existing Narratio writable roots are narratio:narratio mode 2775 and have access/default ACLs that preserve group write and world read/traverse access for new content.
  • The pipeline role declares a pinned ansible.posix dependency and installs the required Ubuntu acl package.
  • The service account and a representative existing human member of the narratio group can use the same configuration and exchange ordinary writable-state content.
  • The role does not create users other than the established service account, alter human group membership, create a secrets directory, or migrate existing descendants recursively.
  • Syntax checking and production-profile lint pass in every changed role.
  • Focused standalone-role integration checks and the complete pipeline integration entry point pass.
  • An immediate repeated application in every changed role reports no changes.
  • Tests observe the shared operating contract without snapshotting defaults, enumerating implementation steps, or testing trusted module behavior.

Dependencies and Follow-up Work

Features 0001 through 0008 and the standalone-role test-suite simplification are complete. Notarius v0.7.0's deployment-owned cache permissions remove the application blocker that previously prevented this policy, but Feature 0009 does not otherwise depend on or modify the Notarius application.

Feature 0010 applies this policy to /var/cache/notarius while adding the D&D extraction integration. Later work may add optional operator-selection and group-membership variables or deliver credentials beneath the restricted secrets/ subtree.

Potential Future Hardening

  • Offer a non-world-readable deployment profile if a concrete environment later treats campaign material as confidential.
  • Select a recursive migration policy if supported upgrades must normalize pre-existing descendants rather than only managed roots and new content.