OpenNHP/opennhp · error

relay: server # publicKeyBase64 invalid

Error message

relay: server #%d publicKeyBase64 invalid: %w

What it means

normalize decodes each server's publicKeyBase64 via utils.PubKeyFingerprintFromBase64 to compute a fingerprint; this error wraps the decode failure with the server index. The value must be valid base64 encoding of an acceptable public key format (e.g. Curve25519).

Solutions

  1. Re-copy the server's public key exactly as output by keygen, without quotes, whitespace, or truncation
  2. Verify the key decodes with `base64 -d` to the expected byte length
  3. Confirm key algorithm matches the relay's cipher scheme (curve vs sm2) and use the corresponding keygen output
  4. Ensure you are using the server's public key, not its private key

Example fix

// before
pubKeyBase64 = "<server pubkey>"
// after
pubKeyBase64 = "dGVzdC1wdWJsaWMta2V5LWJhc2U2NC1zdHJpbmc="
Defensive patterns

Strategy: validation

Validate before calling

raw := strings.TrimSpace(cfg.Servers[i].PubKeyBase64)
b, err := base64.StdEncoding.DecodeString(raw)
if err != nil || len(b) != 32 { // curve25519
	return fmt.Errorf("server #%d pubkey not valid base64 32-byte key", i)
}

Try / catch

if err := cfg.Normalize(); err != nil {
	if strings.Contains(err.Error(), "publicKeyBase64 invalid") {
		return fmt.Errorf("re-copy the server key from keygen output verbatim: %w", err)
	}
	return err
}

Prevention

When it happens

Trigger: PubKeyFingerprintFromBase64 returns an error for server i — the publicKeyBase64 string is not valid base64, has whitespace/quotes/newlines embedded, decodes to the wrong key length, or contains a placeholder like "<server pubkey>".

Common situations: Pasting a key with surrounding quotes or line breaks; truncating the key during copy/paste; putting an SM2 key where Curve is expected (or vice versa); placeholder text left in from a template; using the private key instead of the public key.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

Thrown at endpoints/relay/config.go:233

	// change the instance" mistake. The dedupe key is (fingerprint, addr),
	// NOT addr alone: resolveTarget routes by PeerPk, so two DISTINCT
	// pubkeys legitimately sharing one host:port (a SNI/header-routed
	// front-end, or port-multiplexed identities) is a valid topology and
	// must not be a hard config-load failure. Only same-pubkey + same-addr
	// is the unambiguous copy-paste error.
	type addrOrigin struct {
		server   int
		instance int
	}
	seenAddr := make(map[string]addrOrigin)
	for i := range cfg.Servers {
		c := &cfg.Servers[i]
		if c.PubKeyBase64 == "" {
			return fmt.Errorf("relay: server #%d missing publicKeyBase64", i)
		}
		fp, err := utils.PubKeyFingerprintFromBase64(c.PubKeyBase64)
		if err != nil {
			return fmt.Errorf("relay: server #%d publicKeyBase64 invalid: %w", i, err)
		}
		if dup, ok := seenFP[fp]; ok {
			return fmt.Errorf("relay: server #%d and #%d share the same publicKeyBase64 (fingerprint %s)", dup, i, fp)
		}
		seenFP[fp] = i

		if len(c.Instances) == 0 {
			return fmt.Errorf("relay: server #%d (fingerprint %s) has no [[Servers.Instances]]", i, fp)
		}
		for j := range c.Instances {
			inst := &c.Instances[j]
			if inst.Host == "" {
				return fmt.Errorf("relay: server #%d instance #%d missing host", i, j)
			}
			if inst.Port <= 0 {
				return fmt.Errorf("relay: server #%d instance #%d missing or invalid port", i, j)
			}
			addr := fmt.Sprintf("%s:%d", inst.Host, inst.Port)

View on GitHub (pinned to 6e04ca5ff0)