OpenNHP/opennhp · error

cluster : missing publicKeyBase64

Error message

cluster %q: missing publicKeyBase64

What it means

buildCluster in endpoints/agent/cluster.go rejects a ClusterConfig whose PubKeyBase64 is empty with "cluster %q: missing publicKeyBase64". Each server cluster needs the shared server public key to encrypt knock packets; without it the cluster cannot be constructed. Called from updateServerPeers during config load/reload.

Solutions

  1. Add the server's base64 public key to every cluster entry in the agent config
  2. Re-run key generation/deploy rendering (scripts/generate-nhp-keys.sh) if the key field came up empty from a template
  3. Add startup validation of cluster configs before use, listing which cluster is missing the key
  4. Check for field-name typos (publicKeyBase64 vs pubkey) against the ClusterConfig struct

Example fix

// before
[[clusters]]
name = "nhp-server"
instances = ["udp://10.0.0.5:5555"]
// after
[[clusters]]
name = "nhp-server"
publicKeyBase64 = "<nhp_server_public_key>"
instances = ["udp://10.0.0.5:5555"]
Defensive patterns

Strategy: validation

Validate before calling

for _, c := range clusterConfigs {
	if c.PubKeyBase64 == "" {
		return fmt.Errorf("cluster %q: publicKeyBase64 required", c.Name)
	}
	if _, err := base64.StdEncoding.DecodeString(c.PubKeyBase64); err != nil { return err }
}

Try / catch

cl, err := buildCluster(cfg)
if err != nil {
	return fmt.Errorf("skipping cluster: %w", err) // or fail startup
}

Prevention

When it happens

Trigger: A clusters entry in agent config omits publicKeyBase64 (or names a different field, leaving PubKeyBase64 unset) when updateServerPeers parses and builds clusters at startup or on config reload.

Common situations: Hand-writing clusters in agent.toml/config.json and forgetting the key; template rendering with an unset env var (e.g. nhp_server_public_key missing from secrets) leaving the field blank; renaming fields across versions.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07). Data as JSON: /api/errors/9051091a3a1d2c42. Report an issue: GitHub.

Appendix: source

Thrown at endpoints/agent/cluster.go:133

func (sc *ServerCluster) FindInstanceByAddr(addr string) *ServerInstance {
	if sc == nil {
		return nil
	}
	for _, inst := range sc.instances {
		if inst.hostPort == addr {
			return inst
		}
	}
	return nil
}

// buildCluster turns a parsed ClusterConfig into a runtime cluster.
// The returned cluster's representativePeer is NOT yet registered on a
// device — callers (updateServerPeers) are responsible for that, so
// they can also handle peer removal on reload.
func buildCluster(cfg *ClusterConfig) (*ServerCluster, error) {
	if cfg.PubKeyBase64 == "" {
		return nil, fmt.Errorf("cluster %q: missing publicKeyBase64", cfg.Name)
	}
	if len(cfg.Instances) == 0 {
		return nil, fmt.Errorf("cluster %q (%s): no instances configured",
			cfg.Name, cfg.PubKeyBase64)
	}
	if err := cfg.LoadBalance.Validate(); err != nil {
		return nil, fmt.Errorf("cluster %q (%s): %w",
			cfg.Name, cfg.PubKeyBase64, err)
	}

	sc := &ServerCluster{
		PublicKeyBase64: cfg.PubKeyBase64,
		Name:            cfg.Name,
		Sticky:          cfg.StickyOrDefault(),
		instances:       make([]*ServerInstance, 0, len(cfg.Instances)),
	}

	for i, ic := range cfg.Instances {

View on GitHub (pinned to 6e04ca5ff0)