Skip to content

[ConfigManager] Register Sections 4/4 - #3977

Draft
bdchatham wants to merge 2 commits into
mainfrom
plt-775-sections-4
Draft

[ConfigManager] Register Sections 4/4#3977
bdchatham wants to merge 2 commits into
mainfrom
plt-775-sections-4

Conversation

@bdchatham

@bdchatham bdchatham commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Registers the five configuration sections whose keys belong to the Cosmos server, and
adds the two registry capabilities they need.

  api            8 keys
  base          14 keys
  grpc          11 keys
  state-sync     3 keys
  telemetry      7 keys

These sections have no owning package here. Their structs and their readers live in
sei-cosmos, which this repository vendors rather than authors, so there is nowhere
upstream to put a registration this registry would see. Four of the five register the
upstream struct directly, because its mapstructure tags already name the keys the reader
resolves.

A section can declare keys at the root of the file

The node-wide settings are written at the top of app.toml and read as pruning and
halt-height, with no segment in front. A section that carried its name into every key
would rename all fourteen, and an operator's existing file would reach none of them. So
a section now has a name it is looked up and reported under, and a prefix its keys
carry, and for a root section the prefix is empty.

Both walks build a key through one function. Reverting either one on its own fails a
test: the type walk through the key comparison, and the value walk through the existing
check that a rendered default states one value per declared key.

Two keys can now collide where two prefixes never could

A key two sections both declare has one default rendered over the other, and which one
depends on the order the sections are walked. And a root key that is also a section's
name cannot be written at all, because a file holding both a value for that name and a
table under it is not valid TOML, so one of the two is unreachable and nothing says
which. Both are refused, in either registration order.

A section can say the environment cannot supply a key

The metric label set is a list of name and value rows, and its reader asserts that exact
shape rather than casting what it finds, so no single string satisfies it. That
assertion is the first statement of the whole server configuration, so a resolved
variable installs a value that stops the node. Leaving the channel out means the file's
value applies and the node runs. The reason is required rather than optional, because an
operator whose variable is ignored has to be told why, and a refusal with no reason is
itself refused.

The one schema, and what holds it

The metric section needs a schema, for one field's shape rather than for a spelling: its
label set is declared as untyped rows to match what the reader takes. A test holds every
other field to the upstream field's name, tag and type, and holds the count of differing
types at exactly one. A second divergence fails, and if the upstream type ever comes to
match, the schema is left with nothing to justify it and that fails too.

What I broke on purpose

Eight mutations, each caught: porting the key-joining into only one of the two walks
(both directions), rendering a root section's defaults under its lookup name, dropping
the collision refusal, dropping the environment skip, allowing a refusal with no reason,
resolving the label set as the shape its reader refuses, and giving the node-wide keys a
section prefix.

Two things to know

One declared value is not what a running node uses. The pruning strategy is declared
as keeping everything (DefaultConfig()), while the command line registers a flag of
the same name defaulting to the standard strategy, and a bound flag is a source of its
own below the file. A node started with no pruning key written prunes on the standard
schedule. Whoever resolves for a running node has to supply the flag values to get the
answer that node uses. Nothing depends on this yet; I can file it if it should be
tracked.

Three defaults vary by kind of node, and every section here answers through the mode
rules the binary already applies rather than restating them. A full node and an archive node
exist to serve queries, so both interfaces that serve them are on; a validator is meant to
expose as little as it can, so both are off, and the upstream default would have declared
gRPC open on every validator. Block retention follows the same rules. The test writes the
three values out by kind of node, so a change to the rules fails it and gets looked at.

The recorded configuration surface does not move, because nothing consumes the registry
on a boot path yet.

The manifest that states which sections a binary declares is deliberately not here. It
has to name every section, so it can only land once these four registration changes are
all on main.

Five sections whose keys belong to the Cosmos server, and the two registry
capabilities they need.

  api            8 keys
  base          14 keys
  grpc          11 keys
  state-sync     3 keys
  telemetry      7 keys

These sections have no owning package here. Their structs and their readers live
in sei-cosmos, which this repository vendors rather than authors, so there is
nowhere upstream to put a registration this registry would see. Four of the five
register the upstream struct directly, because its mapstructure tags already name
the keys the reader resolves.

A section can now declare keys at the root of the file. The node-wide settings are
written at the top of app.toml and read as pruning and halt-height, with no
segment in front, so a section carrying a name into every key would rename all
fourteen and an operator's existing file would reach none of them. A section
therefore has a name it is looked up by and a prefix its keys carry, and for a
root section the prefix is empty. Both walks build a key through one function, so
a root key gains no separator on either side; reverting either one on its own
fails a test, the value walk through the check that a rendered default states one
value per declared key.

Two keys can now collide where two prefixes never could. A key two sections both
declare has one default rendered over the other, and which one depends on the
order the sections are walked. And a root key that is also a section's name cannot
be written at all, because a file holding both a value for that name and a table
under it is not valid TOML, so one of the two is unreachable and nothing says
which. Both are refused, in either registration order.

A section can now say that an environment variable cannot supply one of its keys.
The metric label set is a list of name and value rows and its reader asserts that
exact shape rather than casting what it finds, so no single string satisfies it,
and the assertion is the first statement of the whole server configuration. A
resolved variable would install a value that stops the node; leaving the channel
out means the file's value applies and the node runs. The reason is required
rather than optional, because an operator whose variable is ignored has to be told
why, and a refusal with no reason is itself refused.

The metric section is the one here that needs a schema, and for one field's shape
rather than for a spelling. Its label set is declared as untyped rows to match
what the reader takes. A test holds every other field to the upstream field's name,
tag and type, and holds the count of differing types at one, so a second
divergence is a failure and a converged upstream type leaves the schema with
nothing to justify it.

Nothing here varies a default by mode. seid init writes the two interface toggles
and the block retention per mode, so a node it provisioned carries those as
written values, and these are what a node with nothing written runs.

One declared value is not what a running node uses, and it is worth knowing which.
The pruning strategy is declared as keeping everything, while the command line
registers a flag of the same name defaulting to the standard strategy, and a bound
flag is a source of its own below the file. A node started with no pruning key
written prunes on the standard schedule. Whoever resolves for a running node has
to supply the flag values to get the answer that node uses.

The recorded configuration surface does not move, because nothing consumes the
registry on a boot path yet.
@github-actions

github-actions Bot commented Aug 20, 2026

Copy link
Copy Markdown

The latest Buf updates on your PR. Results from workflow Buf / buf (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed✅ passed✅ passed✅ passedAug 21, 2026, 3:16 PM

@codecov

codecov Bot commented Aug 20, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 57.62%. Comparing base (01f3c6a) to head (d87787d).
⚠️ Report is 4 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #3977      +/-   ##
==========================================
- Coverage   58.69%   57.62%   -1.07%     
==========================================
  Files        2326     2230      -96     
  Lines      199122   187927   -11195     
==========================================
- Hits       116877   108301    -8576     
+ Misses      71477    69750    -1727     
+ Partials    10768     9876     -892     
Flag Coverage Δ
sei-chain-pr 100.00% <100.00%> (?)
sei-db 69.80% <ø> (ø)
sei-db-state-db ?

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
config/cosmosbase/cosmosbase.go 100.00% <100.00%> (ø)
config/registry/environment.go 100.00% <100.00%> (ø)
config/registry/registry.go 100.00% <100.00%> (ø)
config/registry/resolve.go 100.00% <100.00%> (ø)

... and 98 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Three of these settings mean something different depending on what kind of node
asks, and all five sections answered the same for every one. The binary already
states the rules, in what it applies when it writes a file, and every section
here now answers through them.

Each of the three matters in a different direction. A full node and an archive
node exist to serve queries, and both interfaces that serve them were declared
closed. A validator is meant to expose as little as it can, and gRPC was
declared open on every one of them, which is the opposite of what the rule beside
it says it is for. And the number of blocks a node retains was declared as
keeping everything for a full node, where the rule prunes at a hundred thousand.

The rules are read rather than restated, so one added later moves these sections
with nothing here changing, and the test writes the three values out by kind of
node so a change to the rules fails and gets looked at. Resolving every mode as a
validator, opening gRPC on a validator, and changing the retention each fail it.

The two sections no rule touches answer through the same function, so there is
one place a mode is applied rather than a decision per section about whether to
apply it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant