From a3734678a15b83fab163b20e2bf77969a1c760c7 Mon Sep 17 00:00:00 2001 From: Calin Martinconi Date: Tue, 14 Apr 2026 11:53:49 +0300 Subject: [PATCH 1/4] refactor: replace full-node flag with explicit node-mode config --- config/config.yaml | 2 +- config/light-node.yaml | 2 +- config/local.yaml | 7 ++++--- config/public-testnet.yaml | 2 +- config/staging.yaml | 2 +- config/testnet-bee-playground.yaml | 2 +- pkg/config/bee.go | 3 ++- pkg/orchestration/k8s/cluster.go | 8 ++++---- pkg/orchestration/k8s/helpers.go | 1 + pkg/orchestration/node.go | 20 +++++++++++++++++++- 10 files changed, 35 insertions(+), 14 deletions(-) diff --git a/config/config.yaml b/config/config.yaml index ba5820aaa..e8d2b07ce 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -103,7 +103,7 @@ bee-configs: db-disable-seeks-compaction: false db-open-files-limit: 200 db-write-buffer-size: 33554432 - full-node: true + node-mode: full mainnet: false nat-addr: "" network-id: 12345 diff --git a/config/light-node.yaml b/config/light-node.yaml index 3722e9791..2b5e6786a 100644 --- a/config/light-node.yaml +++ b/config/light-node.yaml @@ -2,7 +2,7 @@ bee-configs: light-node: _inherit: default - full-node: false + node-mode: light # node groups for light nodes node-groups: diff --git a/config/local.yaml b/config/local.yaml index 13e992ba8..c5fe8b639 100644 --- a/config/local.yaml +++ b/config/local.yaml @@ -166,7 +166,7 @@ bee-configs: db-disable-seeks-compaction: false db-open-files-limit: 200 db-write-buffer-size: 33554432 - full-node: true + node-mode: full mainnet: false nat-addr: "" nat-wss-addr: "" @@ -206,9 +206,10 @@ bee-configs: p2p-wss-enable: true bee-local-ultralight-autotls: _inherit: "bee-local-dns" - full-node: false + node-mode: ultra-light p2p-wss-enable: true blockchain-rpc-endpoint: # ultralight nodes don't connect to the blockchain + swap-enable: false bootnode-local: _inherit: "bee-local" bootnode-mode: true @@ -222,7 +223,7 @@ bee-configs: bee-local-light: _inherit: "bee-local" bootnode: /dnsaddr/localhost - full-node: false + node-mode: light bee-local-gc: _inherit: "bee-local" cache-capacity: 10 diff --git a/config/public-testnet.yaml b/config/public-testnet.yaml index ebd787358..b5b4106dc 100644 --- a/config/public-testnet.yaml +++ b/config/public-testnet.yaml @@ -47,7 +47,7 @@ bee-configs: sepolia: _inherit: "" bootnodes: "/dnsaddr/testnet.ethswarm.org" - full-node: true + node-mode: full checks: pt-pingpong: diff --git a/config/staging.yaml b/config/staging.yaml index 9345579ae..447ec5ea6 100644 --- a/config/staging.yaml +++ b/config/staging.yaml @@ -35,7 +35,7 @@ bee-configs: api-addr: ":1633" blockchain-rpc-endpoint: http://rpc-sepolia-haproxy.default.svc.swarm1.local bootnodes: /dnsaddr/testnet.ethswarm.org - full-node: true + node-mode: full mainnet: false network-id: 10 p2p-addr: ":1634" diff --git a/config/testnet-bee-playground.yaml b/config/testnet-bee-playground.yaml index 4ef4f645a..e3ed3592b 100644 --- a/config/testnet-bee-playground.yaml +++ b/config/testnet-bee-playground.yaml @@ -54,7 +54,7 @@ bee-configs: db-disable-seeks-compaction: true db-open-files-limit: 200 db-write-buffer-size: 33554432 - full-node: true + node-mode: full mainnet: false nat-addr: "" network-id: 5 diff --git a/pkg/config/bee.go b/pkg/config/bee.go index 343664794..d57de6ac4 100644 --- a/pkg/config/bee.go +++ b/pkg/config/bee.go @@ -33,7 +33,8 @@ type BeeConfig struct { DbDisableSeeksCompaction *bool `yaml:"db-disable-seeks-compaction"` DbOpenFilesLimit *int `yaml:"db-open-files-limit"` DbWriteBufferSize *int `yaml:"db-write-buffer-size"` - FullNode *bool `yaml:"full-node"` + FullNode *bool `yaml:"full-node"` // Deprecated: use NodeMode + NodeMode *string `yaml:"node-mode"` Mainnet *bool `yaml:"mainnet"` NATAddr *string `yaml:"nat-addr"` NATWSSAddr *string `yaml:"nat-wss-addr"` diff --git a/pkg/orchestration/k8s/cluster.go b/pkg/orchestration/k8s/cluster.go index 1fbffc9ce..7f068e71c 100644 --- a/pkg/orchestration/k8s/cluster.go +++ b/pkg/orchestration/k8s/cluster.go @@ -226,7 +226,7 @@ func (c *Cluster) NodeNames() (names []string) { // LightNodeNames returns a list of light node names func (c *Cluster) LightNodeNames() (names []string) { for name, node := range c.Nodes() { - if !node.Config().FullNode { + if node.Config().IsLightNode() { names = append(names, name) } } @@ -237,7 +237,7 @@ func (c *Cluster) LightNodeNames() (names []string) { func (c *Cluster) FullNodeNames() (names []string) { for name, node := range c.Nodes() { cfg := node.Config() - if cfg.FullNode && !cfg.BootnodeMode { + if cfg.IsFullNode() && !cfg.BootnodeMode { names = append(names, name) } } @@ -249,7 +249,7 @@ func (c *Cluster) ShuffledFullNodeClients(ctx context.Context, r *rand.Rand) (or var res orchestration.ClientList for _, node := range c.Nodes() { cfg := node.Config() - if cfg.FullNode && !cfg.BootnodeMode { + if cfg.IsFullNode() && !cfg.BootnodeMode { res = append(res, node.Client()) } } @@ -464,7 +464,7 @@ func (c *Cluster) ClosestFullNodeClient(ctx context.Context, s *bee.Client) (*be } cfg := node.Config() // closet peer is not a full node. Check other peers in the same bin - if !cfg.FullNode || cfg.BootnodeMode { + if !cfg.IsFullNode() || cfg.BootnodeMode { skipList = append(skipList, addr) b-- continue diff --git a/pkg/orchestration/k8s/helpers.go b/pkg/orchestration/k8s/helpers.go index e763be697..8ffd4b9f9 100644 --- a/pkg/orchestration/k8s/helpers.go +++ b/pkg/orchestration/k8s/helpers.go @@ -33,6 +33,7 @@ db-disable-seeks-compaction: {{.DbDisableSeeksCompaction}} db-open-files-limit: {{.DbOpenFilesLimit}} db-write-buffer-size: {{.DbWriteBufferSize}} full-node: {{.FullNode}} +node-mode: {{.NodeMode}} mainnet: {{.Mainnet}} nat-addr: {{.NATAddr}} nat-wss-addr: {{.NATWSSAddr}} diff --git a/pkg/orchestration/node.go b/pkg/orchestration/node.go index 20bdce748..63bc841a6 100644 --- a/pkg/orchestration/node.go +++ b/pkg/orchestration/node.go @@ -92,7 +92,8 @@ type Config struct { DbDisableSeeksCompaction bool // disables DB compactions triggered by seeks DbOpenFilesLimit int // number of open files allowed by database DbWriteBufferSize int // size of the database write buffer in bytes - FullNode bool // cause the node to start in full mode + FullNode bool // cause the node to start in full mode (deprecated: use NodeMode) + NodeMode string // node operational mode: full, light, or ultra-light Mainnet bool // enable mainnet NATAddr string // NAT exposed address NATWSSAddr string // NAT exposed secure WebSocket address @@ -123,3 +124,20 @@ type Config struct { WelcomeMessage string // send a welcome message string during handshakes WithdrawAddress string // allowed addresses for wallet withdrawal } + +// IsFullNode reports whether the node is configured as a full node. +// It checks NodeMode first; falls back to the deprecated FullNode bool. +func (c Config) IsFullNode() bool { + if c.NodeMode != "" { + return c.NodeMode == "full" + } + return c.FullNode +} + +// IsLightNode reports whether the node is configured as a light node. +func (c Config) IsLightNode() bool { + if c.NodeMode != "" { + return c.NodeMode == "light" + } + return !c.FullNode +} From 8068425dbf7cbc31645a027c487c0e4f86ec5438 Mon Sep 17 00:00:00 2001 From: Calin Martinconi Date: Thu, 27 Aug 2026 17:21:19 +0300 Subject: [PATCH 2/4] ci: bump golangci-lint to v2.11.3 and disable lint cache --- .github/workflows/go.yml | 4 ++-- AGENTS.md | 2 +- Makefile | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index f15e487d2..c22888480 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -28,9 +28,9 @@ jobs: - name: Lint uses: golangci/golangci-lint-action@v9 with: - version: v2.10.1 + version: v2.11.3 args: --timeout 10m - skip-cache: false + skip-cache: true - name: Vet run: make vet - name: Whitespace check diff --git a/AGENTS.md b/AGENTS.md index 6b281c286..3ce51c5e0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -17,7 +17,7 @@ Beekeeper consumes Bee as a library (`github.com/ethersphere/bee/v2` in `go.mod` ```bash make binary # build ./dist/beekeeper (CGO disabled, version stamped via -ldflags) make test # unit tests: go test -v ./pkg/... -make lint # golangci-lint (pinned v2.10.1; auto-installs if missing) +make lint # golangci-lint (pinned v2.11.3; auto-installs if missing) ``` Run a single test: `go test -run TestName ./pkg//...` (add `-race` to mirror CI). diff --git a/Makefile b/Makefile index 86744fd96..4d6ca66e4 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ GO ?= go GOLANGCI_LINT ?= golangci-lint -GOLANGCI_LINT_VERSION ?= v2.10.1 +GOLANGCI_LINT_VERSION ?= v2.11.3 COMMIT ?= "$(shell git describe --long --dirty --always --match "" || true)" VERSION ?= "$(shell git describe --tags --abbrev=0 | cut -c2-)" LDFLAGS ?= -s -w \ From 34101875f9ea0a3e16416cbf2014e8aca261f88f Mon Sep 17 00:00:00 2001 From: Calin Martinconi Date: Wed, 2 Sep 2026 14:11:15 +0300 Subject: [PATCH 3/4] feat(orchestration): add UltraLightNodeNames and IsUltraLightNode helpers --- pkg/orchestration/cluster.go | 1 + pkg/orchestration/config_test.go | 75 ++++++++++++++++++++++++++++++++ pkg/orchestration/k8s/cluster.go | 10 +++++ pkg/orchestration/node.go | 10 ++++- 4 files changed, 95 insertions(+), 1 deletion(-) diff --git a/pkg/orchestration/cluster.go b/pkg/orchestration/cluster.go index 6b52b5719..a7081bdab 100644 --- a/pkg/orchestration/cluster.go +++ b/pkg/orchestration/cluster.go @@ -23,6 +23,7 @@ type Cluster interface { FullNodeNames() (names []string) GlobalReplicationFactor(ctx context.Context, a swarm.Address) (grf int, err error) LightNodeNames() (names []string) + UltraLightNodeNames() (names []string) Name() string Namespace() string NodeGroup(name string) (ng NodeGroup, err error) diff --git a/pkg/orchestration/config_test.go b/pkg/orchestration/config_test.go index 6da47e2cf..a951a035a 100644 --- a/pkg/orchestration/config_test.go +++ b/pkg/orchestration/config_test.go @@ -60,3 +60,78 @@ func TestDeref(t *testing.T) { t.Errorf("Deref(new(false)) = %v, want false", got) } } + +func TestNodeModeHelpers(t *testing.T) { + t.Parallel() + + strPtr := func(s string) *string { return &s } + boolPtr := func(b bool) *bool { return &b } + + tests := []struct { + name string + cfg orchestration.Config + wantFull bool + wantLight bool + wantUltraLight bool + }{ + { + name: "explicit node-mode full", + cfg: orchestration.Config{NodeMode: strPtr("full")}, + wantFull: true, + wantLight: false, + wantUltraLight: false, + }, + { + name: "explicit node-mode light", + cfg: orchestration.Config{NodeMode: strPtr("light")}, + wantFull: false, + wantLight: true, + wantUltraLight: false, + }, + { + name: "explicit node-mode ultra-light", + cfg: orchestration.Config{NodeMode: strPtr("ultra-light")}, + wantFull: false, + wantLight: false, + wantUltraLight: true, + }, + { + name: "legacy full-node true", + cfg: orchestration.Config{FullNode: boolPtr(true)}, + wantFull: true, + wantLight: false, + wantUltraLight: false, + }, + { + name: "legacy full-node false with rpc (light)", + cfg: orchestration.Config{ + FullNode: boolPtr(false), + BlockchainRPCEndpoint: strPtr("http://localhost:8545"), + }, + wantFull: false, + wantLight: true, + wantUltraLight: false, + }, + { + name: "legacy full-node false without rpc (ultra-light)", + cfg: orchestration.Config{FullNode: boolPtr(false)}, + wantFull: false, + wantLight: false, + wantUltraLight: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.cfg.IsFullNode(); got != tt.wantFull { + t.Errorf("IsFullNode() = %v, want %v", got, tt.wantFull) + } + if got := tt.cfg.IsLightNode(); got != tt.wantLight { + t.Errorf("IsLightNode() = %v, want %v", got, tt.wantLight) + } + if got := tt.cfg.IsUltraLightNode(); got != tt.wantUltraLight { + t.Errorf("IsUltraLightNode() = %v, want %v", got, tt.wantUltraLight) + } + }) + } +} diff --git a/pkg/orchestration/k8s/cluster.go b/pkg/orchestration/k8s/cluster.go index d1c05eb9f..bbb91de51 100644 --- a/pkg/orchestration/k8s/cluster.go +++ b/pkg/orchestration/k8s/cluster.go @@ -233,6 +233,16 @@ func (c *Cluster) LightNodeNames() (names []string) { return names } +// UltraLightNodeNames returns a list of ultra-light node names +func (c *Cluster) UltraLightNodeNames() (names []string) { + for name, node := range c.Nodes() { + if node.Config().IsUltraLightNode() { + names = append(names, name) + } + } + return names +} + // FullNodeNames returns a list of full node names func (c *Cluster) FullNodeNames() (names []string) { for name, node := range c.Nodes() { diff --git a/pkg/orchestration/node.go b/pkg/orchestration/node.go index f1a40b679..de7ff6a1c 100644 --- a/pkg/orchestration/node.go +++ b/pkg/orchestration/node.go @@ -190,5 +190,13 @@ func (c Config) IsLightNode() bool { if c.NodeMode != nil && *c.NodeMode != "" { return *c.NodeMode == "light" } - return !Deref(c.FullNode) + return !Deref(c.FullNode) && Deref(c.BlockchainRPCEndpoint) != "" +} + +// IsUltraLightNode reports whether the node is configured as an ultra-light node. +func (c Config) IsUltraLightNode() bool { + if c.NodeMode != nil && *c.NodeMode != "" { + return *c.NodeMode == "ultra-light" + } + return !Deref(c.FullNode) && Deref(c.BlockchainRPCEndpoint) == "" } From 7c7fa049a2880bd29651060fe88f9edd2ac733d4 Mon Sep 17 00:00:00 2001 From: Calin Martinconi Date: Thu, 3 Sep 2026 14:44:00 +0300 Subject: [PATCH 4/4] fix(config): disable options rejected by bee in light and ultra-light profiles Light and ultra-light profiles inherit storage-incentives-enable: true (and, for ultra-light, chequebook-enable: true) from their full-node base profile. Bee rejects storage incentives outside full mode and chequebook without swap, so the light nodes in the local-dns cluster never became ready and the bee Beekeeper CI job failed at cluster setup. Set the inherited options to false where the mode does not support them. --- config/light-node.yaml | 1 + config/local.yaml | 3 +++ 2 files changed, 4 insertions(+) diff --git a/config/light-node.yaml b/config/light-node.yaml index 2b5e6786a..746191f5c 100644 --- a/config/light-node.yaml +++ b/config/light-node.yaml @@ -3,6 +3,7 @@ bee-configs: light-node: _inherit: default node-mode: light + storage-incentives-enable: false # inherited true is rejected in light mode # node groups for light nodes node-groups: diff --git a/config/local.yaml b/config/local.yaml index f8be852a7..3a6e06996 100644 --- a/config/local.yaml +++ b/config/local.yaml @@ -205,6 +205,8 @@ bee-configs: p2p-wss-enable: true blockchain-rpc-endpoint: # ultralight nodes don't connect to the blockchain swap-enable: false + chequebook-enable: false # inherited true is rejected without swap + storage-incentives-enable: false # inherited true is rejected in ultra-light mode bootnode-local: _inherit: "bee-local" bootnode-mode: true @@ -216,6 +218,7 @@ bee-configs: bee-local-light: _inherit: "bee-local" node-mode: light + storage-incentives-enable: false # inherited true is rejected in light mode bee-local-gc: _inherit: "bee-local" cache-capacity: 10