Prepare the managed credential implementation sprint

This commit is contained in:
2026-09-16 19:12:42 +00:00
parent 6d9b4b87f8
commit 358051af98
5 changed files with 1041 additions and 18 deletions

View File

@@ -48,6 +48,13 @@ file modes are `0750` and `0640`, respectively, with
shared credentials. Configuration may contain credential reference names but
never raw credential values.
Narratio's general operational guidance recommends `0700` directories and
`0600` files for a single-user credential store. The suite deliberately grants
group read/traverse access instead: membership in the selected `narratio`
runtime group authorizes both pipeline execution and use of its shared provider
credentials. Deployments that do not trust every group member require a
different per-user credential policy and are outside this decision.
Version-controlled campaign and prompt assets may remain root-managed because
Git is authoritative, but they are world-readable. The suite does not add
confidentiality controls to non-secret D&D content.

View File

@@ -0,0 +1,392 @@
# Feature 0011: Managed Pipeline Credentials and Notarius Reuse
Status: ready
## Summary
Upgrade the generic Narratio role and D&D pipeline to Narratio v2.1.0, manage
provider credentials through Narratio's conventional ConfigKit secrets
directory, and use the release's typed Notarius credential and checkpoint-reuse
controls. Credential values remain outside ordinary application YAML and are
readable only by the selected runtime user and group. Ordinary pipeline runs
reuse compatible Notarius checkpoints, while forced extraction retains
Narratio's native refresh behavior.
## Problem or Opportunity
The pipeline role currently writes credential reference names but manages no
credential values. A caller must prepare the invoking environment through an
external mechanism before a real run can reach the selected model providers.
The installed Narratio v2.0.0 release also cannot forward a selected provider
credential to Notarius. Its Notarius child receives a restricted environment,
and the v2.0 configuration contract exposes no typed credential field. The
pipeline records Notarius checkpoints but cannot ask Notarius to reuse them.
Narratio v2.1.0, immutable tag `v2.1.0` at `c45e042`, resolves both application
contract gaps. It adds optional `notarius.llm_api_key`, `notarius.resume`, and
`notarius.chunk_cache` fields without requiring migration of existing v2
configuration. The credential field resolves through the existing process
environment or secrets-directory bootstrap, forwards only the exact selected
name and value, and participates in the application's existing redaction
boundary. Ordinary extraction may request checkpoint reuse; forced extraction
omits resume and refreshes the active chunk plan.
The suite separately configures Audita proposal and validation with different
environment names even though both use the same endpoint and model. Narratio's
Audita boundary accepts one typed credential. This feature therefore
coordinates both suite-default Audita sections on `AUDITA_LLM_API_KEY` while
retaining their existing independently overrideable variables.
## Governing Policies
- [The Ansible house style](../../ansible.md) governs the direct desired-state
implementation, native configuration interface, and ordinary module
convergence.
- [The testing house style](../../testing.md) governs synthetic, native,
behavior-oriented validation without live provider calls or filesystem
metadata assertions.
- [The MVP roadmap](../mvp.md) assigns pipeline-wide secret delivery,
application-version coordination, and cache policy to the suite role.
- [Decision 0002](../../decisions/0002-shared-readable-pipeline-filesystem-policy.md)
defines the selected runtime group as the authorization boundary and fixes
`0750` directory and `0640` file modes for shared credential material.
- [Feature 0009](0009-shared-pipeline-filesystem-and-execution.md) establishes
the service-user and existing-group-member invocation model.
- [Feature 0010](0010-dnd-notarius-extraction-integration.md) establishes the
Notarius pipeline, FHS cache roots, automatic chunk-plan reuse, and checkpoint
recording extended by this feature.
- Narratio's immutable v2.1.0 release documentation and current configuration
and Notarius integration references are authoritative for the application
fields, resolution semantics, forced-run behavior, and redaction contract.
## Scope
- Advance the standalone Narratio role's selected application release from
v2.0.0 to v2.1.0 without changing its established source-build task flow.
- Expose the three new native optional fields through nullable, field-specific
role variables: `narratio_notarius_llm_api_key`,
`narratio_notarius_resume`, and `narratio_notarius_chunk_cache`.
- Keep the standalone role generic. All three variables default to null and
are omitted unless a caller selects them, leaving Narratio's native defaults
in control.
- Advance the pipeline role's compatible Narratio application version to
v2.1.0 and pin its Narratio role dependency to the reviewed revision that
provides the new interface.
- Use `/usr/local/etc/narratio/secrets`, the conventional directory adjacent
to `/usr/local/etc/narratio/pipeline.yml`, as the suite's sole managed
credential location.
- Create that directory as the selected pipeline runtime user and group with
mode `0750`.
- Add one public `dnd_pipeline_secrets` mapping. Keys are environment-variable
names. A non-null string value means that the named credential file must be
present with that content; null means that the specifically named managed
credential file must be absent. An omitted key is unmanaged.
- Default `dnd_pipeline_secrets` to an empty mapping so the role never invents
credentials and remains usable with externally prepared process environment
values.
- Validate selected keys as environment-variable names and reject empty
non-null values before constructing a destination path.
- Write non-null credential files as the selected runtime user and group with
mode `0640`, and remove only names explicitly assigned null.
- Suppress credential values from Ansible task output during validation,
creation, updates, and removal.
- Keep ordinary application configuration limited to non-secret credential
reference names.
- Coordinate Audita proposal and validation on the suite-default
`AUDITA_LLM_API_KEY` name and pass that name through the standalone Narratio
role's existing `narratio_audita_llm_api_key` interface.
- Add `dnd_pipeline_narratio_notarius_llm_api_key`, default it to
`OPENROUTER_API_KEY` for Notarius's selected production profile, and pass it
through Narratio's typed Notarius credential field.
- Add `dnd_pipeline_narratio_notarius_resume`, default it to true, and pass it
through Narratio so ordinary extraction requests reuse of the checkpoints
already recorded by the suite's Notarius configuration.
- Expose Narratio's native `notarius.chunk_cache` in the standalone role but do
not duplicate it behind a suite variable or set it from the pipeline role.
The suite already configures Notarius's ordinary chunk-plan mode as `auto`;
Narratio v2.1.0 independently owns its forced-extraction refresh override.
- Preserve Scriptorium's application-owned `api_key_env` profile selection.
Narratio's secrets bootstrap supplies the selected environment value and the
existing Scriptorium adapter passes that named credential to its child.
- Document the mapping interface, Ansible Vault inventory pattern, process
environment precedence, shared-group authorization boundary, explicit null
removal, application credential flow, and checkpoint-reuse policy.
- Add focused integration coverage using distinctive synthetic values and
native Narratio configuration loading. No default test contacts a model
provider.
## Non-goals
- Storing raw credentials in Narratio, Audita, Notarius, or Scriptorium YAML.
- Providing real credentials, unencrypted example values, or a default secret
mapping.
- Implementing Ansible Vault, choosing a vault password mechanism, or requiring
one specific inventory encryption workflow.
- Integrating an external secret manager, cloud vault, hardware-backed key
store, or dynamic credential broker.
- Making the pipeline role authoritative for files omitted from
`dnd_pipeline_secrets`, purging the whole directory, or recursively changing
pre-existing content.
- Adding a launcher script, shell profile, environment file, systemd unit,
scheduler, or persistent service merely to inject credentials.
- Passing arbitrary caller-defined environment variables through Narratio's
subprocess boundary.
- Managing human accounts or their membership in the runtime group.
- Running Audita, Notarius, or Scriptorium against a live endpoint during the
default integration suites.
- Testing Notarius checkpoint reuse end to end in the Ansible role. Narratio
and Notarius own that behavior; the role validates the selected native
configuration and immediate Ansible convergence.
- Setting Narratio's optional `notarius.chunk_cache` field redundantly when
the deployed Notarius configuration already selects `auto`.
- Changing Narratio's native forced-extraction refresh policy.
- Rotating credentials, retaining prior values, validating provider
authorization, or recovering deliberately corrupted secret files.
- Adding storage or publishing credentials before the suite selects a storage
or publishing feature.
- Modifying any upstream application source.
## Target End State
The standalone Narratio role installs v2.1.0 by default and can render all
three new Notarius fields when a caller supplies them. It owns no credential
files and selects no D&D-specific credential or cache policy.
The pipeline role owns this conventional restricted subtree:
```text
/usr/local/etc/narratio/
├── pipeline.yml
└── secrets/ narratio:narratio 0750
├── AUDITA_LLM_API_KEY narratio:narratio 0640
└── OPENROUTER_API_KEY narratio:narratio 0640
```
The filenames are suite-selected reference names, never credential values.
The selected runtime user and group remain configurable through the existing
pipeline interface.
A consumer may keep encrypted values in inventory and select explicit removal
without embedding any value in ordinary configuration:
```yaml
dnd_pipeline_secrets:
AUDITA_LLM_API_KEY: "{{ vault_dnd_audita_llm_api_key }}"
OPENROUTER_API_KEY: "{{ vault_dnd_openrouter_api_key }}"
RETIRED_PROVIDER_KEY: null
```
The role owns only these three named entries for this invocation. It converges
the first two to regular files, removes the third, and leaves every omitted
name alone. The actual `vault_dnd_*` values belong in encrypted inventory and
must not appear in examples, plans, test logs, commits, or ordinary generated
configuration.
When Narratio runs without `--secrets-dir`, it discovers the sibling directory
and loads its immediate regular files before resolving the pipeline. An
already-set process environment value wins over the corresponding file. The
base and `testing` profiles use the same delivery mechanism; profiles select
models and application profiles, not a second credential store.
The suite deliberately uses `narratio:narratio`, directory mode `0750`, and
file mode `0640` rather than Narratio's general single-user recommendation of
`0700` and `0600`. Membership in the selected runtime group is an explicit
authorization to invoke this pipeline with its shared provider credentials.
No world access is granted. Deployments that do not trust every group member
must use separate process-environment or `--secrets-dir` provisioning and are
outside this shared-operator policy.
Audita's proposal and validation configuration both reference
`AUDITA_LLM_API_KEY` by default. Narratio resolves that typed reference and
passes the value explicitly to the Audita child. Callers may still override
the existing Audita reference variables, but a custom split-key design also
requires an application boundary capable of forwarding both values and is not
promised by this feature.
Narratio's effective Notarius configuration names `OPENROUTER_API_KEY`, or a
caller-selected replacement, through `notarius.llm_api_key`. Narratio resolves
the named secret and forwards only that exact name and value to Notarius. It
does not infer a provider from Notarius configuration or forward unrelated
credentials.
Ordinary extraction sets `notarius.resume: true`. Notarius continues to own
checkpoint recording, identity, and storage beneath the configured
`/var/cache/notarius/checkpoints` root. Narratio requests reuse but does not
inspect the cache. The pipeline leaves Narratio's `notarius.chunk_cache` field
unset because the Notarius root configuration already selects `auto` at the
native owner. Forced extraction follows Narratio v2.1.0 behavior: resume is
omitted and the active chunk plan is refreshed without deleting unrelated
cache content.
Scriptorium continues to select credential names through its PromptKit
profiles. The Narratio process receives those names from the secret bootstrap,
and its existing adapter forwards the selected profile credential.
## Repository Ownership
- `../narratio` at immutable tag `v2.1.0` is the read-only application
authority for secrets bootstrap, typed Notarius credential forwarding,
checkpoint reuse, forced refresh, and subprocess redaction.
- `ansible-role-narratio` advances the application release and exposes the
three native Notarius fields through its generic field-specific interface.
It does not manage secret files or select suite policy.
- `ansible-role-dnd-pipeline` owns the conventional secrets directory, managed
credential mapping, suite-level Audita and Notarius reference coordination,
checkpoint-reuse selection, consumer documentation, and behavioral tests.
- `ansible-workspace-dnd` owns this roadmap, the settled MVP policy, progress,
and independent review record.
- The Audita, Notarius, and Scriptorium application roles remain unchanged;
they continue to own only their native configuration interfaces.
- The collection, asset, remaining application, and reference repositories
remain unchanged.
## Constraints and Settled Decisions
- Raw credential bytes never enter ordinary application YAML, Git-tracked
examples, workspace plans, or committed test fixtures.
- The role manages secret values only through `ansible.builtin.copy` content
or an equally direct secret-safe module operation with `no_log: true`.
- The conventional directory path is fixed because automatic Narratio
discovery depends on its relationship to the fixed root pipeline document.
- Credential filenames must match the environment-variable grammar accepted
by ConfigKit. Validation exists to prevent a caller-controlled key from
becoming a path outside the secrets directory.
- Empty credential values are invalid. Null means explicit absence and is
distinct from an empty string.
- An omitted mapping key is outside the role's ownership. Exact-directory
mirroring and deletion of unlisted files are intentionally excluded.
- The shared runtime group is an authorization group. Its members may read the
managed credentials; all other local users are denied access.
- The suite's group-readable credential policy deliberately differs from
Narratio's general single-user mode recommendation. ConfigKit does not
enforce those recommended modes, and the selected policy grants no world
access.
- Application adapters, rather than Ansible, own the secure transfer of loaded
values to child processes. Ansible does not create wrappers or reconstruct
the subprocess environment.
- Notarius's root configuration remains the single owner of ordinary
chunk-plan mode. Narratio's native `resume` field expresses the distinct
request to load compatible recorded checkpoints.
- Production convergence performs no provider call, credential validity test,
cache probe, content checksum bookkeeping, backup, or post-write reread.
## Simplest Viable Implementation Shape
The standalone Narratio role needs only a release update and three nullable
field-specific variables rendered through its existing template. It remains
generic and does not own credential files or suite policy.
The pipeline role needs one empty credential mapping, two Narratio Notarius
policy variables, coordinated Audita reference defaults, and ordinary
dependency wiring. Credential convergence consists of one directory task, one
validation loop, one copy loop for non-null entries, and one file loop for
explicit null removals. Both secret-bearing loops suppress their output.
Documentation and focused native integration coverage complete the public
interface. No state probe, handler, template assembly, manifest, wrapper,
directory scan, or role-private bookkeeping is required. The validation loop
is deliberate: the mapping key becomes a filesystem path, so rejecting
anything other than a valid environment-variable name protects the declared
destination boundary.
## Rationale and Alternatives
Narratio's native ConfigKit bootstrap is preferable to a shell wrapper or
service-specific environment file because every pipeline-loading command uses
the same application-owned behavior, whether invoked by the service account or
an authorized human operator. It also preserves the application's existing
environment-precedence and redaction rules.
A mapping is more ergonomic for inventory than a list of file objects because
the native identity of each entry is already its environment-variable name.
Null provides explicit removal without making an empty mapping destructive or
requiring a filesystem scan. Omitted files remain available for consumers that
combine Ansible-managed credentials with another provisioning mechanism.
Per-user `0700` directories would follow Narratio's general recommendation but
would require separate keys or an additional invocation convention for every
authorized human. The selected shared group already defines who may execute
the suite with shared state, so group-readable and world-inaccessible
credentials provide the intended operational model with less duplication.
Selecting Narratio checkpoint reuse completes the suite's earlier decision to
enable every available Notarius cache. Duplicating `chunk_cache: auto` in both
Narratio and Notarius configuration would add no ordinary behavior, so the
pipeline leaves the optional Narratio override unset and relies on each
application's documented ownership boundary.
## Acceptance Criteria
- The standalone Narratio role installs an explicitly selected v2.1.0 and can
render representative non-null and falsey values for
`narratio_notarius_llm_api_key`, `narratio_notarius_resume`, and
`narratio_notarius_chunk_cache` while omitting null values.
- The pipeline role selects Narratio v2.1.0 and pins the reviewed standalone
role revision that provides its native fields.
- The pipeline role creates `/usr/local/etc/narratio/secrets` with the selected
runtime ownership and suite credential-directory mode.
- `dnd_pipeline_secrets` defaults to an empty mapping and accepts valid
environment names with nonempty string or null values.
- Invalid names and empty non-null values fail before any destination outside
the declared secret directory can be constructed.
- Non-null entries converge to regular files with the selected runtime
ownership and mode `0640`; null entries converge to absence; omitted names
remain unmanaged.
- Credential values are suppressed from Ansible output and never appear in
application YAML, generated examples, native effective-configuration output,
test logs, or committed fixtures.
- Audita proposal and validation use `AUDITA_LLM_API_KEY` by default, and
Narratio's typed Audita field names that reference so the adapter explicitly
supplies the loaded value.
- Narratio's effective pipeline selects `OPENROUTER_API_KEY` through the typed
Notarius field and selects checkpoint resume for ordinary extraction.
- Narratio's optional chunk-cache override remains absent from the suite
configuration while Notarius's effective root configuration continues to
select `auto`.
- Scriptorium continues to receive the credential named by its selected
profile through the existing adapter behavior.
- A synthetic pipeline integration invocation with the relevant ambient
variables unset loads credentials through the conventional directory and
succeeds at native offline configuration validation as the service account
and one existing-group-member fixture.
- One representative explicit-null transition makes the selected synthetic
credential unavailable through the native bootstrap boundary without
inspecting module results or file metadata.
- A distinctive synthetic secret never appears in captured integration output.
- The baseline and representative credential applications each report
`changed=0` and `failed=0` on an immediate identical repeat.
- Syntax checking, production-profile lint, and the complete standalone
Narratio and pipeline integration entry points pass.
- No live provider, WhisperX service, storage backend, model call, or
application cache execution occurs in default tests.
## Dependencies and Follow-up Work
- Features 0005, 0006, 0008, 0009, and 0010 are complete prerequisites.
- Narratio v2.1.0 at immutable tag `c45e042` satisfies the former typed
Notarius credential and cache-control prerequisite.
- The completed upstream feature request was removed from Narratio's active
roadmap during its v2.1.0 release cleanup; the release reference now owns the
delivered behavior.
- The standalone Narratio-role commit produced by this feature must be
available from its canonical Git origin before the pipeline's isolated
dependency test can use it.
- Collection assembly should follow this feature so the first assembled
collection packages a pipeline with complete credential delivery and cache
reuse.
- Storage and publishing policy may later add S3 or other credential reference
names to consumer inventory without changing the file-delivery mechanism.
- Operator selection, group-membership management, services, scheduling,
upgrades, removal, and external secret-manager integration remain separate
features.
## Potential Future Hardening
- Integrate a dedicated external secret manager or short-lived credential
broker.
- Support distinct per-operator secret directories for deployments that do
not treat runtime-group membership as shared credential authorization.
- Add explicit credential rotation orchestration and provider-side validity
checks.
- Add platform-specific mandatory-access-control policy for credential files.

View File

@@ -1,12 +1,617 @@
# Implementation Plan
# Implementation Plan 0011: Managed Pipeline Credentials and Notarius Reuse
Status: pending
Status: ready
No implementation plan is currently active. Feature 0010 was implemented,
independently reviewed, and completed through pipeline-role remediation commit
`bbf01e1`. Its durable scope, decisions, and acceptance criteria remain in
[Feature 0010](features/0010-dnd-notarius-extraction-integration.md), and the
completed review record remains in [the remediation document](remediation.md).
Target executor: GPT-5.6-Terra
Select and approve the next unit of work before replacing this notice with a
new staged implementation plan.
Feature roadmap:
`docs/roadmap/features/0011-managed-pipeline-credential-delivery.md`
Active plan location: `docs/roadmap/implementation.md`
## Objective
Upgrade the generic Narratio role to v2.1.0 and expose its native Notarius
credential and cache controls, then have the D&D pipeline role deliver shared
provider credentials through Narratio's conventional secrets directory,
coordinate the Audita and Notarius credential references, and request
compatible Notarius checkpoint reuse.
The feature roadmap remains authoritative for scope, rationale, target end
state, and acceptance criteria. Decisions 0001 and 0002 remain authoritative
for direct Ansible convergence and the shared-runtime-group authorization
boundary.
## Execution Rules
- Execute stages in numeric order.
- Before each stage, confirm its preconditions and the working-tree state of
its owning repository.
- Before each stage, reread every document listed in that stage's Required
Policy Review. Do not rely on policy recalled from an earlier stage.
- Preserve unrelated and pre-existing changes. Stop if an owning repository is
unexpectedly dirty at a stage boundary.
- Keep `ansible-role-narratio` generic. It exposes Narratio's native optional
fields but does not select D&D credentials, manage secret files, or own
suite cache policy.
- Keep credential delivery and cross-application policy in
`ansible-role-dnd-pipeline`. Do not modify an application source repository
or move suite policy into the Audita, Notarius, or Scriptorium roles.
- Never place a real credential in source, documentation, examples, test
fixtures, command output, or commits. Tests may use distinctive synthetic
values and must prove that those values do not appear in captured output.
- Treat a `dnd_pipeline_secrets` key as untrusted path input. Validate the
complete mapping before any key is interpolated into a destination path.
Accept only environment-variable names matching
`^[A-Za-z_][A-Za-z0-9_]*$`; accept only nonempty strings or null as values.
- Apply `no_log: true` to every production task that evaluates, writes, or
removes mapping entries. Do not expose a loop label containing a credential
value.
- Use direct module-owned convergence: one fixed secrets-directory task, one
validation loop, one copy loop for non-null entries, and one file loop for
explicit null entries. Do not add preliminary `stat`, directory scans,
recursive ownership repair, checksums, handlers, wrappers, manifests, state
files, backups, or post-write validation tasks.
- An omitted credential key is unmanaged. Do not mirror the mapping onto the
directory, purge unlisted files, or infer that an empty mapping removes
anything.
- Keep ordinary application YAML secret-free. It may contain only credential
reference names.
- Leave Narratio's `notarius.chunk_cache` unset in the pipeline role. The
deployed Notarius root configuration remains the sole owner of ordinary
`auto` chunk-plan reuse, while Narratio owns its native forced-refresh
behavior.
- Test role-owned behavior through public role inputs and native application
commands. Do not test Ansible module guarantees, inspect module results,
assert task order, snapshot complete default documents, contact a live
provider, or execute the application pipeline.
- Run every stage's validation before committing it. Commit each completed
stage when it leaves its owning repository valid, using the plain-English
commit intent specified by that stage.
- Do not begin a dependent stage after an earlier stage fails validation.
- The pipeline integration suite installs role dependencies from canonical Git
origins. The Stage 1 Narratio-role commit must therefore be reachable from
its canonical origin before Stage 2 begins. If a fresh Galaxy installation
cannot resolve it, stop after Stage 1 and report the commit hash for
publication; do not substitute a branch, local role path, or weakened test.
## Starting State and Preconditions
- The workspace control repository contains the reviewed Feature 0011 roadmap
and this implementation plan in a clean planning commit on `main`.
- Features 0001 through 0010 and the standalone-role test-suite simplification
are complete.
- The implementation repositories are clean on `main` at these reviewed
starting commits:
| Repository | Starting commit |
| --- | --- |
| `ansible-role-narratio` | `eeb1105590e638c1f46b42c98a5dbbce4ddeea86` |
| `ansible-role-dnd-pipeline` | `bbf01e107a5e4e70a2abd6c696bfdb9ccc38bd7e` |
- The Narratio role matches `origin/main`. The pipeline role is intentionally
ahead of its canonical origin and must not be reset or replaced.
- `../narratio` is clean at immutable tag `v2.1.0`, commit
`c45e042462b77a8873b4215edc40e3840149db34`.
- Narratio v2.1.0 adds optional `notarius.llm_api_key`, `notarius.resume`, and
`notarius.chunk_cache` fields without requiring migration of existing v2
configuration. Its conventional secrets bootstrap loads immediate regular
files from a sibling `secrets/` directory, gives an existing process
environment value precedence, and requires every configured typed reference
to resolve during configuration loading.
- Notarius v0.7.0 is already selected by the suite, records checkpoints under
the persistent FHS cache root, and leaves ordinary chunk-plan mode at
`auto`.
- PromptKit documents `OPENROUTER_API_KEY` as the credential name for the
OpenRouter backend selected by the suite's production Notarius profile.
- The workspace `.venv` provides Ansible Core 2.21.3 and ansible-lint 26.8.0.
- The local integration environment remains an accepted proxy for Ubuntu
26.04, supports task-level privilege escalation, and provides ordinary POSIX
group and ACL behavior.
## Repository Scope
Repositories that change:
- `ansible-role-narratio`: Narratio v2.1.0 selection, three nullable native
Notarius fields, generic documentation, and focused integration coverage.
- `ansible-role-dnd-pipeline`: compatible Narratio role and application pins,
credential delivery, Audita and Notarius reference coordination, checkpoint
reuse, consumer documentation, and focused integration coverage.
- `ansible-workspace-dnd`: implementation progress and review handoff only
after the role stages are complete.
Repositories that may be inspected but must remain unchanged:
- `../narratio`, which is the read-only v2.1.0 application authority.
- `../notarius`, `../audita`, `../seriatim`, `../scriptorium`, and PromptKit,
which may be consulted for current native contracts.
- `ansible-role-notarius`, `ansible-role-audita`,
`ansible-role-seriatim`, and `ansible-role-scriptorium`.
- `ansible-collection-dnd`, all asset repositories, `rakestraw-ansible`, and
every other repository in `workspace.yml`.
## Stage 1: Upgrade the Generic Narratio Role
### Objective
Make the standalone role install Narratio v2.1.0 and expose the release's
three optional Notarius fields without adding suite-specific credential or
cache policy.
### Required Policy Review
Before beginning this stage, read:
- `AGENTS.md`
- `docs/orientation.md`
- `docs/conventions.md`
- `docs/ansible.md`
- `docs/testing.md`
- `docs/roadmap/features/0011-managed-pipeline-credential-delivery.md`
- `docs/decisions/0001-minimal-module-driven-ansible.md`
- `../narratio/docs/releases/v2.1.0.md`
- `../narratio/docs/config.md`
- `../narratio/docs/integrations/notarius.md`
- `../narratio/examples/pipeline.full.annotated.yml`
- `ansible-role-narratio/defaults/main.yml`
- `ansible-role-narratio/meta/argument_specs.yml`
- `ansible-role-narratio/templates/pipeline.yml.j2`
- `ansible-role-narratio/tests/integration`
- `ansible-role-narratio/tests/representative.yml`
- `ansible-role-narratio/tests/verify-baseline.yml`
- `ansible-role-narratio/tests/verify-config.yml`
- `ansible-role-narratio/README.md`
### Preconditions
- `ansible-role-narratio` is clean at its stated starting commit.
- Its existing `tests/integration` entry point passes before modification.
- The upstream files listed above remain at the reviewed immutable v2.1.0 tag.
### Owning Repository
`ansible-role-narratio`
### Required Changes
1. Advance `narratio_version` from `v2.0.0` to `v2.1.0`. Do not change the
established source checkout, build condition, metadata convergence, or
application-account task flow.
2. Add these optional public variables beside the existing Notarius variables,
each defaulted to null:
`narratio_notarius_llm_api_key`, `narratio_notarius_resume`, and
`narratio_notarius_chunk_cache`.
3. Add matching optional argument-specification entries. Use `str` for the
credential reference and chunk-cache mode and `bool` for resume. Do not
duplicate Narratio's native enum or secret-resolution validation in the
role.
4. Extend the existing `notarius` template section and its parent-emission
condition. Render `llm_api_key`, `resume`, and `chunk_cache` only when their
corresponding variable `is not none`; preserve intentional `false` for
resume and serialize every value with the template's established filter.
5. Leave all three fields absent when null. Do not create a secrets directory,
read an environment variable, accept a raw credential value, or select a
Notarius provider in this standalone role.
6. Update the README's version, optional-variable list, native Notarius field
discussion, and validation summary. Explain that the credential field is
an environment-variable name and that credential delivery remains the
caller's responsibility. Describe resume and chunk-cache as native
application choices without assigning D&D defaults.
7. Update the integration suite to explicitly select v2.1.0 for the existing
version-selection assertion. Continue testing version selection rather than
asserting that v2.1.0 remains the default.
8. Extend the representative configuration case with a synthetic environment
reference, `resume: false` as the falsey-value case, and one valid explicit
chunk-cache mode. Supply a synthetic value for that named environment
variable only when running Narratio's native validation or safe inspection;
never put a credential value in managed YAML.
9. Observe the three representative fields and baseline null omission through
the repository's existing focused configuration checks. Retain native
`narratio config validate` coverage and immediate clean repeated
convergence. Do not test ConfigKit's resolution precedence or Narratio's
subprocess forwarding implementation.
### Validation
Run from `ansible-role-narratio`:
```text
tests/integration
git diff --check
git status --short
```
Expected result: syntax and production-profile lint pass; an explicitly
selected v2.1.0 binary reports that version; baseline null values remain
omitted; representative credential-name, false resume, and chunk-cache values
load through Narratio's native configuration interface; immediate repeats
report `changed=0` and `failed=0`; and only intended role, test, and README
files are modified.
### Completion Criteria
- Defaults, argument specification, template, README, and tests expose one
coherent generic interface for the three v2.1.0 fields.
- The role owns no credential value, secrets directory, D&D provider choice,
or suite cache policy.
- Tests exercise the public configuration interface without snapshotting the
complete default document or re-testing application internals.
- The repository is valid and the stage is committed.
- The commit is reachable from the canonical Narratio-role origin before
Stage 2 begins.
### Commit
Commit intent: `Upgrade the Narratio role to version 2.1.0`
## Stage 2: Implement Pipeline Credential Delivery and Reuse Policy
### Objective
Add the pipeline role's production credential-delivery tasks, coordinate its
Audita and Notarius references, and enable Narratio checkpoint reuse through
the reviewed Stage 1 interface.
### Required Policy Review
Before beginning this stage, read:
- `AGENTS.md`
- `docs/orientation.md`
- `docs/conventions.md`
- `docs/ansible.md`
- `docs/testing.md`
- `docs/roadmap/features/0011-managed-pipeline-credential-delivery.md`
- `docs/decisions/0001-minimal-module-driven-ansible.md`
- `docs/decisions/0002-shared-readable-pipeline-filesystem-policy.md`
- the committed Stage 1 changes in `ansible-role-narratio`
- `../narratio/docs/releases/v2.1.0.md`
- `../narratio/docs/config.md`
- `../narratio/docs/integrations/audita.md`
- `../narratio/docs/integrations/notarius.md`
- `../narratio/docs/integrations/scriptorium.md`
- `ansible-role-dnd-pipeline/defaults/main.yml`
- `ansible-role-dnd-pipeline/meta/argument_specs.yml`
- `ansible-role-dnd-pipeline/meta/main.yml`
- `ansible-role-dnd-pipeline/requirements.yml`
- `ansible-role-dnd-pipeline/tasks/main.yml`
- `ansible-role-dnd-pipeline/tests/converge.yml`
- `ansible-role-dnd-pipeline/tests/integration`
### Preconditions
- Stage 1 is committed, validated, and clean.
- The Stage 1 commit is reachable from the canonical
`ansible-role-narratio` Git origin. Stop and report the hash if a fresh
Galaxy installation cannot resolve it.
- `ansible-role-dnd-pipeline` is clean at its stated starting commit or an
expected reviewed descendant.
- Its existing `tests/integration` entry point passes before modification.
### Owning Repository
`ansible-role-dnd-pipeline`
### Required Changes
1. Set `dnd_pipeline_narratio_version: v2.1.0` and pin the
`rakestraw.narratio` entry in `requirements.yml` to the exact Stage 1
commit. Retain the canonical Git URL and all unrelated dependency pins.
2. Add `dnd_pipeline_secrets: {}` as a required, defaulted dictionary in the
public interface. Its keys are credential filenames/environment-variable
names; values are either nonempty strings to converge or null to remove the
exact named file.
3. Add required, defaulted
`dnd_pipeline_narratio_notarius_llm_api_key: OPENROUTER_API_KEY` as a string
and `dnd_pipeline_narratio_notarius_resume: true` as a boolean. Do not add a
suite variable for Narratio's optional chunk-cache override.
4. Change the suite default for
`dnd_pipeline_audita_llm_validation_api_key_env` from
`AUDITA_VALIDATION_LLM_API_KEY` to `AUDITA_LLM_API_KEY`. Retain the
proposal and validation fields as independently overrideable public
variables; do not add an equality assertion or promise split-key forwarding.
5. Pass `dnd_pipeline_audita_llm_proposal_api_key_env` through Narratio's
`narratio_audita_llm_api_key` interface. Pass the new Notarius reference and
resume variables through `narratio_notarius_llm_api_key` and
`narratio_notarius_resume`. Do not pass
`narratio_notarius_chunk_cache`; Notarius's root `auto` setting remains in
control for ordinary runs.
6. Before any task interpolates a mapping key into a path, validate every
`dnd_pipeline_secrets | dict2items` entry in one `ansible.builtin.assert`
loop. Require a string key matching `^[A-Za-z_][A-Za-z0-9_]*$` and a value
that is either null or a nonempty string. Apply `no_log: true` to the entire
validation task. Do not normalize, trim, rename, or infer keys or values.
7. Create `/usr/local/etc/narratio/secrets` with one ordinary
`ansible.builtin.file` task using the selected runtime user and group and
fixed mode `0750`. Do not recursively alter existing entries.
8. Converge non-null mapping entries with one `ansible.builtin.copy` loop to
`/usr/local/etc/narratio/secrets/<key>`, using the selected runtime user and
group and fixed mode `0640`. Write only the supplied value; do not append
metadata, encode it, back it up, or reread it. Apply `no_log: true`.
9. Converge null entries to absence with one `ansible.builtin.file` loop over
the same mapping. Remove only the exact validated named file and apply
`no_log: true`. An empty mapping and omitted keys must leave existing
directory entries untouched.
10. Keep production convergence to those four operations: validation,
directory creation, non-null copy, and explicit-null removal. Do not add
`stat`, `find`, commands, handlers, environment files, shell profiles,
launchers, services, providers, rotation, or role-private state.
11. Adapt the existing integration driver's baseline arguments to supply
distinctive synthetic values for `AUDITA_LLM_API_KEY` and
`OPENROUTER_API_KEY` through `dnd_pipeline_secrets`, with both ambient
variables explicitly unset. Preserve isolated dependency installation and
cleanup restoration.
12. Extend the existing native Narratio effective-configuration check to
observe the two public credential reference names and `resume: true` while
confirming that Narratio's `chunk_cache` override is absent. Retain the
native Notarius check that its root chunk-plan mode is `auto`, the service
and existing-group-member validation paths, and the baseline clean repeat.
### Validation
Run from `ansible-role-dnd-pipeline`:
```text
tests/integration
git diff --check
git status --short
```
Expected result: isolated role installation resolves the exact Stage 1
commit; syntax and production-profile lint pass; the role converges the
synthetic credential mapping without displaying its values; Narratio natively
loads the conventional secrets directory as both the service account and the
existing-group-member fixture; its safe effective configuration names the
Audita and Notarius references and enables resume without a chunk-cache
override; Notarius retains `auto`; the baseline repeat reports `changed=0` and
`failed=0`; and only intended pipeline-role files are modified.
### Completion Criteria
- The suite installs Narratio v2.1.0 through the exact reviewed role revision.
- One public mapping owns only explicitly named credential files, including
explicit null removal, behind complete pre-path validation and secret-safe
task output.
- Audita's suite defaults use one typed reference, Notarius uses
`OPENROUTER_API_KEY`, and ordinary extraction requests checkpoint reuse.
- The pipeline does not duplicate Notarius's chunk-plan policy or add a second
credential-loading mechanism.
- The existing integration suite passes and the repository is committed.
### Commit
Commit intent: `Add managed credentials to the D&D pipeline`
## Stage 3: Document and Exercise the Credential Contract
### Objective
Document the pipeline's credential and reuse policy and add the smallest
behavioral coverage for validation, ownership semantics, explicit removal,
redaction, authorization, and immediate idempotence.
### Required Policy Review
Before beginning this stage, read:
- `AGENTS.md`
- `docs/orientation.md`
- `docs/conventions.md`
- `docs/ansible.md`
- `docs/testing.md`
- `docs/roadmap/features/0011-managed-pipeline-credential-delivery.md`
- `docs/decisions/0002-shared-readable-pipeline-filesystem-policy.md`
- the committed Stage 1 and Stage 2 changes
- `../narratio/docs/config.md`
- `../narratio/docs/cli.md`
- `../narratio/docs/integrations/notarius.md`
- `ansible-role-dnd-pipeline/README.md`
- `ansible-role-dnd-pipeline/tests/integration`
- `ansible-role-dnd-pipeline/tests/converge.yml`
### Preconditions
- Stages 1 and 2 are committed, validated, and clean.
- The pipeline's isolated dependency installation resolves the Stage 1 commit.
- The deployed baseline can be restored through the integration driver's
existing cleanup path.
### Owning Repository
`ansible-role-dnd-pipeline`
### Required Changes
1. Update the README's compatible Narratio version, Audita validation
credential default, public variable documentation, and obsolete statements
that API-key management and checkpoint reuse are deferred.
2. Document `dnd_pipeline_secrets` as an empty-by-default mapping with valid
environment-variable keys, nonempty string values for presence, null for
exact removal, and omission for unmanaged entries. Explain that the role
neither mirrors nor purges the directory.
3. Include an Ansible Vault inventory pattern that references encrypted
variables without showing a real credential. State that Vault is a
recommended consumer workflow, not functionality implemented by the role.
4. Document the fixed conventional path, `0750` directory and `0640` file
modes, selected runtime ownership, runtime-group authorization meaning,
process-environment precedence, and the absence of wrappers, services, or
parallel loaders. Make clear that ordinary configuration remains
world-readable and secret-free while the secrets subtree does not grant
world access.
5. Document the coordinated `AUDITA_LLM_API_KEY` default, the typed
`OPENROUTER_API_KEY` Notarius reference, Scriptorium's existing
profile-owned reference behavior, ordinary checkpoint resume, Notarius's
root-owned `auto` chunk cache, and Narratio's forced refresh behavior.
6. Add one representative expected-failure case for an invalid mapping key
and one for an empty non-null value. Require each convergence to fail before
any candidate destination is managed. Keep task output secret-safe, do not
assert a task name or other implementation trace, and do not add an
exhaustive validation matrix.
7. Exercise explicit-null removal with a uniquely named synthetic credential:
first manage it, select its name through the public Notarius reference
variable, and confirm native configuration loading succeeds. Then converge
the same mapping entry to null with the relevant ambient variable unset.
Observe unavailability through an expected failure from Narratio's native
configuration loader rather than by inspecting a module result or file
metadata.
8. Require an immediate identical repeat of the explicit-null state to report
`changed=0` and `failed=0`, then restore the baseline credential mapping so
cleanup and later tests remain deterministic.
9. Retain native configuration validation as the service account and the
existing-group-member fixture. Add at most one representative native
negative check showing that an identity outside the runtime group cannot
load the credential-backed pipeline; do not build a permission matrix or
assert numeric filesystem metadata.
10. Capture convergence and native inspection output and fail if either
distinctive synthetic credential value appears. Names may appear in safe
effective configuration; values must not.
11. Retain the existing baseline, testing-profile, selected-version,
caller-composed Notarius pipeline, shared-cache, immutable local asset,
and clean-repeat coverage. Do not contact an LLM, WhisperX, storage, or
publishing endpoint; do not execute extraction or test ConfigKit,
Narratio, Notarius, or trusted Ansible modules independently.
### Validation
Run from `ansible-role-dnd-pipeline`:
```text
tests/integration
git diff --check
git status --short
```
Expected result: isolated dependency installation, syntax checking, and
production-profile lint pass; valid synthetic credentials load through the
conventional directory for authorized identities; invalid names and empty
values fail safely; explicit null makes only the selected credential
unavailable through native loading and converges cleanly on repeat; no
synthetic value appears in captured output; all existing representative paths
continue to pass; and documentation matches the public interface.
### Completion Criteria
- Consumer documentation completely describes the mapping, Vault usage,
authorization boundary, credential flows, cache policy, and exclusions.
- Tests protect role-owned validation, explicit ownership semantics,
redaction, authorized use, and clean repetition without testing module or
application internals.
- The default suite and representative explicit-null state each converge
cleanly on immediate repeat.
- The full integration suite passes and the repository is committed.
### Commit
Commit intent: `Document and test pipeline credential delivery`
## Stage 4: Record the Implementation for Review
### Objective
Record the completed implementation and validation evidence in the workspace
without declaring the feature complete before independent review.
### Required Policy Review
Before beginning this stage, read:
- `AGENTS.md`
- `docs/orientation.md`
- `docs/conventions.md`
- `docs/planning.md`
- `docs/roadmap/features/0011-managed-pipeline-credential-delivery.md`
- `docs/roadmap/implementation.md`
- `docs/status.md`
### Preconditions
- Stages 1 through 3 are committed and their owning repositories are clean.
- Both complete integration entry points pass at the committed revisions.
- The workspace control repository is clean at the planning commit.
### Owning Repository
`ansible-workspace-dnd`
### Required Changes
1. Update this plan's Progress Notes with the three implementation commit
hashes and concise validation results.
2. Update `docs/status.md` so Feature 0011 is `in_progress`, its evidence names
the implementation commits, and the current focus is independent review of
the completed implementation.
3. Keep the feature roadmap `ready`; do not mark Feature 0011 `done`, clean the
durable roadmap, or create or revise `docs/roadmap/remediation.md`. Those
actions belong to independent review and closeout.
4. Do not change implementation repositories or unrelated workspace policy in
this stage.
### Validation
Run from the workspace root:
```text
git diff --check
./scripts/status
git status --short
```
Expected result: the status report identifies only the intended workspace
tracking edits before commit; both implementation repositories are clean; and
the dashboard and progress notes point to the exact completed stage commits.
### Completion Criteria
- Implementation and validation evidence is traceable across repositories.
- Feature 0011 remains awaiting independent review rather than being marked
complete.
- The workspace control repository is valid and the stage is committed.
### Commit
Commit intent: `Record the pipeline credential implementation`
## Final Validation and Handoff
After Stage 4, rerun both owning repositories' complete integration entry
points:
```text
cd ansible-role-narratio && tests/integration
cd ../ansible-role-dnd-pipeline && tests/integration
```
Then, from the workspace root, run:
```text
git diff --check
./scripts/status
```
The final handoff must:
- identify the Stage 1 through Stage 4 commit hashes and confirm every changed
repository has a clean working tree;
- confirm syntax and production-profile lint pass in both changed roles;
- confirm the standalone role installs an explicitly selected Narratio v2.1.0,
exposes all three nullable native fields, validates representative values,
and immediately converges cleanly;
- confirm the pipeline installs the exact immutable Narratio-role commit,
loads managed synthetic credentials for authorized identities, rejects bad
mapping entries before path use, removes an explicitly null entry, suppresses
credential values, requests Notarius checkpoint reuse, leaves ordinary
chunk-plan mode with Notarius, and immediately converges cleanly;
- confirm no upstream application, asset, collection, unrelated standalone
role, real credential, external service, or real human account was modified;
- summarize any deviation, blocker, or deferred work, including any pause
required to publish the Stage 1 dependency commit;
- report any production complexity beyond the three generic Narratio fields,
suite variables and dependency wiring, and the four direct credential tasks,
together with the acceptance criterion that required it; and
- leave Feature 0011 `in_progress` for independent review rather than marking
it `done`.
## Progress Notes
- Planning complete; implementation has not begun.

View File

@@ -255,9 +255,27 @@ application-role releases in one source repository.
- Debug output is not enabled by the MVP. Any later debug feature must either
produce world-readable non-secret D&D diagnostics or identify and justify a
narrower application contract before deployment.
- Credential material will live beneath a restricted `secrets/` subdirectory
owned by `narratio:narratio`, with directories mode `0750` and files mode
`0640`. Ordinary configuration contains only credential reference names.
- Credential material lives beneath the conventional
`/usr/local/etc/narratio/secrets` subdirectory owned by
`narratio:narratio`, with directory mode `0750` and file mode `0640`.
Membership in the `narratio` group is an explicit authorization to use the
suite's shared credentials. This deliberately differs from Narratio's
general single-user `0700` and `0600` recommendation while retaining no
world access. Ordinary configuration contains only credential reference
names.
- The pipeline role exposes one empty-by-default credential mapping. Non-null
entries manage files named for valid environment variables; null explicitly
removes the named file; omitted names remain unmanaged. Credential values
are expected to come from encrypted inventory such as Ansible Vault and are
suppressed from task output.
- Narratio's native ConfigKit secrets bootstrap owns loading these files and
process-environment precedence. The suite does not add a wrapper, shell
profile, service environment file, or parallel credential loader.
- The suite coordinates Audita proposal and validation on the
`AUDITA_LLM_API_KEY` reference and selects `OPENROUTER_API_KEY` for
Notarius's production extraction profile. Narratio v2.1.0 resolves those
typed references and forwards only the selected credentials to the
corresponding subprocesses.
- [Decision 0002](../decisions/0002-shared-readable-pipeline-filesystem-policy.md)
records the complete rationale and boundary for this policy.
@@ -309,8 +327,9 @@ application-role releases in one source repository.
cache beneath `/var/cache/notarius`. That FHS cache root is owned by the
`narratio` account and group with mode `2775` and the suite's shared default
ACL policy. Notarius v0.7.0 leaves cache-entry access to that deployment
policy. Chunk plans are reused automatically; checkpoints are recorded but
Narratio does not yet request their reuse.
policy. Chunk plans are reused automatically. Narratio v2.1.0 requests reuse
of compatible recorded checkpoints during ordinary extraction; forced
extraction omits checkpoint loading and refreshes the active chunk plan.
- The standalone Notarius role exposes Notarius v0.7.0's native ConfigKit
import list while remaining independent of D&D content. The pipeline role
owns an imported `pipelines/dnd-session.yml` fragment containing its
@@ -378,7 +397,6 @@ The following areas require investigation and decisions before implementation:
- The mechanism used to incorporate the independently developed pipeline role
into collection releases
- Secret injection and storage
- Optional suite-level operator-selection and group-membership management
- Upgrade, downgrade, and removal behavior
- Collection bootstrap and end-to-end validation details beyond the locked

View File

@@ -1,10 +1,10 @@
# Workspace Status
Last updated: 2026-09-15
Last updated: 2026-09-16
Current milestone: M1-03 suite-level pipeline role
Current focus: Select the next unit of work.
Current focus: Begin the Feature 0011 implementation sprint.
| ID | Deliverable | Owner | State | Evidence or next step |
| --- | --- | --- | --- | --- |
@@ -15,7 +15,7 @@ Current focus: Select the next unit of work.
| 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 | done | Feature 0003 roadmap fixes the Notarius configuration interface |
| M1-02 | Standalone application roles | role repositories | done | All five standalone roles implemented and independently reviewed |
| M1-03 | Suite-level pipeline role | ansible-role-dnd-pipeline | in_progress | Feature 0010 is complete; select the next suite-policy feature |
| M1-03 | Suite-level pipeline role | ansible-role-dnd-pipeline | in_progress | Execute the ready Feature 0011 implementation plan |
| 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 | Field-specific configuration completed in `e61f02f` and `847c4c3`; full suite passed |
@@ -27,6 +27,7 @@ Current focus: Select the next unit of work.
| F0008 | Define opinionated D&D processing configuration | ansible-role-dnd-pipeline | done | Implemented and independently reviewed through remediation commit `3e253d7` |
| F0009 | Establish shared pipeline filesystem and execution | configuration-bearing application roles and ansible-role-dnd-pipeline | done | Implemented and independently reviewed through remediation commit `b15fe8d` |
| F0010 | Integrate D&D Notarius extraction | ansible-role-notarius and ansible-role-dnd-pipeline | done | Implemented and independently reviewed through remediation commit `bbf01e1` |
| F0011 | Deliver pipeline credentials and Notarius reuse | ansible-role-narratio and ansible-role-dnd-pipeline | ready | Staged implementation plan is ready at `docs/roadmap/implementation.md` |
| A0001 | Audit completed standalone-role test suites | workspace and standalone roles | done | Findings recorded in `docs/roadmap/test-suite-audit-findings.md` |
| R0001 | Simplify completed standalone-role test suites | workspace and standalone roles | done | Role commits `e08c63c`, `d2d0b9d`, and `cfecf37` passed implementation and independent review |