Compare commits

..

4 Commits

Author SHA1 Message Date
c0b0ebd13e Import pipeline operator enrollment 2026-09-18 12:57:00 +00:00
d292297b6a Squashed 'roles/pipeline/' changes from 78afffe..c13f726
c13f726 Add optional pipeline operator enrollment

git-subtree-dir: roles/pipeline
git-subtree-split: c13f726a0a846180a1aa95d110b8bbc801edaae5
2026-09-18 12:57:00 +00:00
5e695f38d0 Import pipeline documentation correction 2026-09-17 23:51:51 +00:00
f62445ad1e Squashed 'roles/pipeline/' changes from 7c2bad8..78afffe
78afffe Clarify the pipeline snapshot workflow

git-subtree-dir: roles/pipeline
git-subtree-split: 78afffe8e5fdedbd274e256dfa0e0721cfbf9a5c
2026-09-17 23:51:51 +00:00
5 changed files with 46 additions and 20 deletions

View File

@@ -49,8 +49,9 @@ The collection source exposes a reviewed snapshot of this role as
Collection consumers should follow the unified bootstrap instructions in the Collection consumers should follow the unified bootstrap instructions in the
[`rakestraw.dnd` collection repository](https://gitea.maximumdirect.net/eric/ansible-collection-dnd). [`rakestraw.dnd` collection repository](https://gitea.maximumdirect.net/eric/ansible-collection-dnd).
This standalone repository remains authoritative for role changes; collection This standalone repository remains authoritative for role changes; collection
maintainers import a reviewed release as a snapshot. Collection releases are maintainers import a reviewed, pushed commit or immutable tag as a snapshot.
published independently and are listed in the collection repository. Collection releases are published independently and are listed in the
collection repository.
## Variables ## Variables
@@ -89,6 +90,7 @@ group before the remaining configuration-bearing dependencies run.
| --- | --- | | --- | --- |
| `dnd_pipeline_runtime_user` | `narratio` | | `dnd_pipeline_runtime_user` | `narratio` |
| `dnd_pipeline_runtime_group` | `narratio` | | `dnd_pipeline_runtime_group` | `narratio` |
| `dnd_pipeline_operator_users` | `[]` |
| `dnd_pipeline_config_directory_mode` | `0755` | | `dnd_pipeline_config_directory_mode` | `0755` |
| `dnd_pipeline_config_file_mode` | `0644` | | `dnd_pipeline_config_file_mode` | `0644` |
@@ -258,8 +260,8 @@ inspection shows enabled sources, inferred dependencies, and generated family
members; the diff shows the overlay's profile selections without modifying members; the diff shows the overlay's profile selections without modifying
managed files. managed files.
Storage and publishing, operator selection and group-membership management, Storage and publishing, scheduling, and end-to-end external execution remain
scheduling, and end-to-end external execution remain deferred. deferred.
### Shared execution filesystem ### Shared execution filesystem
@@ -268,27 +270,30 @@ scheduling, and end-to-end external execution remain deferred.
`narratio:narratio` mode `2775` **setgid** directories. Their access and `narratio:narratio` mode `2775` **setgid** directories. Their access and
default ACLs retain owner and group write access plus world read/traverse access default ACLs retain owner and group write access plus world read/traverse access
for newly created content. The pipeline may run as `narratio` or as a human for newly created content. The pipeline may run as `narratio` or as a human
account already in the selected runtime group; the production role neither account in the selected runtime group. Consumers can enroll selected existing
selects people nor manages their group membership. For example, a consumer human accounts through the role interface:
play can manage its selected operator separately:
```yaml ```yaml
- name: Add the selected operator to the pipeline group dnd_pipeline_operator_users:
become: true - eric
ansible.builtin.user: - alice
name: "{{ pipeline_operator }}"
groups:
- "{{ dnd_pipeline_runtime_group }}"
append: true
``` ```
Every selected account must already exist; the role does not create human
accounts or manage their identity and login policy. Enrollment adds the
selected runtime group without replacing other supplementary groups. Removing
or omitting a name later does not revoke membership. A newly enrolled operator
must begin a new login session before the operating system exposes the added
supplementary group to that session.
Configuration, transcripts, caches, generated artifacts, campaign data, Configuration, transcripts, caches, generated artifacts, campaign data,
prompts, and logs are non-secret D&D content. Credentials use the restricted prompts, and logs are non-secret D&D content. Credentials use the restricted
`/usr/local/etc/narratio/secrets` subtree, owned by the selected runtime user `/usr/local/etc/narratio/secrets` subtree, owned by the selected runtime user
and group with mode `0750`; managed files use mode `0640`. Runtime-group and group with mode `0750`; managed files use mode `0640`. Runtime-group
membership authorizes use of these shared credentials, while the subtree grants membership, including enrollment through `dnd_pipeline_operator_users`,
no world access. Ordinary configuration remains world-readable and contains authorizes use of these shared credentials, while the subtree grants no world
only reference names. access. Ordinary configuration remains world-readable and contains only
reference names.
### Managed credentials ### Managed credentials

View File

@@ -9,6 +9,7 @@ dnd_pipeline_go_snap_channel: 1.26/stable
dnd_pipeline_go_binary: /snap/bin/go dnd_pipeline_go_binary: /snap/bin/go
dnd_pipeline_runtime_user: narratio dnd_pipeline_runtime_user: narratio
dnd_pipeline_runtime_group: narratio dnd_pipeline_runtime_group: narratio
dnd_pipeline_operator_users: []
dnd_pipeline_secrets: {} dnd_pipeline_secrets: {}
dnd_pipeline_config_directory_mode: "0755" dnd_pipeline_config_directory_mode: "0755"
dnd_pipeline_config_file_mode: "0644" dnd_pipeline_config_file_mode: "0644"

View File

@@ -43,6 +43,11 @@ argument_specs:
type: str type: str
required: true required: true
description: Shared group for suite-managed configuration. description: Shared group for suite-managed configuration.
dnd_pipeline_operator_users:
type: list
elements: str
required: true
description: Existing human accounts enrolled in the shared runtime group.
dnd_pipeline_secrets: dnd_pipeline_secrets:
type: dict type: dict
required: true required: true

View File

@@ -1,4 +1,19 @@
--- ---
- name: Validate selected pipeline operator accounts
ansible.builtin.getent:
database: passwd
key: "{{ item }}"
loop: "{{ dnd_pipeline_operator_users }}"
- name: Enroll selected pipeline operators
become: true
ansible.builtin.user:
name: "{{ item }}"
groups: "{{ dnd_pipeline_runtime_group }}"
append: true
state: present
loop: "{{ dnd_pipeline_operator_users }}"
- name: Install ACL support - name: Install ACL support
become: true become: true
ansible.builtin.apt: ansible.builtin.apt:

View File

@@ -31,6 +31,7 @@ baseline_args=(
-e "dnd_pipeline_prompt_library_repository_url=file://$prompt_library_repository" -e "dnd_pipeline_prompt_library_repository_url=file://$prompt_library_repository"
-e "dnd_pipeline_prompt_library_revision=$prompt_library_revision" -e "dnd_pipeline_prompt_library_revision=$prompt_library_revision"
-e "dnd_pipeline_prompt_library_root=$prompt_library_root" -e "dnd_pipeline_prompt_library_root=$prompt_library_root"
-e "{\"dnd_pipeline_operator_users\":[\"$operator_user\"]}"
-e "{\"dnd_pipeline_secrets\":{\"AUDITA_LLM_API_KEY\":\"$synthetic_audita_api_key\",\"OPENROUTER_API_KEY\":\"$synthetic_notarius_api_key\"}}" -e "{\"dnd_pipeline_secrets\":{\"AUDITA_LLM_API_KEY\":\"$synthetic_audita_api_key\",\"OPENROUTER_API_KEY\":\"$synthetic_notarius_api_key\"}}"
) )
@@ -38,8 +39,8 @@ cleanup() {
exit_status=$? exit_status=$?
trap - EXIT trap - EXIT
/usr/bin/sudo /bin/rm -rf -- "$shared_fixture_root" || true /usr/bin/sudo /bin/rm -rf -- "$shared_fixture_root" || true
/usr/bin/sudo /usr/sbin/userdel --remove "$operator_user" 2>/dev/null || true
"$ansible_playbook" -i "$inventory" "${baseline_args[@]}" "$converge" >/dev/null 2>&1 || true "$ansible_playbook" -i "$inventory" "${baseline_args[@]}" "$converge" >/dev/null 2>&1 || true
/usr/bin/sudo /usr/sbin/userdel --remove "$operator_user" 2>/dev/null || true
rm -rf -- "$test_tmp" rm -rf -- "$test_tmp"
exit "$exit_status" exit "$exit_status"
} }
@@ -70,6 +71,7 @@ printf '%s\n' \
-p "$test_tmp/roles" -p "$test_tmp/roles"
"$ansible_playbook" -i "$inventory" --syntax-check "$converge" "$ansible_playbook" -i "$inventory" --syntax-check "$converge"
"$workspace_venv/bin/ansible-lint" --offline --profile production "$converge" "$workspace_venv/bin/ansible-lint" --offline --profile production "$converge"
/usr/bin/sudo /usr/sbin/useradd --create-home --shell /usr/sbin/nologin "$operator_user"
baseline_log="$test_tmp/baseline.log" baseline_log="$test_tmp/baseline.log"
run_converge "$baseline_log" "${baseline_args[@]}" run_converge "$baseline_log" "${baseline_args[@]}"
@@ -116,8 +118,6 @@ grep -Fq 'lane_id: item-registry' "$narratio_effective_config"
/usr/bin/sudo -u narratio /usr/local/bin/narratio config diff default testing \ /usr/bin/sudo -u narratio /usr/local/bin/narratio config diff default testing \
--config /usr/local/etc/narratio/pipeline.yml --campaign centaurs | \ --config /usr/local/etc/narratio/pipeline.yml --campaign centaurs | \
awk -F '\t' '$1 != "changed" || $2 !~ /\.profile_id$/ { invalid = 1 } END { exit invalid || NR == 0 }' awk -F '\t' '$1 != "changed" || $2 !~ /\.profile_id$/ { invalid = 1 } END { exit invalid || NR == 0 }'
/usr/bin/sudo /usr/sbin/useradd --create-home --groups narratio \
--shell /usr/sbin/nologin "$operator_user"
/usr/bin/sudo -u "$operator_user" /usr/local/bin/narratio config validate \ /usr/bin/sudo -u "$operator_user" /usr/local/bin/narratio config validate \
--config /usr/local/etc/narratio/pipeline.yml --campaign centaurs --config /usr/local/etc/narratio/pipeline.yml --campaign centaurs
/usr/bin/sudo -u "$operator_user" /usr/local/bin/notarius config validate \ /usr/bin/sudo -u "$operator_user" /usr/local/bin/notarius config validate \