Resolve typed credentials safely

This commit is contained in:
2026-09-07 04:29:00 +00:00
parent d31ba5a410
commit 92432232ef
34 changed files with 572 additions and 260 deletions

View File

@@ -47,7 +47,9 @@ the durable copied session input.
`narratio config validate`, `narratio config show`, and `narratio config
sources` use the same `--config`, `--campaign`, `--campaign-file`, and
`--profile` selection rules as pipeline commands, but do not select, discover,
or load a session. They do not read credential values or create runtime state.
or load a session. They resolve configured typed credential references as part
of the ordinary ConfigKit load but expose only their environment-variable names;
they do not create runtime state.
`narratio config diff <left-profile> <right-profile>` uses the same pipeline and
campaign selectors, resolves each named profile with an independent ConfigKit
load, and does not accept a separate `--profile` flag.
@@ -215,7 +217,10 @@ inputs:
## Secrets Handling
- Do not place raw secrets in YAML.
- Use env var names in config (for example `pipeline.audita.llm_api_key_env`).
- Credential fields contain environment-variable names, represented internally
as ConfigKit typed secret references (for example
`pipeline.audita.llm_api_key: AUDITA_LLM_API_KEY`). A configured reference
must resolve to a nonempty environment value during load.
- Every command that loads a pipeline accepts `--secrets-dir <path>`. When the
flag is omitted, Narratio uses a present `secrets/` directory beside the
resolved root `pipeline.yml`; an absent conventional directory is ignored.
@@ -283,8 +288,8 @@ Rules:
| `pipeline.storage.s3.region` | string | No | empty |
| `pipeline.storage.s3.endpoint` | string | No | empty |
| `pipeline.storage.s3.force_path_style` | bool | No | `false` |
| `pipeline.storage.s3.access_key_id_env` | string | No | `OBJECT_STORAGE_KEY_ID` |
| `pipeline.storage.s3.secret_access_key_env` | string | No | `OBJECT_STORAGE_KEY` |
| `pipeline.storage.s3.access_key_id` | secret reference | Conditional | unset; must be paired with `secret_access_key` |
| `pipeline.storage.s3.secret_access_key` | secret reference | Conditional | unset; must be paired with `access_key_id` |
| `pipeline.spool.root` | string | No | `/var/spool/narratio` |
| `pipeline.spool.delete_audio_after_publish` | bool | No | `false`; selects the published run's spool-audio target for final cleanup and requires effective `pipeline.publish.enabled=true` and `pipeline.publish.upload_run=true` |
| `pipeline.cache.root` | string | No | `/var/cache/narratio` |
@@ -312,7 +317,7 @@ Rules:
| `pipeline.seriatim.env.filler_max_duration` | float | No | unset |
| `pipeline.audita.binary` | string | No | `audita` |
| `pipeline.audita.timeout` | duration | No | `3h` |
| `pipeline.audita.llm_api_key_env` | string | No | empty |
| `pipeline.audita.llm_api_key` | secret reference | No | unset |
| `pipeline.audita.modules[]` | list[string] | No | empty |
| `pipeline.audita.base_url` | string | No | empty |
| `pipeline.audita.model` | string | No | empty |

View File

@@ -41,7 +41,6 @@ Construction fails for invalid static config values, including:
Run fails for:
- missing required request paths;
- missing required credential env var when configured (`llm_api_key_env`);
- subprocess execution failure;
- invalid processed transcript JSON (`segments` array required);
- invalid report JSON when reporting is enabled.
@@ -53,6 +52,9 @@ Failure results still include output/log/config/exit metadata for diagnostics.
## Deterministic Behavior
- CLI args are built from runner config + request in a fixed order.
- A configured typed `llm_api_key` reference is resolved during pipeline load;
the captured value is supplied only as the child's `AUDITA_LLM_API_KEY`
override and is never re-read from the parent environment.
- Generated invocation YAML (`audita.generated.v1`) is emitted when requested.
- Manifest writes are stage-owned; adapter itself is stateless.

View File

@@ -84,8 +84,9 @@ in-memory backend without changing package state.
Cancellation, deadlines, and
diagnostic limits use the same terminal disposal path without losing their
original result classification. Child environments contain only the execution
baseline and adapter-specified values; configured credentials are explicit
sensitive values. Stdout and stderr are redacted while streaming into separate
baseline and adapter-specified values; configured credentials are resolved
once by ConfigKit and passed explicitly as sensitive values. Adapters do not
re-read their named environment references. Stdout and stderr are redacted while streaming into separate
8 MiB diagnostic captures; a bounded wait closes a stream retained by a
departed leader's descendant. Unsupported platforms reject owned command
execution.

View File

@@ -155,8 +155,10 @@ execution, but the digest substitutes their normalized logical values captured
before root-relative resolution, so relocating an equivalent configuration
bundle does not change provenance. Because ConfigKit and resolution metadata
are private, the digest excludes source layout, profile name, and ownership.
Configuration stores environment variable names rather than resolving raw
credentials, so raw secret values are neither loaded nor hashed.
Configuration stores credentials as ConfigKit typed references. A successful
load resolves their values inside the opaque typed boundary, while this safe
projection serializes only `EnvName()` identities. Credential-value changes are
therefore never hashed; reference-name changes remain ordinary provenance.
`recomputePipelineEffectiveDigest` is the single package-owned refresh point
for later runtime expansion.

View File

@@ -35,8 +35,9 @@ validation model, module set, transcript description, output schema, selected
external configuration path, and canonical polished-transcript identity. Module
ordering is normalized because the configured modules form a set. Audita's
executable, timeouts, concurrency, report and debug behavior, work retention,
and credential environment name are operational and do not invalidate a
successful result.
and resolved credential value are operational and do not invalidate a
successful result. The public credential reference name may participate in
configuration provenance but never exposes its resolved value.
Narratio can fingerprint a selected model, module, or configuration identifier,
but it cannot inspect content that Audita privately resolves behind that stable

View File

@@ -566,6 +566,13 @@ invalid existing directories fail the command. ConfigKit follows a symlink used
as the selected directory, but immediate entries must be regular non-symlink
files. Existing environment variables—including empty values—always win.
S3 `access_key_id` and `secret_access_key` are an optional pair of typed
environment references. When both are absent, the AWS SDK retains its default
credential provider chain. When both are configured, Narratio supplies their
load-time resolved values through an explicit static provider. Audita's optional
`llm_api_key` is likewise captured at pipeline load and supplied only through
the child process's `AUDITA_LLM_API_KEY` override.
External adapter results are individually bounded before Narratio validates or
materializes them. These per-file limits do not reserve disk space: prevent hard
disk exhaustion with filesystem, service, container, or volume quotas sized for

View File

@@ -320,7 +320,11 @@ architecture, and troubleshooting to the new bootstrap/permission contract.
## Stage 7 — Resolve typed credentials and protect all output boundaries
Status: Pending
Status: Complete — Migrated S3 and Audita credentials to optional ConfigKit
typed references, passed resolved values directly to runtime adapters, preserved
the AWS default provider chain when unconfigured, and verified reference-only
inspection, provenance, generated configuration, manifest, and diagnostic
boundaries with focused, full, and race test suites.
Read: ConfigKit “Typed secret references” and rendering contracts;
`internal/config/config.go`, `defaults.go`, `validate_adapters.go`, the safe