Skip to content

feat(seeds): ship Sei Labs seeds as the default bootstrap-peers - #3885

Open
monty-sei wants to merge 7 commits into
mainfrom
monty/plt-816-default-bootstrap-peers
Open

feat(seeds): ship Sei Labs seeds as the default bootstrap-peers#3885
monty-sei wants to merge 7 commits into
mainfrom
monty/plt-816-default-bootstrap-peers

Conversation

@monty-sei

@monty-sei monty-sei commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

What

A fresh seid init --chain-id pacific-1 (or atlantic-2) now writes a config.toml with bootstrap-peers already populated with the Sei Labs seed nodes, so a node bootstraps peer discovery with no other config set. Today the field defaults to "" and operators have to source a peer list out of band.

How

Adds app/seeds, mirroring the existing app/genesis pattern for well-known chain data, and calls applyDefaultBootstrapPeers in InitCmd after the chain-id is resolved and before WriteConfigFile.

Decisions worth reviewing

  • bootstrap-peers, not persistent-peers. Seeds populate the address book via PEX and may then be dropped. Holding operator connections open against our seeds indefinitely is wrong for them and a load multiplier for us.
  • arctic-1 is deliberately excluded. It is a devnet, it has no Cosmos chain-registry entry, and a devnet is the most likely network to be reset or re-keyed — the worst case for an address baked permanently into a release. It stays well-known for genesis; it just ships no seeds. A test asserts both halves of that.
  • Init-time only, no runtime injection. Writing config at start-up would mutate a file the operator owns.

Behaviour on existing configs — please read before release notes

An earlier revision of this description said "the operator value always wins". That was imprecise, and the review was right to flag it. Precisely:

  • seid init builds its config from tmcfg.DefaultConfig() and exposes no flag for bootstrap-peers, so the field is always empty at that point. The empty check in applyDefaultBootstrapPeers is defensive, not an operator-precedence mechanism — it keeps the behaviour correct for any future caller that pre-populates the field.
  • Without --overwrite, init refuses to touch an existing config at all, so a hand-edited bootstrap-peers is safe.
  • With --overwrite, config.toml is rewritten wholesale, so a hand-edited bootstrap-peers is now replaced by the Sei seeds instead of by "". Not a regression, and arguably an improvement, but it is a behaviour change and belongs in the release notes.

Verified empirically: hand-edit bootstrap-peers, run init --overwrite, and the seeds replace it; run init without --overwrite and it errors out leaving the file untouched.

Nodes that already ran init do not retroactively get seeds — they are covered by the docs update and a separate chain-registry submission.

Permanence

These strings ship inside released binaries and operators pin them; the secret-connection handshake verifies the NodeID, so a changed ID is a rejected dial rather than a degraded one, and a release in the wild cannot be recalled. The inputs are final: the DNS pattern is settled, all node keys are pinned in encrypted secrets, and every instance-target NLB port is pinned in the infrastructure repo. Retiring an address means keeping it dialable until every release carrying it is out of use — noted in the package doc.

Drive-by

Corrects the --chain-id flag help, which claimed "if left blank will use sei" while the code panics on an empty value.

The other half of that mismatch — panic() printing a Go stack trace for what is plain operator error, where RunE returns errors for the neighbouring invalid-mode case — is left for a separate PR, per the review. It is tracked.

Testing

Wiring coverage (new). TestInitCmdWritesDefaultBootstrapPeers executes the real InitCmd against a temp home and asserts the written config.toml. This closes a gap the review identified: previously nothing exercised RunE, so deleting the wiring left the entire suite green. Confirmed by mutation — removing the call now fails this test.

Data coverage. app/seeds tests hold each NodeID against types.NodeID.Validate rather than a local regex (so the check cannot drift from CometBFT's definition), require the :26656 port, and assert NodeID and host uniqueness across the whole table — a per-chain check would miss a pacific-1 entry pasted into the atlantic-2 block. A further test asserts every seeded chain is well-known per genesis.IsWellKnown, catching a typo'd chain-id that would otherwise be a silent no-op.

End to end with a locally built binary:

--chain-id bootstrap-peers
pacific-1 3 entries
atlantic-2 3 entries
arctic-1 ""
my-private-chain ""

Existing cmd/seid/cmd tests pass unchanged.

Related

The operator docs update is held as a draft until this ships in a release, since it documents the defaulted behaviour.

@cursor

cursor Bot commented Aug 10, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes default P2P bootstrap for new nodes and embeds long-lived seed endpoints in release binaries; scope is init-time config only with tests, not consensus or auth.

Overview
Fresh seid init on pacific-1 or atlantic-2 now writes bootstrap-peers in config.toml with three Sei Labs seed nodes per network (multi-region), so P2P discovery works without operators sourcing peers manually.

A new app/seeds package holds the chain-id → seed table (mirroring app/genesis); applyDefaultBootstrapPeers runs during init only when the field is still empty. arctic-1, private chains, and unknown chain-ids stay at "". --chain-id help text is corrected to match the required flag behavior.

sei-tendermint/config exposes ParseNodeAddress for tests and callers outside internal/p2p. Unit tests cover seed address validation and init wiring (including real InitCmdconfig.toml); optional integration tests dial live seeds.

Note: init --overwrite rewrites config.toml, so a custom bootstrap-peers is replaced by the defaults (same as before, but defaults are now seeds instead of empty).

Reviewed by Cursor Bugbot for commit 6666ff7. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actions

github-actions Bot commented Aug 10, 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 13, 2026, 12:08 AM

@codecov

codecov Bot commented Aug 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 60.75%. Comparing base (feb6bba) to head (6666ff7).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #3885      +/-   ##
==========================================
+ Coverage   59.48%   60.75%   +1.27%     
==========================================
  Files        2323     2256      -67     
  Lines      198554   190268    -8286     
==========================================
- Hits       118106   115597    -2509     
+ Misses      69240    64447    -4793     
+ Partials    11208    10224     -984     
Flag Coverage Δ
sei-chain-pr 60.93% <100.00%> (?)
sei-db 70.41% <ø> (ø)
sei-db-state-db ?

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

Files with missing lines Coverage Δ
app/seeds/seeds.go 100.00% <100.00%> (ø)
cmd/seid/cmd/init.go 66.01% <100.00%> (+30.87%) ⬆️
sei-tendermint/config/node_address.go 100.00% <100.00%> (ø)

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

@seidroid seidroid Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small, well-documented change that defaults p2p.bootstrap-peers to the Sei Labs seeds at seid init; the data package and its tests are solid and the wiring is correctly scoped (empty-value only, exact chain-id match, no runtime mutation). No blockers — the notes are about the untested production wiring, an inline comment that AGENTS.md would have as a named step, and two small test/API cleanups.

Findings: 0 blocking | 9 non-blocking | 5 posted inline

Blockers

  • None at the file/PR level.

Non-blocking

  • No test exercises InitCmd's RunE, so nothing covers the new wiring (shared finding with Codex). cmd/seid/cmd/init_test.go re-simulates config construction (tmcfg.DefaultConfig() + SetTendermintConfigByMode + WriteConfigFile) rather than running the command, so deleting init.go:128-130 leaves every test in the tree passing. The app/seeds tests only cover the data. The manual verification table in the PR description is the only evidence the feature works; extracting the step into a named helper (see the inline note) makes it assertable without standing up the whole command.
  • The Cursor second-opinion pass produced no output — cursor-review.md is empty. Only the Codex pass contributed findings (its single P2 item is folded in above).
  • The PR description says "Operator value always wins," which is true for the value but worth stating precisely in release notes: seid init builds tmConfig from tmcfg.DefaultConfig(), so there is no way for an operator to supply bootstrap-peers to init at all, and init --overwrite rewrites config.toml wholesale. The behavior change there is that an operator's hand-edited bootstrap-peers is now replaced by the Sei seeds instead of by "" — not a regression, but not "the operator value survives" either.
  • Verified against REVIEW_GUIDELINES.md and testutil/configtest: no configtest row is owed here. The suite pins configuration reads, and this change is an init-time write of a default; p2p.bootstrap-peers is not in the tendermint precedence manifest (only p2p.persistent-peers is), so nothing in that suite fails or needs updating.
  • 5 suggestion(s)/nit(s) flagged inline on specific lines.

Comment thread cmd/seid/cmd/init.go Outdated
Comment thread app/seeds/seeds_test.go Outdated
Comment thread app/seeds/seeds_test.go Outdated
Comment thread app/seeds/seeds.go Outdated
Comment thread cmd/seid/cmd/init.go
cmd.Flags().BoolP(FlagOverwrite, "o", false, "overwrite the genesis.json and existing config files (config.toml, app.toml)")
cmd.Flags().Bool(FlagRecover, false, "provide seed phrase to recover existing key instead of creating")
cmd.Flags().String(flags.FlagChainID, "", "genesis file chain-id, if left blank will use sei")
cmd.Flags().String(flags.FlagChainID, "", "chain-id to initialise for (required), e.g. pacific-1 or atlantic-2")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] Good fix to the help text. Since the drive-by is specifically about the mismatch between "if left blank will use sei" and the code's behavior, the other half is still there: line 120 panic("chain-id is required, ...") prints a Go stack trace for what is plain operator error. RunE already returns errors for the invalid-mode case a few lines up — return fmt.Errorf("chain-id is required, please set using --chain-id") would match that and give a clean message. Fine to leave for a separate PR.

A fresh `seid init --chain-id pacific-1` (or atlantic-2) now writes a
config.toml with bootstrap-peers already populated, so a node bootstraps
peer discovery with no operator configuration. Previously the field
defaulted to "" and operators had to source a peer list out of band.

Adds app/seeds, mirroring the existing app/genesis pattern for well-known
chain data, and consults it in InitCmd after the chain-id is resolved.

- An operator-supplied bootstrap-peers always wins; we only fill an empty
  value. An unrecognised chain-id is a no-op, so private and local chains
  are unaffected.
- Seeds go in bootstrap-peers rather than persistent-peers: they seed the
  address book via PEX and may then be dropped, and pinning operators to
  long-lived connections against them is wrong for them and a load
  multiplier for us.
- arctic-1 is deliberately excluded. It is a devnet with no Cosmos
  chain-registry entry, and a devnet is the most likely network to be
  reset or re-keyed — the worst case for an address baked permanently
  into a release.

Also corrects the --chain-id flag help, which claimed "if left blank will
use sei" while the code panics on an empty value.

Verified end to end: init on pacific-1 and atlantic-2 yields three seeds
each, arctic-1 and unknown chain-ids yield "".
@monty-sei
monty-sei force-pushed the monty/plt-816-default-bootstrap-peers branch from ec0fff5 to 4c49b2b Compare August 10, 2026 01:02

@seidroid seidroid Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A small, well-scoped change that pre-populates bootstrap-peers at seid init for pacific-1/atlantic-2, with a genuinely thoughtful package doc and decent unit tests. No correctness or security blockers — the pinned NodeIDs mean a hijacked DNS name yields a rejected dial rather than a MITM — but the wiring in InitCmd is untested, and a couple of comments describe invariants the code doesn't actually establish.

Findings: 0 blocking | 9 non-blocking | 5 posted inline

Blockers

  • None at the file/PR level.

Non-blocking

  • No test drives InitCmd's RunE, so the new wiring is uncovered (shared with Codex). TestInitModeConfiguration re-implements init's steps (tmcfg.DefaultConfig()SetTendermintConfigByModeWriteConfigFile) rather than invoking the command, so deleting the seeds block at cmd/seid/cmd/init.go:123-130 would not fail a single test. Given this repo's characterization-test culture for configuration (see testutil/configtest/AGENTS.md), a test that runs the command for pacific-1 / atlantic-2 / arctic-1 / a private chain-id and asserts p2p.bootstrap-peers in the written config.toml would pin exactly the four rows the PR description verified by hand.
  • Security posture is sound and worth recording: the seed list is the only new attack surface, and each address is bound to a pinned NodeID verified by the secret-connection handshake, so DNS compromise of *.platform.sei.io produces a failed dial rather than a MITM; seeds only populate the address book via PEX and gate nothing in consensus.
  • The Cursor second-opinion file (./cursor-review.md) is empty — that pass produced no output, so this review merges only Claude's and Codex's findings.
  • Optional, adjacent to the drive-by flag-help fix: cmd/seid/cmd/init.go:120 still panics on an empty chain-id inside a RunE that returns error. Now that the help text advertises the flag as required, returning an error (or marking the flag required via cobra) would give operators a clean message instead of a stack trace. Out of scope if you'd rather keep the diff tight.
  • 5 suggestion(s)/nit(s) flagged inline on specific lines.

Comment thread cmd/seid/cmd/init.go Outdated
Comment thread app/seeds/seeds_test.go Outdated
Comment thread app/seeds/seeds_test.go Outdated
Comment thread app/seeds/seeds_test.go Outdated
Comment thread app/seeds/seeds.go Outdated
Extracts the inline bootstrap-peers block into applyDefaultBootstrapPeers,
so RunE reads as a sequence of named steps and the rationale lives in a
doc comment rather than mid-flow.

The extraction is what makes the wiring testable. TestInitCmdWrites-
DefaultBootstrapPeers now executes InitCmd against a temp home and asserts
the written config.toml; previously the wiring had no coverage at all —
deleting the call left the whole suite green.

Test hardening in app/seeds:
- Hold NodeIDs against types.NodeID.Validate instead of a local regex, so
  the check cannot drift from CometBFT's definition (the old pattern also
  accepted a five-digit port).
- Check NodeID and host uniqueness across the whole table rather than per
  chain. The likeliest copy/paste error is a pacific-1 entry pasted into
  the atlantic-2 block, which a per-chain check could not catch.

Drops the exported Chains(), whose only caller was a same-package test
that can range over chainSeeds directly.

Also documents the actual --overwrite behaviour on the helper: init has no
bootstrap-peers flag, so the empty check is defensive rather than an
operator-precedence mechanism, and `init --overwrite` replaces a
hand-edited value with the seeds (previously with "").

@seidroid seidroid Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean, well-tested change: app/seeds ships the Sei Labs seed list and applyDefaultBootstrapPeers wires it into seid init, with tests covering both the data and the call site. No blocking issues found; remaining notes are operational (verifying the baked-in addresses, release-notes coverage for init --overwrite) plus two nits.

Findings: 0 blocking | 7 non-blocking | 3 posted inline

Blockers

  • None at the file/PR level.

Non-blocking

  • The Cursor second-opinion pass produced no output (cursor-review.md is empty); Codex reported no material issues. This review is therefore effectively a single-reviewer pass plus Codex.
  • init --overwrite now replaces a hand-edited bootstrap-peers with the Sei seeds instead of "". The PR description calls this out correctly and nothing in the repo documents it — worth making sure it actually lands in the release notes, since the only in-tree record is the doc comment on applyDefaultBootstrapPeers.
  • The --chain-id help text now says (required) while the code still panic()s on an empty value, printing a Go stack trace for plain operator error next to a RunE-returned error for the invalid-mode case. The author explicitly deferred the panicreturn fmt.Errorf(...) change to a tracked follow-up; noting only so it isn't lost, not asking for it here.
  • Verification note rather than a defect: the correctness of the six seed addresses cannot be checked from this repo — the tests validate form (NodeID validity, :26656, uniqueness) but a well-formed wrong host or ID passes. Given the stated one-way permanence, a dial check against all six from outside the Sei network before the release is cut would be the thing that catches it.
  • 3 suggestion(s)/nit(s) flagged inline on specific lines.

Comment thread app/seeds/seeds.go
// sei-protocol/platform (the SeiNode's externalAddress plus its NodeID).
var chainSeeds = map[string][]string{
"pacific-1": {
"0cd5f57c249b5aca815710338e1fe7a14797585d@seed-0-p2p.pacific-1.prod.platform.sei.io:26656",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] Naming asymmetry worth a second look: seed-1/seed-2 carry an explicit cell suffix (prod-euw1, prod-use2) while seed-0 is bare prod, so the doc comment's "one per cell (eu-central-1, eu-west-1, us-east-2)" is only readable as three cells if you already know prod == eu-central-1. Not something the tests can catch (host uniqueness passes either way), and these strings are one-way once released — worth confirming against clusters/<cell>/<chain>/seeds/ that the unsuffixed name really is the eu-central-1 cell for both chains.

Comment thread app/seeds/seeds.go Outdated
Comment thread cmd/seid/cmd/init.go Outdated

@masih masih left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Integration tests would be awesome to have where we assert those endpoints are connectable and are usable as seed, i.e. new peers are discovered from them. Those tests should be disabled by default and only run on CI since they would be making remote calls.

Excited to get this moving! 🙌

Comment thread app/seeds/seeds.go Outdated
Comment thread app/seeds/seeds.go Outdated
Comment thread app/seeds/seeds_test.go Outdated
Comment thread app/seeds/seeds_test.go Outdated
Comment thread app/seeds/seeds_test.go Outdated
Comment thread app/seeds/seeds_test.go Outdated
Comment thread app/seeds/seeds_test.go Outdated
Comment thread app/seeds/seeds_test.go Outdated
Comment thread app/seeds/seeds_test.go Outdated
Comment thread app/seeds/seeds_test.go Outdated
…h p2p

Collapses app/seeds to the address table plus BootstrapPeers, the only
thing anything outside the package uses. ForChain had no caller beyond
this package, so exporting it (and defensively copying the slice for
callers that did not exist) was API for nobody. With it gone,
BootstrapPeers reads straight off the map: a missing chain yields nil,
and strings.Join renders that as "".

Re-exports ParseNodeAddress from sei-tendermint/config so the tests
validate addresses with the parser the router actually dials with,
rather than string-slicing and re-encoding the format locally. The
parser lives under sei-tendermint/internal and is unreachable from
app/seeds; config already surfaces p2p types (AutobahnValidator.NodeKey)
and does not import back into it, so the re-export costs nothing.

Test changes follow from that:
- parse each address instead of splitting on "@", which also covers the
  port without asserting a specific one — 26656 is our convention, not a
  protocol rule
- assert the seed list is non-empty rather than exactly three; the count
  is an artefact of how many we happen to run
- drop the no-spaces assertion: SplitAndTrimEmpty trims the cutset, so
  the parser tolerates spaces and the assertion tested nothing
- assert empty rather than nil for unknown chains, which is the contract
- chain names as constants, testify to match app/genesis and app/params
- drop the copy-semantics test, moot now the copy is gone

Adds a build-tagged integration suite (go test -tags=integration) that
dials every published seed and requires it to send its preface. A seed
that accepts TCP and then says nothing is otherwise invisible: the pod
reports Ready and seed mode publishes no metrics.
Comment thread app/seeds/seeds_integration_test.go Outdated

@seidroid seidroid Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ships Sei Labs seed nodes as default bootstrap-peers at seid init time; the wiring, data format, and test coverage are sound (addresses parse with the same p2p.ParseNodeAddress the router uses, and the InitCmd end-to-end test closes the mutation gap). No correctness or security blockers — remaining notes are godoc-style violations of AGENTS.md, a port assertion the tests claim but don't make, and integration tests that are neither run nor compile-checked in CI.

Findings: 0 blocking | 11 non-blocking | 5 posted inline

Blockers

  • None at the file/PR level.

Non-blocking

  • The Cursor second-opinion file (./cursor-review.md) is empty — that pass produced no output, so this review merges only Claude's and Codex's findings.
  • TestSeedAddressesAreDialableForm (integration) largely duplicates TestSeedAddressesParseAndAreUnique (unit), but the hostname-shape and NodeID-length assertions live only in the tagged copy that never runs. Consider moving the pure-parsing assertions into the untagged test so they actually execute, leaving only the network calls behind the tag.
  • require.Lenf(t, string(addr.NodeID), 40, ...) is redundant: ParseNodeAddress -> NodeAddress.Validate -> NodeID.Validate already enforces exactly 40 lowercase hex chars, so the preceding require.NoErrorf subsumes it.
  • cmd/seid/cmd/bootstrap_peers_test.go mixes assertion styles — raw t.Errorf in the first three tests, testify/require in the rest. Pick one for the file.
  • The --overwrite behaviour change (a hand-edited bootstrap-peers is now replaced by the Sei seeds rather than by "") is correctly identified in the PR description; make sure it actually lands in the release notes, since nothing in the tree records it outside the applyDefaultBootstrapPeers doc comment.
  • No prompt-injection or instruction-like content was found in the diff, commit messages, or PR description.
  • 5 suggestion(s)/nit(s) flagged inline on specific lines.

Comment thread cmd/seid/cmd/init.go Outdated
Comment thread app/seeds/seeds_test.go
@@ -0,0 +1,71 @@
//go:build integration

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion] (Also raised by Codex.) Nothing invokes go test -tags=integration ./app/seeds/... — not .github/workflows/, not the Makefile, not integration-test-matrix.json (which uses the unrelated yaml_integration tag).

Worse than just not running: .golangci.yml sets build-tags: [codeanalysis] and tests: false, and go build / go vet skip tagged files by default, so this file is never even compile-checked in CI. It can break against a config API change and stay green indefinitely.

A scheduled workflow (these assert live endpoint health, so cron fits better than per-PR) plus a make target would give the reachability check a way to actually fire. At minimum, add it to a compile-only step (go vet -tags=integration ./app/seeds/...) so it can't rot silently.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question, and I went and checked: no, integration is a new tag. The repo currently uses norace, linux, inprocess, rocksdb, dummy, cgo, darwin, mock_balances, ledger, littdb_wip, windows, and the existing integration suite runs under a different one, yaml_integration, driven by .github/workflows/integration-test-matrix.json.

I looked at reusing that tag and decided against it: that suite is docker based (docker exec sei-node-0 ...) against a locally spun chain, whereas this one dials live public endpoints. Folding them together means either the docker matrix starts making external calls, or this gets dragged into a container lifecycle it does not need.

So for now I have gone the honest route rather than the half wired one: the file header now says plainly that nothing runs or compile checks it and that it is an on demand tool rather than coverage, and I have raised a follow up to add a scheduled (cron plus workflow_dispatch) job.

Scheduled rather than a PR gate, since it depends on live external infrastructure and would otherwise block unrelated PRs whenever a seed hiccups or CI egress is flaky. Sequencing it after the two silent seeds are fixed as well, because wiring it today means the job is red on its first run, which is the quickest way to train everyone to ignore it. Happy to bring it into this PR instead if you would rather it did not land separately!

Comment thread app/seeds/seeds_integration_test.go Outdated
Comment thread sei-tendermint/config/node_address.go Outdated
@monty-sei

Copy link
Copy Markdown
Contributor Author

Hey @masih! This is the comment I was most keen to action, so I've added a build tagged suite (go test -tags=integration ./app/seeds/...) that's off by default and dials every published seed, asserting it actually sends its preface rather than just accepting the TCP connection.

Worth flagging that it currently fails on two of the six, both in the prod cell, and that's a real finding rather than a flaky test. Those endpoints complete the TCP handshake and then send nothing at all, even holding the connection open for three minutes, while the ones in prod-euw1 and prod-use2 respond in under a second. I'm still digging into why and @bdchatham is looking into it as well, it needs a look from inside the cluster to tell whether the pod is serving at all or whether it's something in the load balancer path. Because seed mode publishes no metrics and the pods report Ready, there's currently nothing that would have told us, which I think makes a pretty good case for exactly the tests you're asking for!

I'd like to get to the bottom of those two and have the full suite green before we merge, so the seeds are genuinely serving by the time the defaults go out to operators.

ParseNodeAddress substitutes 26657 — the RPC port — when the port is
missing or zero, so the previous NotZero assertion could never fail. A
seed entry written without ":26656", the likeliest paste error, parsed
clean, passed every test, and would have shipped pointing at the RPC
port with no way to recall it. Assert the port explicitly in both the
unit and integration suites.

That gap was introduced when the tests moved from string-slicing to the
parser: parsing is the stronger check for everything except the one
field the parser silently fills in.

Also:
- document the port substitution on ParseNodeAddress, where callers meet
  it, and correct the doc's claim about internal visibility (it is
  scoped to the sei-tendermint tree, not the module — the repo has a
  single go.mod)
- cut applyDefaultBootstrapPeers' godoc to what the function does; the
  PEX rationale is already in the app/seeds package doc and the
  --overwrite behaviour belongs in the release notes
- compile-check the build-tagged integration file in `make lint`, since
  the untagged `go vet ./...` skips it and it would otherwise rot

@seidroid seidroid Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well-tested, well-documented change that wires Sei Labs seeds into seid init; the wiring is correct (ordering vs. SetTendermintConfigByMode and WriteConfigFile verified, and the explicit :26656 assertion is genuinely load-bearing given ParseNodeAddress defaults a missing port to 26657). No correctness bugs found. Two non-blocking issues: the new configinternal/p2p re-export is test-only but adds a heavy dependency edge and a latent import-cycle hazard to a widely imported package, and the integration tests are compile-checked but never executed by any CI job.

Findings: 0 blocking | 10 non-blocking | 4 posted inline

Blockers

  • None at the file/PR level.

Non-blocking

  • The Cursor review file (./cursor-review.md) is empty — that pass produced no output, so this review reflects Claude + Codex only.
  • Highest-value pre-merge action, and not something review can discharge: the NodeIDs and hostnames cannot be verified from this repo, and the package doc correctly states edits here are one-way (a wrong NodeID is a rejected handshake, not a degraded dial, and a released binary can't be recalled). Worth an independent second pair of eyes diffing all six entries against clusters/<cell>/<chain>/seeds/seed-N/seed-N.yaml in sei-protocol/platform before merge. The test suite deliberately cannot catch this class of error — it validates form and uniqueness, not identity.
  • The --overwrite behaviour change (a hand-edited bootstrap-peers is now replaced by the Sei seeds rather than by "") is accurately described in the PR body; just confirm it actually reaches the release notes, since the PR description isn't where operators will look.
  • Checked testutil/configtest per AGENTS.md: bootstrap-peers isn't pinned there, and this changes an init-time write rather than a config read, so no characterization row is owed. Noting it explicitly so the omission reads as deliberate.
  • Verified no script, Dockerfile, or workflow in the repo runs seid init with pacific-1/atlantic-2, so this does not cause CI containers to start dialing production seeds.
  • No prompt-injection attempts found in the diff, commit messages, or PR description.
  • 4 suggestion(s)/nit(s) flagged inline on specific lines.

Comment thread sei-tendermint/config/node_address.go
Comment thread Makefile
Comment thread app/seeds/seeds.go Outdated
Comment thread cmd/seid/cmd/bootstrap_peers_test.go Outdated
@monty-sei monty-sei added the backport release/v6.6 Backport to release v6.6 label Aug 12, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit ae5bfc5. Configure here.

Comment thread app/seeds/seeds.go

@seidroid seidroid Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well-scoped change: the seed table, the applyDefaultBootstrapPeers guard at the single choke point in InitCmd, and the end-to-end init test all look correct, and the parse/port/uniqueness tests guard the failure mode that matters (a malformed baked-in peer is a hard startup failure at sei-tendermint/node/setup.go:528, not a degraded one). No blockers; the notes are about the new integration tests never running in CI and the configinternal/p2p re-export being added for test convenience.

Findings: 0 blocking | 9 non-blocking | 5 posted inline

Blockers

  • None at the file/PR level.

Non-blocking

  • Cursor's second-opinion pass produced no output (cursor-review.md is empty), so this review reflects Claude + Codex only.
  • Release notes: the init --overwrite behaviour change (a hand-edited bootstrap-peers is now replaced by the Sei seeds rather than by ""), the fact that already-initialised nodes get nothing, and how an operator opts out of dialling Sei Labs infra are all correctly described in the PR body but not enforced by anything in-tree — worth making sure they land in the notes.
  • Nothing in this repo's docs/ describes joining a network, so there is no in-repo doc to update; the PR's "covered by the docs update" refers to an external change. Fine, just noting it is out of band and unverifiable from this diff.
  • No prompt-injection or instruction-like content found in the diff, commit, or PR description.
  • 5 suggestion(s)/nit(s) flagged inline on specific lines.

Comment thread Makefile Outdated
Comment thread sei-tendermint/config/node_address.go
Comment thread sei-tendermint/config/node_address.go Outdated
Comment thread app/seeds/seeds_integration_test.go Outdated
Comment thread cmd/seid/cmd/bootstrap_peers_test.go Outdated

@masih masih left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One blocker on integration test tag and comments.

Comment thread Makefile Outdated
Comment thread app/seeds/seeds_integration_test.go Outdated
@@ -0,0 +1,75 @@
//go:build integration

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is integration an existing tag used elewhere? If yes and CI is already hooked up to running them then great.

If not, i recommend to:

  • pick an existing tag to save yourself from having to set up a CI for it, unless there is a really good reason that I might be missing for keeping it separate.
  • at the very least this PR should hook these tests to some CI job that runs them.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question, and I went and checked: no, integration is a new tag. The repo currently uses norace, linux, inprocess, rocksdb, dummy, cgo, darwin, mock_balances, ledger, littdb_wip, windows, and the existing integration suite runs under a different one, yaml_integration, driven by .github/workflows/integration-test-matrix.json.

I looked at reusing that tag and decided against it: that suite is docker based (docker exec sei-node-0 ...) against a locally spun chain, whereas this one dials live public endpoints. Folding them together means either the docker matrix starts making external calls, or this gets dragged into a container lifecycle it does not need.

So for now I have gone the honest route rather than the half wired one: the file header now says plainly that nothing runs or compile checks it and that it is an on demand tool rather than coverage, and I have raised a follow up to add a scheduled (cron plus workflow_dispatch) job.

Scheduled rather than a PR gate, since it depends on live external infrastructure and would otherwise block unrelated PRs whenever a seed hiccups or CI egress is flaky. Sequencing it after the two silent seeds are fixed as well, because wiring it today means the job is red on its first run, which is the quickest way to train everyone to ignore it. Happy to bring it into this PR instead if you would rather it did not land separately!

Comment thread app/seeds/seeds_test.go Outdated
Comment thread cmd/seid/cmd/bootstrap_peers_test.go Outdated
Comment thread sei-tendermint/config/node_address.go Outdated
@masih masih added the ai: skip-review Skips Seidroid AI review triggered via Unified CI. label Aug 12, 2026
…port

Replaces the explicit 26656 assertion with a parse round-trip. The
property that matters is that a published address survives the parser
unchanged, not that the port equals a particular number; pinning the
number read as though the protocol required it. The round-trip still
catches the case that prompted the assertion, since ParseNodeAddress
substitutes 26657 for a missing port and the re-rendered string then
differs from what was written. Verified by dropping a port from the
table and watching the test fail.

Deletes TestSeedAddressesAreDialableForm. It made no network calls, so
the integration tag meant it never ran, and its parse and port checks
duplicated the unit test. Its one unique assertion, that seeds publish a
DNS name rather than a bare host, moves to seeds_test.go where it runs
by default. The integration file now holds only the reachability check,
which is the sole thing there that needs a network.

The cmd tests no longer pin the seed count; they assert init writes
seeds.BootstrapPeers(chainID) unaltered, which is what the wiring is
responsible for. Cardinality and address shape belong to app/seeds,
where the table lives.

Drops the `go vet -tags=integration` line from `make lint`: `make lint`
is not a CI job, so it only ever fired locally and did not deliver the
compile check it claimed. Wiring the tagged package into CI is the real
fix and is still open.
Names the unsuffixed `prod` cell as eu-central-1 in the seed table's
comment. The other two are inferable from the hostnames (prod-euw1,
prod-use2) but that one was only inferable by elimination, and whoever
adds or retires a cell reads exactly this comment.

Uses require.* throughout bootstrap_peers_test.go rather than mixing it
with t.Errorf in the three helper-level tests.

States plainly in the integration file's header that nothing runs or
compile-checks it: the `integration` tag is used nowhere else, and both
go vet and golangci-lint skip tagged files. It is an on-demand tool
today, not coverage, and saying so beats a header that claims CI it
does not have. The scheduled job is tracked separately and should land
after the seeds are healthy, so its first run is green.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai: skip-review Skips Seidroid AI review triggered via Unified CI. backport release/v6.6 Backport to release v6.6 non-app-hash-breaking

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants