Plan Notarius credential forwarding

This commit is contained in:
2026-09-16 14:14:56 +00:00
parent cd75bf6d5a
commit 2d346733d6
2 changed files with 315 additions and 225 deletions

View File

@@ -1,225 +1,220 @@
# ConfigKit v0.3.0 Implementation Plan # Notarius Credential Forwarding Implementation Plan
Status: Complete — 2026-09-16 Status: Not started
Implement the [feature roadmap](configkit-v0.3.0.md) in sequential stage order. Implement the [feature roadmap](notarius-credential-forwarding.md) in the order
Each stage is a bounded assignment for a gpt-5.6-terra coding agent. Mark it below. Each stage is a bounded assignment for a gpt-5.6-terra coding agent.
complete only after its acceptance checks pass, and record the commands and Record a short completion note and executed checks beside each stage's status;
results beside its status. This replaces the completed original ConfigKit do not mark it complete until its acceptance criteria pass. The completed
migration plan; that history remains in Git. ConfigKit upgrade plan is preserved in Git history.
## Working rules and fixed decisions ## Instructions and fixed decisions
- Read `docs/development.md`, all documents under `docs/policy/`, the feature - Read `docs/development.md`, all documents under `docs/policy/`, the feature
roadmap, and the stage-specific sources before editing. roadmap, and each stage's listed sources. Inspect callers and existing tests
- Scope is the ConfigKit upgrade and the identified configuration-discovery test before editing; file lists are starting points, not edit allowlists.
isolation gaps. Do not implement the separate Notarius credential-forwarding - Implement one optional `notarius.llm_api_key` field. It contains an environment
proposal, remove other roadmaps, or refactor unrelated command infrastructure. variable name, not a value. Omission and null mean no credential forwarding.
- Keep production configuration semantics unchanged: root-relative pipeline Every surviving non-null reference resolves during ordinary configuration
paths, current schema identifiers, imports/profiles, presence-aware defaults, loading, even when extract is disabled, omitted from the range, or resumable.
historical ownership checks, effective-only secret resolution, safe inspection, - Reuse ConfigKit v0.3.0 and invocation-scoped secret bootstrap. Do not upgrade
and domain expansion. Do not adopt `Resolution.InputInfo` or dependencies, modify upstream repositories, add flags, infer provider settings,
`Resolution.Source` without a separate feature requirement. or add a general environment map. Keep unrelated work out of scope.
- Use the released ConfigKit v0.3.0 root API, not internal packages or sibling - Keep the credential opaque across configuration and stage orchestration:
checkout state. No replace directive, workspace dependency, vendoring, parser both `NotariusConfig.LLMAPIKey` and `notarius.RunRequest.LLMAPIKey` use
fallback, or additional base load. `*configkit.Secret`. This is the adapter's explicit typed-credential boundary
- Preserve the Go 1.26 baseline and Linux amd64/arm64 plus best-effort macOS arm64 contract, using only ConfigKit's public root API. It avoids another credential
targets. Do not change CI toolchains or upstream repositories in this work. abstraction and avoids introducing raw-secret string fields in stage requests.
- Tests use real configuration/discovery logic and temporary files. Synthetic Only the Notarius adapter calls `Value()` to construct the child environment.
credentials only; no real system configuration, cloud services, or adapter - A supplied adapter credential must already be resolved. Reject unresolved or
executables. Do not mutate global search paths in the tests being repaired. empty programmatic references before process launch with a value-free error.
- Keep each stage buildable. Diagnose unexpected failures rather than skipping Do not resolve again from the process environment or silently drop an invalid
tests, weakening invariants, or changing snapshots indiscriminately. credential. Nil remains supported by all existing callers and fakes.
- Update canonical documentation only for implemented changes. Published release - The adapter forwards `EnvName()` unchanged through `EnvOverrides` and
notes, including v2.0.0, are immutable. No tag, push, release, or CI polling is includes that name in `SensitiveEnvNames`. Do not widen the shared inherited
authorized by this plan. environment or hardcode a provider-specific destination variable.
- Neither credential name nor value participates in extract's semantic resume
fingerprint. Safe configuration projections include at most the name:
renaming can change the effective digest, value rotation cannot. Do not bump
the extract fingerprint version or add digest-wide invalidation.
- Never serialize or log a raw subprocess request/environment map. Tests use
disposable synthetic credentials and assertions that do not print values on
failure. Environment-mutating tests are nonparallel and restore state.
- Retain the roadmap's downstream responsibility boundary: redact captured
streams, but do not scan/rewrite arbitrary Notarius files or claim protection
against encoded secrets or a malicious executable.
- Each stage must compile and keep existing tests green. Stage 1's configuration
support is an intermediate state, not a releasable completed feature.
- No release, tag, push, upstream changes, or CI polling are part of this plan.
Preserve published release notes and unrelated roadmaps.
## Stage 1 — Isolate command configuration discovery ## Stage 1 — Add typed configuration and safe projections
Status: Complete — 2026-09-16 Status: Not started
Completion: Added an invocation-local pipeline discovery dependency with the Read: `internal/config/config.go`, `credential_projection.go`,
production resolver as its default, then moved the affected missing-config tests `effective_projection.go`, `effective_pipeline.go`, `effective_digest.go`,
onto real resolver calls over temporary candidate paths. The command-boundary `pipeline_configkit.go`, `pipeline_normalize.go`, `validate_notarius.go`,
coverage verifies successful default selection reaches the configured loader and `notarius_test.go`, `load_validate_test.go`, and `pipeline_configkit_test.go`.
that discovery failures stop before bootstrap, typed loading, or execution. Also read `docs/config.md` and `docs/internal/configuration.md`.
Checks: 1. Add `LLMAPIKey *configkit.Secret` to `NotariusConfig`, with
`yaml:"llm_api_key,omitempty" json:"llm_api_key,omitempty"`. Leave its default
- `go test ./internal/app -run 'TestResolvePipelineConfigPath|TestExecute.*Missing|TestExecuteSessionInitMissingDefaultConfig'` — pass. nil. Do not add a new environment lookup, codec, or secret-validation framework;
- `GOWORK=off go test -race ./internal/app` — pass. ConfigKit already owns loaded reference syntax and resolution.
- `GOWORK=off go test ./...` — pass with ConfigKit v0.1.2. 2. Add `NotariusConfig.MarshalYAML` in `credential_projection.go`, following
the existing Audita/S3 pattern: preserve all other Notarius fields and their
Purpose: Make missing-config command tests deterministic before changing the tags, and project the credential through `secretReferenceName`.
dependency, while exercising the real discovery logic. This protects direct YAML serialization as well as effective inspection.
JSON uses ConfigKit's safe secret representation. Preserve existing behavior
Read: of nil stage sections, reference/output maps, and logical path normalization.
`internal/app/command_dependencies.go`, `commands.go`, `config_loader.go`, 3. Verify that the existing effective projection, schema-derived metadata paths,
`pipeline_config_path.go`, `pipeline_config_path_test.go`, copy/normalization helpers, source reporting, and digest capture carry the new
`analyze_artifacts_commands_test.go`, `commands_test.go`, field correctly. Make only narrowly necessary changes. Do not add another
`operator_helpers_test.go`; configuration discovery in `docs/cli.md` and projection or provenance store, and do not mutate or resolve the secret in
`docs/config.md`. normalization.
4. Extend package-boundary tests for:
Implementation: - omitted/null references; a valid custom reference; missing, empty, and
invalid reference names rejected without exposing values;
1. Add a private `resolvePipelineConfigPath func(string) (string, error)` field - selected profiles replacing or nulling an unavailable base reference,
to `commandDependencies`. Normalize a nil value to the existing while references in disabled Notarius configuration still resolve;
`resolvePipelineConfigPath` function. Keep that function's production default - safe effective YAML/JSON and direct pipeline YAML containing the reference
candidates and explicit-path behavior unchanged. name, never its value; nil references retain the previous projection shape;
2. In `loadPipelineConfigWithDependencies`, call the normalized dependency - correct root/profile provenance for the new field;
instead of the free function. Discovery must still precede secret bootstrap - unchanged safe digest after rotating a value under the same name, and
and typed configuration loading. Preserve the selected path and all existing observable projection/diff identity changes after renaming the reference.
error propagation. Verify all command-loading paths continue through this Reuse existing credential-projection and profile regression test owners.
shared owner; do not add per-command discovery implementations. Avoid asserting the entire configuration or complete diagnostic wording.
3. Repair the analyze and publish missing-config tests and the
`run missing config uses defaults` case in `TestExecuteMissingRequiredFlags`.
Call `executeWithDependencies` with a discovery closure that invokes
`resolvePipelineConfigPathWithCandidates` on an invocation-owned list of
missing paths under `t.TempDir()`. Use fresh dependencies per invocation.
A small shared test helper is appropriate for this repeated fixture.
Do not merely inject a prewritten discovery error or add an explicit
`--config` argument: these tests must still exercise default discovery.
4. Convert `TestExecuteSessionInitMissingDefaultConfigReportsSearchedPaths`
to the same seam, removing its global search-path mutation. Change
`TestResolvePipelineConfigPathWithCandidatesMissing` to temporary missing
paths rather than assumed-nonexistent absolute host paths.
Inspect adjacent missing-config tests and apply the same treatment where
needed; do not turn this into a general rewrite of unrelated successful
command fixtures.
5. Preserve the existing resolver tests for explicit precedence and first
existing candidate. Add one representative command-boundary case using a
temporary existing default pipeline and the real resolver to prove that
successful discovery reaches the configured loader with that selected path.
Stop at a controlled loader boundary; do not initialize runtime services.
In a representative missing-path case, guard the loader/bootstrap/executor
boundaries so accidental continuation after discovery failure fails the test.
6. Assert nonzero exit status, command context, a short missing-config diagnostic,
and the controlled searched path. Avoid complete prose snapshots. Keep all
test fixtures outside real system configuration and secrets directories.
Acceptance: Acceptance:
- `go test ./internal/app -run 'TestResolvePipelineConfigPath|TestExecute.*Missing|TestExecuteSessionInitMissingDefaultConfig'` - `GOWORK=off go test ./internal/config`
- `go test -race ./internal/app` - `GOWORK=off go test ./...`
- `go test ./...` on the existing ConfigKit v0.1.2 dependency. - Existing examples still load without adding new required credentials.
- The repaired tests own their candidate paths and do not depend on installed - No adapter delivery yet, but all serialization paths for the new field are safe.
configuration being absent. Do not create/remove an actual system config to
demonstrate this. Ordinary `Execute` still uses the production defaults.
## Stage 2 — Adopt the released dependency and version diagnostics ## Stage 2 — Deliver the selected credential and register redaction
Status: Complete — 2026-09-16 Status: Not started
Completion: Upgraded the normal module graph from ConfigKit v0.1.2 to the Read: `internal/adapters/notarius/runner.go`, `subprocess.go`, `fake.go`,
released v0.3.0 without production-code migration. Party schema version `1` `subprocess_test.go`; `internal/adapters/subprocess/run.go`,
remains invalid through Narratio's unsupported-version check, while a boolean `diagnostics.go`, `run_test.go`, `execution_test.go`;
case preserves malformed-kind coverage. Existing credential, ownership, `internal/stage/extract.go`, `extract_test.go`; and
inspection, provenance, digest, bootstrap, and maintained-example regressions `docs/internal/adapters.md`, `docs/internal/stage-extract.md`.
pass unchanged.
Checks: 1. Add the optional opaque field to `notarius.RunRequest` and document that it
is an already-resolved reference. Pass `notariusConfig.LLMAPIKey` when extract
- `GOWORK=off go mod tidy -diff` — pass. constructs the request. Do not add credentials to stage metadata, receipts,
- `GOWORK=off go mod verify` — pass. output definitions, manifests, reference arguments, or generated files.
- `GOWORK=off go test ./internal/config ./internal/app` — pass. Keep fakes compatible and avoid dumping credential-bearing requests in tests.
- `GOWORK=off go test -race ./internal/config ./internal/app` — pass. 2. Validate any non-nil request credential before subprocess launch. Obtain the
- Targeted preserved-behavior regression selections in `internal/config` and name and value only at this adapter boundary; a non-nil unresolved reference
`internal/app` — pass. is an error, not an instruction to reread the environment.
3. In `SubprocessRunner.Run`, supply exactly the selected credential override
Purpose: Upgrade without altering the set of valid Narratio configurations. and its sensitive name to the shared subprocess request. Nil adds no override
or sensitive name. Preserve arguments, reference ordering, timeout, working
Read: `go.mod`, `go.sum`, `internal/config/party.go`, directory, log/receipt destinations, process lifecycle, and bundle validation.
`party_test.go`, `pipeline_configkit.go`, `pipeline_configkit_test.go`, 4. Extend the extract request test to show the selected resolved reference
`document_configkit.go`, and ConfigKit's tagged v0.2.0/v0.3.0 release notes reaches the adapter without adding raw values to result metadata. Construct
and public API documentation. The downloaded module is authoritative; a sibling resolved test credentials through the real loader, not reflection or private
checkout is reference material only. ConfigKit state.
5. Extend adapter tests for nil, resolved custom name, and unresolved reference.
Implementation: Assert custom override/name wiring and unchanged argv without printing the
environment map. A caller changing the parent environment after loading must
1. Run `GOWORK=off go get gitea.maximumdirect.net/eric/ConfigKit@v0.3.0` not change the resolved credential delivered in this request.
and `GOWORK=off go mod tidy`. Review the dependency diff; this task does not 6. Extend the existing real subprocess test using a controlled executable:
authorize unrelated module or Go-version upgrades. - check that the selected value arrives under exactly the configured name,
2. Change the integer-version case in and an unrelated synthetic parent credential is absent;
`TestParsePartyCanonicalRejectsInvalidDocuments` from a `version.type` - report only success/failure of those checks, not an environment dump;
expectation to unsupported-version rejection. Rename it to identify an - emit the selected canary through stdout/stderr in success and nonzero-exit
unsupported integer version. Preserve `PartySchemaVersion = cases, and assert its absence from persisted captured streams and returned
"narratio.party.v1"` and the existing application checker. errors, with the existing redaction marker present;
3. Retain successful canonical-party coverage and ordinary-string strictness. - keep a valid successful bundle/receipt fixture; put any stdout canary in an
Add one boolean schema-version case if no equivalent malformed-kind coverage ignorable JSON field rather than corrupting the expected receipt protocol.
exists, so the suite distinguishes an unsupported identifier from an invalid Use a custom name outside the built-in sensitive-name list. Verify actual
version kind. Do not duplicate ConfigKit's numeric grammar matrix. redaction on disk, not only the request's `SensitiveEnvNames`.
4. Run existing profile credential replacement/null-removal, hidden duplicate 7. Reuse shared subprocess timeout/cancellation and streaming-boundary coverage.
ordinary/family definition, safe inspection, provenance, digest, bootstrap, Do not duplicate its whole test matrix or add a new redactor. If adapter
and maintained-example tests unchanged. In particular, keep the control flow bypasses that shared path, add a focused regression for it.
`Metadata.HistoricalPaths()` check and the single effective pipeline load.
No production migration is expected beyond Stage 1's dependency seam.
5. Investigate any additional behavior difference against the roadmap before
changing production policy. Do not introduce private-engine imports or restore
old parsing to preserve incidental error wording.
Acceptance: Acceptance:
- `GOWORK=off go mod tidy -diff` and `GOWORK=off go mod verify`. - `GOWORK=off go test ./internal/adapters/notarius ./internal/adapters/subprocess ./internal/stage`
- `GOWORK=off go test ./internal/config ./internal/app`. - `GOWORK=off go test -race ./internal/adapters/notarius ./internal/stage`
- `GOWORK=off go test -race ./internal/config ./internal/app`. - Delivery is opt-in and uses the loaded value. Known and custom names receive
- Valid configurations and effective secret handling remain unchanged; integer the same captured-stream protection, including subprocess failure diagnostics.
party versions still fail. No temporary module file, test overlay, local
replacement, or skipped failing case is needed.
## Stage 3 — Document and verify the completed adoption ## Stage 3 — Verify command lifecycle, inspection, and resume invariants
Status: Complete — 2026-09-16 Status: Not started
Completion: Narratio now uses released ConfigKit v0.3.0. The implementation Read: `internal/app/secrets_bootstrap.go`, `secrets_bootstrap_test.go`,
adds invocation-local pipeline discovery for isolated command tests and adjusts `config_commands_test.go`, `config_loader.go`, `extract_lifecycle_test.go`,
the integer party-version expectation to Narratio's unsupported-version `semantic_resume_test.go`; `internal/stage/semantic_contracts_delivery.go`,
diagnostic; it requires no operator configuration migration. The internal `semantic_contracts_delivery_test.go`, `prepare.go`, and their focused tests.
command-dependency inventory documents the new seam. No unreleased Narratio
release note exists, and the immutable v2.0.0 note was not changed.
Checks: 1. Extend existing command credential fixtures with the Notarius reference.
Exercise real pipeline loading/bootstrap with a controlled pipeline path:
conventional and explicit secret directories, environment precedence, and
an existing empty environment variable remaining an error rather than being
replaced from a file. Reuse established fixture helpers and avoid a second
exhaustive bootstrap suite.
2. Prove public config validation accepts the resolved field and rejects an
unavailable one before any adapter execution. Exercise show, sources, and
diff with a custom reference name and a synthetic canary: reference names and
ownership are visible, raw values are absent, and profile null removal works.
3. Cover prepared/resolved pipeline diagnostics with the Notarius canary using
the actual persistence path. Assert that manifests and Narratio-generated
configuration contain no credential bytes. Do not assert sanitation of
arbitrary files authored directly by the downstream fixture.
4. Extend extract semantic fingerprint and resume tests to cover credential
addition/removal, reference-name changes, and value rotation after valid load.
The semantic fingerprint and resume eligibility must remain unchanged.
Verify a previously successful extraction is reused without invoking Notarius;
retain normal selected-reference validation at command loading.
5. Keep effective digest/provenance distinct from semantic resume identity.
Do not change the runner's invalidation relation, fingerprint version, or
schema to satisfy these tests. No blanket cache key may include credentials.
- `GOWORK=off go mod tidy -diff` — pass. Acceptance:
- `GOWORK=off go mod verify` — pass.
- `GOWORK=off go test -count=1 ./...` — pass.
- `GOWORK=off go test -race -count=1 ./...` — pass.
- `GOWORK=off go vet ./...` — pass.
- `GOWORK=off go build ./...` — pass.
- `GOWORK=off go test ./internal/doccheck` — pass.
- `GOWORK=off go test ./internal/config -run '^TestExamplesLoadAndValidate$'` — pass.
- `git diff --check` — pass.
- `gofmt -l` on the changed Go files — no output.
- `scripts/build-release-assets.sh v0.0.0-test OUTPUT_DIR` — pass with
Linux amd64, Linux arm64, and macOS arm64 assets. The macOS result is
cross-build evidence only, not native runtime validation.
Purpose: Establish a clean, reproducible handoff for later release preparation. - `GOWORK=off go test ./internal/app ./internal/config ./internal/stage`
- `GOWORK=off go test -race ./internal/app ./internal/config ./internal/stage`
- Tests do not depend on real system configuration, installed Notarius, or a
provider. No secret value is printed even if a non-disclosure assertion fails.
- Credential changes alone do not reexecute a completed extraction or stale
dependent artifacts.
Read: `docs/development.md`, `docs/internal/configuration.md`, ## Stage 4 — Document the operator contract and validate the whole feature
`docs/internal/overview.md`, `docs/config.md`, `docs/release.md`,
`scripts/build-release-assets.sh`, and `scripts/check-release-candidate.sh`.
Implementation: Status: Not started
1. Update internal command-dependency documentation to include the new Read: `docs/config.md`, `docs/cli.md`, `docs/integrations/notarius.md`,
invocation-local discovery seam where the dependency inventory is described. `docs/internal/configuration.md`, `docs/internal/adapters.md`,
Update other current documentation only if a claim is actually affected. `docs/internal/stage-extract.md`, `docs/troubleshooting.md`,
The unchanged pipeline path policy and party identifier need no migration `examples/README.md`, `docs/release.md`, and the feature roadmap.
instructions. Do not rewrite historical version pins in completed audit records.
2. Record in this plan's completion notes the released dependency, diagnostic
adjustment, test-isolation repair, and absence of operator migration.
If a genuinely unreleased release note already exists, update it. There is
currently no such note: v2.0.0 is already tagged. Do not invent a release
version or modify v2.0.0; transfer this summary into the new note when a
separate release task chooses its version.
3. Run all checks below against the normal module files, with no overlays or
skipped tests. Review the final diff for scope, formatting, whitespace,
credentials, and accidental dependency changes. Record results and limitations.
4. Mark the implementation stages complete and update the feature roadmap's
status only after acceptance succeeds. Do not delete roadmap/audit records
or implement the adjacent Notarius roadmap.
Required checks: 1. Update canonical documentation with the field's type, absence/null behavior,
environment-name matching with Notarius/PromptKit, existing bootstrap
precedence, and the load-time requirement even when extraction is reusable.
State the redaction/downstream responsibility boundary and operational-only
resume treatment in their appropriate owners; link rather than duplicate.
Do not advertise new CLI flags or automatic provider-name discovery.
2. Add a maintained, secret-free example of the optional reference in an
appropriate existing Notarius example. Keep ordinary examples runnable without
real keys: if a fixture actively declares the reference, its example test must
supply and restore a synthetic environment value. Never add real secret files.
3. Preserve immutable release notes, including v2.0.0. Update an existing
unreleased note if one exists; otherwise record a concise release-ready summary
here for the separately authorized release task. Do not choose/tag a new
version merely to complete this stage.
4. Run the repository checks below against normal module files. Inspect the diff
for unintended dependency, platform, CLI, schema, and fingerprint changes.
Mark the roadmap and all stages complete only after acceptance succeeds.
Retain other roadmap and audit files.
Required validation:
```sh ```sh
GOWORK=off go mod tidy -diff GOWORK=off go mod tidy -diff
@@ -233,20 +228,13 @@ GOWORK=off go test ./internal/config -run '^TestExamplesLoadAndValidate$'
git diff --check git diff --check
``` ```
Also check formatting with `gofmt -l` on changed Go files. Use Check changed Go files with `gofmt -l`. Run
`scripts/build-release-assets.sh v0.0.0-test OUTPUT_DIR`, where `scripts/build-release-assets.sh v0.0.0-test OUTPUT_DIR` with an empty absolute
`OUTPUT_DIR` is an empty absolute directory created with `mktemp -d`, to temporary directory created by `mktemp -d`. All three maintained targets must
exercise the maintained Linux amd64/arm64 and macOS arm64 build path without build; report macOS cross-compilation as such, not native runtime validation.
choosing or publishing a release. Report macOS as cross-build evidence only. No live model calls, tag publication, or CI monitoring are required.
Acceptance: All checks pass without access to an installed Narratio configuration
or real credentials. The implementation no longer needs evaluation-only
workarounds. The later release task must create its note, commit/push its candidate,
and run the normal release-candidate checker; this plan does not claim those
publication steps are complete.
## Open Questions ## Open Questions
None. Dependency version, configuration behavior, discovery seam, test scope, None. The operator contract, optionality, delivery boundary, redaction scope,
and validation gates are decided. Release version selection is deliberately and resume policy are settled. Release version selection is a separate task.
outside this implementation's scope and does not block the work.

View File

@@ -1,6 +1,6 @@
# Typed Notarius Credential Forwarding # Typed Notarius Credential Forwarding
Status: proposed Status: Accepted — implementation pending
## Summary ## Summary
@@ -9,8 +9,12 @@ stage. Narratio resolves that reference through its existing ConfigKit secrets
bootstrap and supplies the credential to the Notarius subprocess under the bootstrap and supplies the credential to the Notarius subprocess under the
referenced environment-variable name. referenced environment-variable name.
This request defines only the operator-facing configuration and invocation The target is a bounded configuration-and-adapter feature that reuses ConfigKit
contract. It does not prescribe an internal implementation. typed secrets and Narratio's existing subprocess environment and redaction
support. No upstream change or general environment-management framework is
required. This roadmap owns the desired end state and policy; the
[implementation plan](implementation.md) owns execution order and concrete
implementation steps.
## Problem ## Problem
@@ -36,21 +40,11 @@ Add one optional typed secret reference at:
pipeline.notarius.llm_api_key pipeline.notarius.llm_api_key
``` ```
Example: Illustrative addition to an otherwise valid pipeline:
```yaml ```yaml
notarius: notarius:
enabled: true
config_path: /usr/local/etc/notarius/config.yml
pipeline_id: dnd-session
llm_api_key: OPENROUTER_API_KEY llm_api_key: OPENROUTER_API_KEY
outputs:
spells:
lane_id: spells
media_type: application/json
schema_id: notarius.dnd.spells
schema_version: v1
module_key: dnd/spells
``` ```
`llm_api_key` contains an environment-variable name, never a raw credential. `llm_api_key` contains an environment-variable name, never a raw credential.
@@ -68,6 +62,12 @@ Narratio credential references:
- a selected profile may replace or explicitly remove the field under ordinary - a selected profile may replace or explicitly remove the field under ordinary
ConfigKit overlay semantics. ConfigKit overlay semantics.
An explicit null has the same credential-delivery meaning as absence. Every
non-null reference surviving the selected profile must resolve, including during
inspection or when extract will not execute. Unselected or removed base
references are not independently resolved. Do not introduce stage-dependent
secret loading or a second environment lookup at execution time.
For a configured reference such as `OPENROUTER_API_KEY`, the Notarius child For a configured reference such as `OPENROUTER_API_KEY`, the Notarius child
process receives the resolved credential under exactly that environment name. process receives the resolved credential under exactly that environment name.
This lets the operator coordinate the Narratio field with the `api_key_env` This lets the operator coordinate the Narratio field with the `api_key_env`
@@ -78,6 +78,47 @@ No new CLI flag is required. The existing Notarius argument vector, pipeline
configuration path, working directory, reference bindings, receipt, and output configuration path, working directory, reference bindings, receipt, and output
contracts remain unchanged. contracts remain unchanged.
## Required Architecture and Changes
### Configuration and inspection
Add an optional `*configkit.Secret` to `NotariusConfig`, following the existing
typed credential pattern. ConfigKit owns reference syntax, effective-profile
resolution, and access to the resolved value; the application continues to own
the one-per-invocation secrets-directory bootstrap. The field defaults to absent.
Extend the relevant safe configuration projections, serialization, provenance,
and tests so this field is visible by its public reference name. Review both
effective inspection and generated/persisted configuration paths; protecting
`config show` alone is insufficient. Do not serialize an adapter invocation
request or a raw credential-bearing structure for diagnostics.
### Extract and adapter delivery
Carry the optional credential through extract's existing Notarius invocation
boundary. Keep transport-specific environment construction in the Notarius
adapter and expose the resolved value only at the trusted delivery boundary.
Reuse the subprocess runner's `EnvOverrides` for the selected name/value pair
and `SensitiveEnvNames` to register that same name for redaction. Register custom
names as well as familiar provider names; the built-in sensitive-name list is
not sufficient for this feature.
The child retains Narratio's existing minimal inherited environment plus the
explicit credential override. There is no broad process-environment inheritance,
provider-name remapping, or forwarding of unrelated bootstrapped secrets. An
absent reference adds neither an override nor a credential requirement.
### Ownership
Keep configuration semantics in `internal/config`, bootstrap in `internal/app`,
invocation orchestration in `internal/stage`, and environment/redaction delivery
in `internal/adapters/notarius` and the shared subprocess boundary. Reuse the
existing helpers rather than introducing another secret resolver or redactor.
The [architecture policy](../policy/architecture.md),
[configuration internals](../internal/configuration.md), and
[adapter internals](../internal/adapters.md) remain the canonical owners of the
implemented system's boundaries.
## Inspection and Diagnostic Contract ## Inspection and Diagnostic Contract
- `narratio config validate` resolves the configured secret reference and - `narratio config validate` resolves the configured secret reference and
@@ -92,6 +133,39 @@ contracts remain unchanged.
- Errors may identify the field or unresolved environment-variable name but - Errors may identify the field or unresolved environment-variable name but
must not include credential bytes. must not include credential bytes.
The selected credential must receive the existing streaming redaction protection
on captured stdout and stderr, including logs, captured receipt output, and
stderr-derived errors on nonzero exit, timeout, or cancellation. Test the delivery
of the dynamically selected sensitive name; do not create a provider-specific
redaction exception or a second log-sanitization mechanism.
### Downstream responsibility boundary
Narratio must not place credential values in its authored configuration,
arguments, manifests, or metadata, and must redact the selected value from
subprocess streams it captures. Notarius remains responsible for keeping
credentials out of files it writes directly, including bundle artifacts and
diagnostics. Captured-stream redaction is not a filesystem sandbox and does not
guarantee sanitization of arbitrary downstream files or transformed/encoded
secret values. This feature does not add recursive scanning or rewriting of
Notarius output bundles. Such rewriting could corrupt valid artifacts and would
not establish a general non-disclosure guarantee.
## Resume and Configuration Identity
Authentication is operational configuration, not extraction content. Neither
the credential value nor its reference name becomes part of extract's semantic
resume fingerprint. Credential rotation, renaming, addition, or removal alone
must not invalidate a successfully recorded extraction or its dependents.
The effective configuration projection may include the public reference name,
so changing that name can appear in `config diff` and change the effective
configuration digest. Rotating only the resolved value must not change that
digest. Preserve the existing distinction between configuration provenance and
stage-owned resume evidence; do not add a blanket digest-based invalidation.
Normal configuration loading still requires any selected reference to resolve
even when the prior extraction can be reused.
## Compatibility ## Compatibility
The field is optional and defaults to absent. Existing configurations retain The field is optional and defaults to absent. Existing configurations retain
@@ -120,8 +194,16 @@ that agrees with the Notarius PromptKit profile they deploy.
explicit-null, source-reporting, diff, and secret-resolution semantics. explicit-null, source-reporting, diff, and secret-resolution semantics.
- Effective inspection exposes at most the safe reference name and never the - Effective inspection exposes at most the safe reference name and never the
resolved value. resolved value.
- No raw credential appears in command arguments, generated configuration, - No raw credential appears in Narratio-authored command arguments, generated
manifests, receipts, persisted diagnostics, error messages, or test output. configuration, manifests, metadata, or test output. Captured receipt/log streams
and errors redact the selected value within the responsibility boundary above.
- A custom environment-variable name not present in the built-in sensitive-name
list receives the same redaction protection as a known provider name, on both
successful and failing subprocess execution.
- Credential rotation or reference-name changes do not alter extract's semantic
fingerprint or invalidate a reusable extraction. Inspection reflects name
changes without revealing values; value-only rotation leaves the safe digest
unchanged.
- The existing Notarius CLI arguments and result-validation contract remain - The existing Notarius CLI arguments and result-validation contract remain
unchanged. unchanged.
- Focused tests use synthetic credentials and a controlled Notarius substitute; - Focused tests use synthetic credentials and a controlled Notarius substitute;
@@ -130,10 +212,27 @@ that agrees with the Notarius PromptKit profile they deploy.
documentation are updated only where their existing canonical ownership documentation are updated only where their existing canonical ownership
requires it when the feature is implemented. requires it when the feature is implemented.
Use focused behavioral tests at the configuration, extract/adapter delivery,
and captured-output boundaries. Retain shared subprocess tests for its generic
streaming guarantees rather than duplicating their full matrix. Verify the
assembled invocation with a controlled executable and synthetic credentials,
including proof that an unrelated credential is not forwarded. Follow the
[testing policy](../policy/testing.md) and the normal
[development validation](../development.md#validation); no live-provider
authentication is an acceptance gate.
## Target End State
An operator supplies a named API-key file through the existing secrets directory
or sets that environment variable, declares `notarius.llm_api_key`, and configures
the matching `api_key_env` in Notarius's selected PromptKit profile. Narratio
validates the reference, delivers only the selected credential to Notarius, and
keeps its own configuration and diagnostics credential-safe. Existing pipelines
need no migration, and changing authentication does not rerun completed analysis
work. No new CLI, dependency upgrade, or upstream feature is required.
## Non-goals ## Non-goals
- Defining how Narratio implements credential resolution or subprocess
construction internally.
- Adding an arbitrary environment-variable map to the Notarius stage. - Adding an arbitrary environment-variable map to the Notarius stage.
- Forwarding every variable loaded from the secrets directory. - Forwarding every variable loaded from the secrets directory.
- Inferring provider, backend, profile, model, or credential names from - Inferring provider, backend, profile, model, or credential names from
@@ -146,6 +245,9 @@ that agrees with the Notarius PromptKit profile they deploy.
- Validating a credential with its provider during configuration loading. - Validating a credential with its provider during configuration loading.
- Adding credentials to command-line arguments, ordinary YAML values, or - Adding credentials to command-line arguments, ordinary YAML values, or
generated invocation files. generated invocation files.
- Scanning or sanitizing arbitrary files created directly by downstream tools.
- Changing resume semantics, pipeline path bases, artifact schemas, or the
existing minimal inherited subprocess environment.
## Consumer Dependency ## Consumer Dependency