Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
1da0d72
config: the first four sections enter the registry
bdchatham Aug 20, 2026
467ceec
config: four app sections enter the registry
bdchatham Aug 20, 2026
25fa5f9
config: the EVM sections enter the registry
bdchatham Aug 20, 2026
59a387a
config: the upstream server sections enter the registry
bdchatham Aug 20, 2026
f7dc6bb
config: say why a field excluded from configuration declares no key
bdchatham Aug 20, 2026
8466082
config: answer the store section per mode, and measure the divergences
bdchatham Aug 20, 2026
0e1d870
config: state each comment's own subject
bdchatham Aug 21, 2026
12fe6c0
config: name the untagged field, and say each thing once
bdchatham Aug 21, 2026
d87787d
config: answer the upstream sections per kind of node
bdchatham Aug 21, 2026
dc4a458
config: hand out values nothing else holds, and answer the EVM interf…
bdchatham Aug 21, 2026
db12097
config: refuse a mode nothing declares, and report a variable that di…
bdchatham Aug 21, 2026
eb53ea6
config: the contract says what the registry now does
bdchatham Aug 22, 2026
02878d0
config: drop a guard with no instance, and name the gap it leaves
bdchatham Aug 23, 2026
9b2d183
config: a declared value is what seid init writes, and the record mea…
bdchatham Aug 23, 2026
3f9d618
config: name which generator a declared value follows
bdchatham Aug 23, 2026
e244418
config: the archive retention departs on purpose, and says so
bdchatham Aug 24, 2026
7fc9154
config: name what these declared values are
bdchatham Aug 24, 2026
517f240
config: the wasm query limit is one statement, and it is the one a no…
bdchatham Aug 24, 2026
dd25b13
Merge branch 'plt-775-sections-1' into plt-775-boot
bdchatham Aug 24, 2026
13d1aab
Merge branch 'plt-775-sections-2' into plt-775-boot
bdchatham Aug 24, 2026
9040975
Merge branch 'plt-775-sections-3' into plt-775-boot
bdchatham Aug 24, 2026
534a847
Merge branch 'plt-775-sections-4' into plt-775-boot
bdchatham Aug 24, 2026
d5e4643
feat(config): install what sei.toml supplies at boot
bdchatham Aug 24, 2026
905df20
refactor(config): name the conditions a field tag has to pass
bdchatham Aug 24, 2026
1eab9bc
Merge branch 'plt-775-sections-1' into plt-775-tm-sections-1
bdchatham Aug 24, 2026
a8fe04f
feat(config): declare the peer-to-peer and remote procedure call sect…
bdchatham Aug 24, 2026
a1f9d49
feat(config): declare the consensus and mempool sections
bdchatham Aug 24, 2026
634e07f
feat(config): declare the remaining node configuration sections
bdchatham Aug 24, 2026
259e8ca
feat(config): declare the node configuration file's root keys
bdchatham Aug 24, 2026
261a308
fix(config): no section declares the node's root directory
bdchatham Aug 24, 2026
c216800
Merge branch 'plt-775-node-sections-1' into plt-775-node-sections-2
bdchatham Aug 24, 2026
44f76d2
fix(config): the consensus and mempool sections leave the root direct…
bdchatham Aug 24, 2026
ecc927b
fix(config): the signing key section leaves the root directory out
bdchatham Aug 24, 2026
bbd07d4
Merge branch 'plt-775-node-sections-3' into plt-775-node-sections-4
bdchatham Aug 24, 2026
cbc2db3
Merge branch 'plt-775-boot' into plt-775-node-install
bdchatham Aug 24, 2026
973f12f
feat(config): deliver the node configuration file's written values
bdchatham Aug 24, 2026
54a8195
feat(config): close four gaps around the node configuration delivery
bdchatham Aug 24, 2026
2b3919f
fix(config): repair what a peer review measured, and pin each repair
bdchatham Aug 24, 2026
0faead8
style(config): state the mode comparison without a negated conjunction
bdchatham Aug 24, 2026
798f11a
test(config): name the rule both deliveries depend on
bdchatham Aug 25, 2026
42e6711
refactor(config): name the other divergence record for what it holds
bdchatham Aug 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions admin/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ const (
DefaultAddress = "127.0.0.1:9095"
)

// The keys this package's reader resolves.
const (
flagAdminEnabled = "admin_server.admin_enabled"
flagAdminAddress = "admin_server.admin_address"
)

// Config defines configuration for the admin gRPC server.
type Config struct {
// Enabled controls whether the admin gRPC server starts.
Expand All @@ -29,10 +35,10 @@ var DefaultConfig = Config{
// ReadConfig reads admin config from app options (Viper-backed).
func ReadConfig(opts servertypes.AppOptions) (Config, error) {
cfg := DefaultConfig
if v := opts.Get("admin_server.admin_enabled"); v != nil {
if v := opts.Get(flagAdminEnabled); v != nil {
cfg.Enabled = cast.ToBool(v)
}
if v := opts.Get("admin_server.admin_address"); v != nil {
if v := opts.Get(flagAdminAddress); v != nil {
if s := cast.ToString(v); s != "" {
cfg.Address = s
}
Expand Down
19 changes: 19 additions & 0 deletions admin/register.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package admin

import (
"github.com/sei-protocol/sei-chain/config/registry"
)

// SectionName is this section's name in the configuration key space.
const SectionName = "admin_server"

// Registration puts this section in the configuration registry.
//
// The keys derive from the mapstructure tags, and the reader resolves the same strings through the
// constants beside it, so a rename moves one occurrence and the test holds the two together.
func init() {
registry.RegisterSection(SectionName, &Config{}, defaults)
}

// defaults is what the seid init command writes for a node of this kind.
func defaults(registry.Mode) any { return DefaultConfig }
44 changes: 44 additions & 0 deletions admin/register_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package admin

import (
"reflect"
"sort"
"testing"

"github.com/sei-protocol/sei-chain/config/registry"
)

// TestTheDeclaredKeysAreTheKeysThisReaderResolves holds the declaration against the reader.
//
// The tags derive the keys and the constants below are what the reader passes to Get, so this compares
// two statements that are edited for different reasons rather than one written out twice.
func TestTheDeclaredKeysAreTheKeysThisReaderResolves(t *testing.T) {
for _, defect := range registry.Defects() {
if defect.Section == SectionName {
t.Fatalf("%s was refused: %v", SectionName, defect.Err)
}
}
section, ok := registry.Lookup(SectionName)
if !ok {
t.Fatalf("%s is not registered, so nothing resolves its keys", SectionName)
}

want := []string{flagAdminAddress, flagAdminEnabled}
sort.Strings(want)
if got := section.Keys; !reflect.DeepEqual(got, want) {
t.Errorf("declared keys are %v, want %v", got, want)
}
}

// TestTheDefaultsAreWhatTheNodeAlreadyRuns keeps the section from restating the values by hand.
func TestTheDefaultsAreWhatTheNodeAlreadyRuns(t *testing.T) {
for _, mode := range registry.Modes() {
got, ok := defaults(mode).(Config)
if !ok {
t.Fatalf("mode %q: defaults returned %T, want Config", mode, defaults(mode))
}
if got != DefaultConfig {
t.Errorf("mode %q resolves to %+v, want the package default %+v", mode, got, DefaultConfig)
}
}
}
194 changes: 194 additions & 0 deletions app/config_register.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
package app

import (
"github.com/sei-protocol/sei-chain/app/params"
"github.com/sei-protocol/sei-chain/config/registry"
srvconfig "github.com/sei-protocol/sei-chain/sei-cosmos/server/config"
"github.com/sei-protocol/sei-chain/sei-db/config"
)

// The names these sections have in the configuration key space.
const (
LightInvarianceSectionName = "light_invariance"
GenesisSectionName = "genesis"
StateStoreSectionName = "state-store"
StateCommitSectionName = "state-commit"
)

// Registration puts this package's configuration sections in the registry.
//
// The owning package registers its own sections, so the struct, the values and the keys come from one
// place and cannot drift apart. The keys derive from mapstructure tags, so a section's spelling and its
// reader's own constants stay the same strings.
func init() {
registry.RegisterSection(LightInvarianceSectionName, &LightInvarianceConfig{}, lightInvarianceDefaults)
registry.RegisterSection(GenesisSectionName, &genesisSchema{}, genesisDefaults)
registry.RegisterSection(StateStoreSectionName, &stateStoreSchema{}, stateStoreDefaults)
registry.RegisterSection(StateCommitSectionName, &stateCommitSchema{}, stateCommitDefaults)
}

// lightInvarianceDefaults is what this section resolves to for a node that has written nothing.
//
// The same value for every mode, and on. What the check compares is a property of every node rather than
// of one kind, so a mode that resolved it off would stop those nodes noticing they had diverged.
func lightInvarianceDefaults(registry.Mode) any { return DefaultLightInvarianceConfig }

// genesisSchema declares the keys the genesis import reader resolves.
//
// A schema and not a transport: nothing decodes into it. The type the reader fills is
// genesistypes.GenesisImportConfig, which carries no mapstructure tags at all, so no key can be derived
// from it. Declaring the spelling here is what lets the registry name the keys the reader looks up.
type genesisSchema struct {
StreamImport bool `mapstructure:"stream-import"`
ImportFile string `mapstructure:"import-file"`
}

// genesisDefaults is what this section resolves to for a node that has written nothing.
//
// Read out of the reader's own default rather than written again here, so a changed default moves both at
// once and this states only which key carries which setting. The same values for every mode: streaming a
// genesis file is what an operator does to import a chain's existing state, and no node mode implies it.
func genesisDefaults(registry.Mode) any {
return genesisSchema{
StreamImport: DefaultGenesisConfig.StreamGenesisImport,
ImportFile: DefaultGenesisConfig.GenesisStreamFile,
}
}

// stateStoreSchema declares the keys parseSSConfigs resolves.
//
// A schema and not a transport: nothing decodes into it. config.StateStoreConfig carries mapstructure
// tags of its own and every one names something other than the key the reader looks up, so deriving from
// that type would declare a set of keys no operator writes. It also holds settings no key reaches, which
// stay at whatever the defaults struct holds; giving them keys would declare settings a written value
// could not change.
type stateStoreSchema struct {
Enable bool `mapstructure:"ss-enable"`
DBDirectory string `mapstructure:"ss-db-directory"`
Backend string `mapstructure:"ss-backend"`
AsyncWriteBuffer int `mapstructure:"ss-async-write-buffer"`
KeepRecent int `mapstructure:"ss-keep-recent"`
PruneIntervalSeconds int `mapstructure:"ss-prune-interval"`
ImportNumWorkers int `mapstructure:"ss-import-num-workers"`
EnableReadWriteMetrics bool `mapstructure:"ss-enable-read-write-metrics"`
SnapshotEnable bool `mapstructure:"ss-snapshot-enable"`
EVMDBDirectory string `mapstructure:"evm-ss-db-directory"`
SeparateEVMSubDBs bool `mapstructure:"evm-ss-separate-dbs"`
EVMSplit bool `mapstructure:"evm-ss-split"`
}

// stateStoreDefaults is what the seid init command writes for a node of this kind, with one deliberate
// departure.
//
// Answered per mode, because two of these settings mean something different depending on what kind of node
// asks. An archive node exists to keep history, so it keeps every version; a validator and a seed serve no
// queries, so the store is off for them. Both come from the mode rules the binary already states rather
// than being written again here, so a change to those rules moves this too.
//
// The departure is the retention an archive node keeps. The mode rules set it to keep everything and the
// command does not write that, because the type it renders declares a state store field of its own and
// fills it from the mode-blind default, so the rule is applied and then discarded. PLT-955 records that,
// and records the decision: pin what a node resolves today and correct it here, in the versioned
// declaration, rather than at the point that loses it. So this states the rule and the command states the
// value the rule was overwritten by, and the test beside this holds both, because a departure nothing
// measures is indistinguishable from an oversight.
//
// The declared values are also not what this section's reader produces for a file missing the keys, which
// is a different comparison and measured separately.
func stateStoreDefaults(mode registry.Mode) any {
server := srvconfig.DefaultConfig()
params.SetAppConfigByMode(server, params.NodeMode(mode))
live := server.StateStore
return stateStoreSchema{
Enable: live.Enable,
DBDirectory: live.DBDirectory,
Backend: live.Backend,
AsyncWriteBuffer: live.AsyncWriteBuffer,
KeepRecent: live.KeepRecent,
PruneIntervalSeconds: live.PruneIntervalSeconds,
ImportNumWorkers: live.ImportNumWorkers,
EnableReadWriteMetrics: live.EnableReadWriteMetrics,
SnapshotEnable: live.SnapshotEnable,
EVMDBDirectory: live.EVMDBDirectory,
SeparateEVMSubDBs: live.SeparateEVMSubDBs,
EVMSplit: live.EVMSplit,
}
}

// stateCommitFlatKVSchema declares the one flat key-value key this package's reader resolves.
//
// A nested segment, because the key is state-commit.flatkv.enable-read-write-metrics. Four further keys
// under that name are read by the Cosmos server's own configuration reader and not by this one, so they
// belong to whoever registers that reader's section rather than to this one.
type stateCommitFlatKVSchema struct {
EnableReadWriteMetrics bool `mapstructure:"enable-read-write-metrics"`
}

// stateCommitSchema declares the keys parseSCConfigs resolves.
//
// A schema and not a transport: nothing decodes into it. config.StateCommitConfig nests its settings
// under MemIAVLConfig, FlatKVConfig and HashLogger, and the keys the reader looks up are flat names on the
// section itself, so no derivation from that type produces them.
//
// The write mode is a plain string rather than the reader's own named type, because the reader parses a
// written name into that type itself. Declaring the named type would have one key answer as a named string
// from these defaults and as a plain one from an operator's file, which is a difference a caller can trip
// over and nothing here needs.
type stateCommitSchema struct {
Enable bool `mapstructure:"sc-enable"`
Directory string `mapstructure:"sc-directory"`
AsyncCommitBuffer int `mapstructure:"sc-async-commit-buffer"`
SnapshotKeepRecent uint32 `mapstructure:"sc-keep-recent"`
SnapshotInterval uint32 `mapstructure:"sc-snapshot-interval"`
SnapshotMinTimeInterval uint32 `mapstructure:"sc-snapshot-min-time-interval"`
SnapshotWriterLimit int `mapstructure:"sc-snapshot-writer-limit"`
SnapshotPrefetchThreshold float64 `mapstructure:"sc-snapshot-prefetch-threshold"`
SnapshotWriteRateMBps int `mapstructure:"sc-snapshot-write-rate-mbps"`
HistoricalProofMaxInFlight int `mapstructure:"sc-historical-proof-max-inflight"`
HistoricalProofRateLimit float64 `mapstructure:"sc-historical-proof-rate-limit"`
HistoricalProofBurst int `mapstructure:"sc-historical-proof-burst"`
WriteMode string `mapstructure:"sc-write-mode"`
WriteModeEnableAuto bool `mapstructure:"sc-write-mode-enable-auto"`
HashLoggerEnable bool `mapstructure:"sc-hash-logger-enable"`
HashLoggerDirectory string `mapstructure:"sc-hash-logger-directory"`
HashLoggerBlocksToRetain uint `mapstructure:"sc-hash-logger-blocks-to-retain"`
HashLoggerTargetFileSize uint `mapstructure:"sc-hash-logger-target-file-size"`
HashLoggerMaxDiskSize uint `mapstructure:"sc-hash-logger-max-disk-size"`
FlatKV stateCommitFlatKVSchema `mapstructure:"flatkv"`
}

// stateCommitDefaults is what this section resolves to for a node that has written nothing.
//
// The declared defaults. Two of them are not what this section's reader produces for a file missing the
// key, and a test names which two and what a node runs instead.
//
// The same values for every mode. How often a node snapshots and how much proof history it serves are
// decisions about disk and load that an operator writes down, and nothing in the binary makes either
// follow from what kind of node is asking.
func stateCommitDefaults(registry.Mode) any {
live := config.DefaultStateCommitConfig()
return stateCommitSchema{
Enable: live.Enable,
Directory: live.Directory,
AsyncCommitBuffer: live.MemIAVLConfig.AsyncCommitBuffer,
SnapshotKeepRecent: live.MemIAVLConfig.SnapshotKeepRecent,
SnapshotInterval: live.MemIAVLConfig.SnapshotInterval,
SnapshotMinTimeInterval: live.MemIAVLConfig.SnapshotMinTimeInterval,
SnapshotWriterLimit: live.MemIAVLConfig.SnapshotWriterLimit,
SnapshotPrefetchThreshold: live.MemIAVLConfig.SnapshotPrefetchThreshold,
SnapshotWriteRateMBps: live.MemIAVLConfig.SnapshotWriteRateMBps,
HistoricalProofMaxInFlight: live.HistoricalProofMaxInFlight,
HistoricalProofRateLimit: live.HistoricalProofRateLimit,
HistoricalProofBurst: live.HistoricalProofBurst,
WriteMode: string(live.WriteMode),
WriteModeEnableAuto: live.WriteModeEnableAuto,
HashLoggerEnable: live.HashLogger.Enable,
HashLoggerDirectory: live.HashLogger.Directory,
HashLoggerBlocksToRetain: live.HashLogger.BlocksToRetain,
HashLoggerTargetFileSize: live.HashLogger.TargetFileSize,
HashLoggerMaxDiskSize: live.HashLogger.MaxDiskSize,
FlatKV: stateCommitFlatKVSchema{
EnableReadWriteMetrics: live.FlatKVConfig.EnableReadWriteMetrics,
},
}
}
Loading
Loading