Skip to content

[ConfigManager] Check a Node's sei.toml 3/3 - #4045

Draft
bdchatham wants to merge 2 commits into
plt-775-install-decodefrom
plt-775-install-check
Draft

[ConfigManager] Check a Node's sei.toml 3/3#4045
bdchatham wants to merge 2 commits into
plt-775-install-decodefrom
plt-775-install-check

Conversation

@bdchatham

@bdchatham bdchatham commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

A command that answers what a node's sei.toml reaches, without starting the node. 705 lines.
Last of three, on top of the two deliveries.

seid sei-config check

It reports which declared keys the file supplies, which names in it reach nothing, and which
written values a delivery would refuse.

Why this sits on top of the deliveries rather than beside them

Predicting a refusal has to come from the same code that would refuse the value at boot. Reading
the file and forming a second opinion gives an operator an answer that can disagree with the one
that matters, which is worse than no answer.

A file it cannot read is a problem, not an absence

A node with no sei.toml has nothing that could be wrong, and that stays quiet. A file that will
not parse, records a schema version this binary does not know, or names no node kind is the case
this command exists for, and it now exits non-zero on all three. It previously reported that the
node had no file at all, which is both wrong and the answer least likely to make an operator look
twice.

Its own command group

sei-config rather than a subcommand of the existing configuration command, which reads and
writes the files this one is about rather than the file that replaces them.

What review changed

It failed on a correct file. Run the way a runbook runs it, the command exited non-zero and
named a key the file does not contain:

$ seid sei-config check --home /var/lib/sei
home: sei.toml writes this and no section declares it, so it has no effect
Error: 1 problem(s); a boot would apply what it could and report the rest

Every unmatched flag reached the resolution under its own name and was counted with the file's own
undeclared keys. --home is on every real invocation, so the verdict was red on every node. It now
reports only the file's keys. This matters more here than anywhere else in the series: a boot may
not refuse a file, which makes this command the thing that catches a mistake before a restart, and
a pre-flight that fails every time is one nobody runs after the second week.

It wrote the files it was asked about. The root command's hook runs the configuration handler,
which generates config.toml and app.toml when they are absent, so asking a question about one
file created two others. The same hook copies configuration values into flags and marks them
changed, which is exactly the state that makes a flag indistinguishable from a key an operator's
app.toml holds, and this command reports on what was typed. The command group now carries a hook
of its own, which stops both.

It was silent on the question with the largest consequence. Two files record what kind of node
this is, under different names, and nothing keeps them in step. A node whose sei.toml says
validator while its own file says full resolves a validator's answers and serves queries, and every
report about it reads correctly. The boot reports that at its loudest level and this command did not
ask. A node with no configuration file of its own has nothing to disagree with, and that case is now
answered in the one function both callers pass through.

It said nothing about whether a boot would read the file at all. Until the gate is switched a
boot reads none of it, so a passing check read as "in use and correct" on every node, which invites
trusting a file nothing reads. It now says so, and it names a gate value this binary would refuse
outright.

None of this was visible to the tests, because they executed the command with no parent: no hook
ran, no flag was marked changed, and no file was generated. The new tests run it through the real
root command with the wiring the binary uses.

Not rehearsed. The install into the source a node builds, because that source does not exist
until a boot builds it, so a key can be refused there for a reason nothing here can see. That is
stated in the command's own documentation, along with the fact that this reads the environment of
whoever runs it rather than the node's.

Notes for review

Formatters, go vet and golangci-lint clean; -count=2 -shuffle clean.

Mutation-verified: collapsing an unreadable file back to an absent one fails all three of the
cases an operator actually hits.

@codecov

codecov Bot commented Aug 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.57143% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 60.28%. Comparing base (ae60b14) to head (0dc6b00).

Files with missing lines Patch % Lines
cmd/seid/cmd/configmanager/check.go 88.42% 6 Missing and 5 partials ⚠️
cmd/seid/cmd/configmanager/install.go 50.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@                   Coverage Diff                   @@
##           plt-775-install-decode    #4045   +/-   ##
=======================================================
  Coverage                   60.28%   60.28%           
=======================================================
  Files                        2058     2058           
  Lines                      177299   177323   +24     
=======================================================
+ Hits                       106887   106902   +15     
- Misses                      60588    60593    +5     
- Partials                     9824     9828    +4     
Flag Coverage Δ
sei-chain-pr 61.14% <88.57%> (-7.90%) ⬇️
sei-db 69.80% <ø> (ø)

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

Files with missing lines Coverage Δ
cmd/seid/cmd/root.go 69.78% <100.00%> (+0.89%) ⬆️
cmd/seid/cmd/configmanager/install.go 79.41% <50.00%> (+1.47%) ⬆️
cmd/seid/cmd/configmanager/check.go 88.42% <88.42%> (ø)

... and 3 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.

@github-actions

github-actions Bot commented Aug 27, 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 27, 2026, 10:14 PM

@bdchatham

Copy link
Copy Markdown
Contributor Author

@seidroid review

bdchatham and others added 2 commits August 27, 2026 14:59
An operator running this before a restart is asking whether their file is right. It
answers without starting a node: which declared keys the file supplies, which names
reach nothing, and which written values a delivery would refuse.

Predicting the refusals is why this sits on top of the deliveries rather than beside
them. The answer has to come from the same code that would refuse the value at boot, or
it is a second opinion that can disagree with the one that matters.

A file that cannot be read is reported as a problem of a file that was found, so the
command exits non-zero. It previously reported that the node had no file at all, which
is both wrong and the answer least likely to make an operator look. Its own group of
commands rather than a subcommand of the existing one, which reads and writes the files
this is about rather than the file that replaces them.

Verified by mutation: collapsing an unreadable file back to an absent one fails all
three of the cases an operator hits.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…tor runs it on

The command failed on a correct file. Run the way a runbook runs it, with --home,
it exited non-zero and named `home` as a key sei.toml writes:

  $ seid sei-config check --home /var/lib/sei
  home: sei.toml writes this and no section declares it, so it has no effect
  Error: 1 problem(s); a boot would apply what it could and report the rest

The file contains no such key. Every unmatched flag reached the resolution under
its own name and was counted with the file's own undeclared keys. It now reports
only the file's. A pre-flight that fails every time carries nothing, and this
command is the whole compensating control for a boot that may not refuse a file.

It also wrote the files it was asked about. The root command's hook runs the
configuration handler, which generates config.toml and app.toml when they are
absent, so asking a question about one file created two others. The same hook
copies configuration values into flags and marks them changed, which is exactly
the state that makes a flag indistinguishable from a key an operator's app.toml
holds, and this command reports on what was typed. The command group now carries a
hook of its own, which stops both.

It was silent on the question with the largest consequence. Two files record what
kind of node this is, under different names, and nothing keeps them in step. A node
whose sei.toml says validator while its own file says full resolves a validator's
answers and serves queries, and every report about it reads correctly. The boot
reports that at its loudest level and the check did not ask. A node that has no
configuration file of its own has nothing to disagree with, and that case is now
answered in the one function both callers pass through rather than at each of them.

It said nothing about whether a boot would read the file at all. Until the gate is
switched a boot reads none of it, so a passing check read as "in use and correct"
on every node, which invites trusting a file nothing reads. It now says so, and it
names a gate value this binary would refuse outright.

The refusal messages spliced two sentences into one, and the tests could not see any
of this: they executed the command with no parent, so no hook ran, no flag was
marked changed, and no file was generated. The new tests run it through the real
root command with the wiring the binary uses.

Not rehearsed: the install into the source a node builds, because that source does
not exist until a boot builds it. A key can be refused there for a reason nothing
here can see. Stated in the command's own documentation, along with the fact that
this reads the environment of whoever runs it rather than the node's.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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