Documentation / Internal package reference
Audience: Provider and composition authors.
Status: implemented supplied-input preparation, including an identity-neutral data seam.
Package: github.com/frost-leo/fathomry/internal/resource.
Start with the resource interface. This is the contract for supplied settings, not an application configuration-file format.
Input.Format must equal the nonzero Schema.Format; there is no fallback to a
different version's defaults. This is the selected Provider's configuration schema
version, not the YAML specification version, module version, or source revision.
The supported DTO is a nonrecursive Go struct with explicit json:"field" tags.
Fields may contain strings, booleans, finite numeric values, pointers, nested plain
structs, string-keyed maps, and slices. Tag options, anonymous/private fields,
arrays, interfaces, byte slices, custom serialization, and runtime handles are
rejected. Default strings and map keys must be valid UTF-8; invalid bytes are
rejected instead of being silently normalized by JSON encoding. Named scalar types such as time.Duration can use their numeric
representation, with units defined by the Provider. SDK options, callbacks, dynamic
credential-refresh handles, and time.Time are not generic copyable settings:
derive or inject those at their actual ownership boundary.
Each explicit layer supplies one YAML mapping; JSON syntax is accepted as YAML. This is a restricted YAML input contract, not a promise to accept all YAML features:
The explicit internal JSONVariables(content) constructor is the sole exception:
it decodes generated environment data as strict JSON, preserving Unicode strings,
decoded map names and exact number spellings without YAML normalization. Ordinary
Layer literals, including Variables, retain the existing YAML behavior. Both
paths use the same structural checks, merge, copy and validation engine below.
Malformed UTF-8/Unicode escapes, duplicate decoded names and trailing values reject.
| Aspect | Contract |
|---|---|
| Precedence | Typed defaults < base < environment-specific < local < variables; independent of slice order |
| Layer identity | At most one document per non-default layer kind; the framework must resolve same-kind input selection explicitly; no loader or second merge policy is implemented |
| Structural validation | Every layer must have exact, known field names and compatible value types, even if a higher layer would override it |
| Semantic validation | Provider validation runs on the final effective settings, on a separate copy whose mutations are discarded |
| Objects | Recursive merge retains siblings; an empty object preserves inherited children |
| Maps and lists | String-keyed maps merge recursively without case folding; slices replace as a whole, never concatenate |
| Absence | Inherits existing settings; newly introduced objects use Go zero values for otherwise absent fields |
| Empty values | Empty string, false, numeric zero, and empty slice explicitly override |
| Null | Clears pointers, maps, and slices; rejected for non-nullable fields; not a deletion operator |
| Numbers | JSON numeric syntax, with target-type range checks; integer fields reject fractions and exponent notation without a float64 intermediate |
| Rejected syntax | Duplicate keys, non-string keys, anchors, aliases, merge keys, explicit/custom tags (including bare ! on keys, values and collections), implicit timestamps, multiple documents |
| Bounds | Each input document and resolved JSON at most 1 MiB; normalized input nesting at most 64 levels |
| Reload | No implicit reload, environment reread, file watcher, credential rotation, or live settings mutation |
The pinned YAML parser is go.yaml.in/yaml/v3 v3.0.5. The restricted contract and
tests, not upstream defaults or a candidate SDK inventory, determine Fathomry
behavior. A parser change requires its own compatibility evidence.
Bare ! loses TaggedStyle in this parser, so preparation also checks the original
character at each node's retained start position. This is not a character blacklist:
quoted/block scalars and comments may contain literal !. Source indexing follows
the parser's character coordinates, line breaks and BOM-selected UTF-8/UTF-16
decoding; it does not reinterpret or normalize scalar text.
Upstream version policy,
pinned release.
Viper is not a managed resource or forbidden dependency. The
internal Viper v1 integration provides isolated
native reads and a separate raw-input preparation proof, not a public framework
loader. The resource
foundation does not pass Viper objects to Providers. A Viper v1.21.0 comparison
confirmed that configuration lookup can fall back from explicit null to a default,
case-fold mapping keys, and modify a map supplied to MergeConfigMap; its
MergeConfig does preserve nested siblings in the tested case. These behaviors
are not interchangeable with the contract above. Taking AllSettings() cannot
recover input distinctions already lost. Loading must preserve the raw authorized
layer distinctions and invoke the existing preparation/merge once, not silently
apply a competing precedence or null policy. Internal callers can prepare raw
local/Nacos observations through PrepareData. The independent
public preparation contract does not
wrap this engine. Framework configuration
consumes that public contract and public source ownership, not this private seam.
Viper documentation,
merge/lookup implementation.
PrepareData/PreparedData.ValueCopy share this engine without a named resource
Identity. They do not create an Assembly or a token accepted by resource.Select.
Named Prepare retains its identity admission. CheckDocumentFormat uses the same
strict parser to check each original external header without re-emitting bytes.
The neutral-seam tests cover copying,
guards, unchanged named admission and format rejection.
Preparation freezes resolved bytes, not an alias to the caller's maps or slices.
Every factory receives a freshly decoded settings copy, including when the same
selection is assembled concurrently in different scopes. Mutations by one factory
cannot change another instance's settings. The generic type boundary cannot be
reinterpreted as another configuration or capability type through an ordinary Go
conversion. Unsafe code is outside this boundary.
Private, defined generic identity markers seal both Prepared[T] and Selection[C],
including anonymous struct types differing only in JSON tags. Such cross-type
value/pointer casts fail compilation; aliases of the identical type remain valid.
Converting plain settings before a new Prepare remains ordinary Go use and must
pass that preparation's own validation. No type registry or shared settings copy
is introduced.
Callers must not mutate input bytes/defaults concurrently with preparation. Validation/factory closures and injected dependencies have their own explicit concurrency and ownership contracts; freezing settings does not clone closures or stop an SDK from secretly sharing resources. Those remain integration obligations.
Description.Revision is an opaque, random 128-bit preparation identity. Every
successful preparation gets a new revision, even for equivalent settings; reusing
that prepared value preserves it. It identifies the frozen effective settings,
including defaults, without hashing low-entropy secrets. It does not establish
content equality, authenticity, deployment correspondence, or compatibility.
Token refresh is not implemented, and mutable tokens never belong in frozen Run
input.
Description.Provenance records the declared layers and schema fields they
supplied, in precedence order. It is input provenance, not a reconstructed winning
origin for every dynamic map entry. Map/list contents, arbitrary map keys, raw
documents, file paths, and environment variable names are excluded. Returned
descriptions are independent copies. Prepared formatting is redacted and its
JSON serialization is refused.
Preparation tests, type/negative-compilation controls and native YAML checks exercise the rules. Boundary repair: Issue #17. See testing for parser fuzzing.