Add safe historical configuration explanations

This commit is contained in:
2026-09-06 22:14:54 +00:00
parent d4ec8607d1
commit 6ef240b1a0
17 changed files with 1032 additions and 141 deletions

View File

@@ -141,10 +141,60 @@ translated to a fixed safe error. There is no unredacted rendering option and
ConfigKit performs no implicit logging.
`Result.Config` is the explicit trusted boundary and contains ordinary typed values,
including secrets. Metadata, histories, diagnostics, and load errors contain no raw
configuration values. Applications must still keep source labels, resolution
reasons, and diagnostic messages value-free because ConfigKit cannot infer and
sanitize secrets embedded in arbitrary application prose.
including secrets. Metadata records, diagnostics, and load errors contain no
configuration values. Explanation snapshots deliberately expose non-secret
configuration history after applying the redaction contract below. Applications
must still keep source labels, resolution reasons, and diagnostic messages value-free
because ConfigKit cannot infer and sanitize secrets embedded in arbitrary application
prose.
### Historical-value explanation
`Result.Explain(path)` returns a detached `Explanation` for any known effective or
historical path. Its effective value and each chronological before/after value are
`ValueSnapshot` records containing deterministic YAML text rather than mutable Go or
parser values. `Present=false` with empty YAML means absence or deletion; explicit
null is present and renders as `null`. The history starts with the canonical typed
application-default or Go-zero baseline, then includes only relevant base sources in
traversal order, the selected profile, supplied CLI records, and audited net
resolution operations. Explicit equal writes remain visible.
Base, profile, and CLI history preserves the policy-validated input representation,
including a superseded value that could not have decoded into the final Go field.
Baseline, effective, and resolution values use canonical typed projection. Mapping
queries reconstruct each source or override contribution, while sequences remain
atomic: a descendant history identifies its presence before and after the containing
sequence replacement without implying element-wise merging. Removed paths remain
explainable; unknown and malformed paths return false.
Every snapshot applies the same historical tag, predicate, and recorded-dependency
sensitivity closure as effective rendering before YAML is exposed. A redacted scalar
is the quoted marker `'[REDACTED]'`, and a container snapshot has `Redacted=true` if
it contains a masked descendant. Typed `Secret` snapshots contain only the configured
environment name. `SecretReference` reports that name only for a successfully
resolved effective reference, and is omitted when its path is classified. Credential
values are never retained in explanation history.
`Result.WriteExplanation(output, path)` renders the safe structured result with the
canonical JSON-pointer path, canonical-versus-input representation labels, effective
source, composite locations, chronological values, resolution reasons and
dependencies, and successful secret-reference lookup attribution. Control characters
in paths, source identifiers and labels, reasons, dependencies, and reference names
are escaped. An unknown path returns a fixed safe error before writing anything;
writer errors and short writes are returned and may leave partial output. There is no
unredacted mode.
Retained history consists of immutable source applications and audited subtree
deltas, rather than a full tree copy per field. Shared nodes count once toward a
4,000,000-node retained-history limit; exceeding it fails loading with
`explain.limit_exceeded`. Replaying one requested path is performed on demand, and
the returned YAML strings and nested records cannot mutate later explanations.
Application commands should render the separate `SecretsReport` immediately after
an explicit `LoadSecretsDir` bootstrap, then use `WriteDiagnostics`,
`WriteEffective`, or `WriteExplanation` on the later load result as appropriate.
ConfigKit does not infer bootstrap outcomes from a secret reference and does not
bundle these independent reports into a command framework.
## Typed secret references