OpenNHP/opennhp · critical

no private key configured; check etc/config.toml

Error message

no private key configured; check etc/config.toml

What it means

UdpAgent.Start refuses to boot the agent when no base64 private key is set in etc/config.toml and the agent was not created with allowMissingConfig. The agent's identity (device key pair) is central to knocking and registration, so starting with a random throwaway key silently would break all peer trust. The library throws this to fail loudly on real misconfiguration while still permitting a temporary key flow for registration (allowMissingConfig=true).

Solutions

  1. Run the agent's keygen command (e.g. ./nhp-agentd keygen --curve) and paste the resulting base64 private key into PrivateKeyBase64 in etc/config.toml
  2. Verify the agent actually loads the intended etc/config.toml (check working directory / config path flag)
  3. If the agent is only used for registration with a throwaway key, construct/initialize it with allowMissingConfig enabled so ReinitWithKey can replace the key after Start

Example fix

// before (etc/config.toml)
# PrivateKeyBase64 = ""
// after
PrivateKeyBase64 = "dJf3...base64key=="
Defensive patterns

Strategy: validation

Validate before calling

if agent.config.PrivateKeyBase64 == "" && !agent.allowMissingConfig {
    return fmt.Errorf("agent private key missing: run keygen and set PrivateKeyBase64 in etc/config.toml")
}

Try / catch

if err := agent.Start(); err != nil {
    if strings.Contains(err.Error(), "no private key configured") {
        log.Fatal("configure PrivateKeyBase64 in etc/config.toml before starting")
    }
}

Prevention

When it happens

Trigger: Calling Start (via runApp, runDHPApp, runRegisterApp, or RestartAgent) on a UdpAgent whose config.PrivateKeyBase64 is empty and whose allowMissingConfig flag is false.

Common situations: Fresh checkout without filling in etc/config.toml; key field accidentally cleared during redeployment; running the agent binary before running keygen; wrong config file path so PrivateKeyBase64 never loads.

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/d9e16434c55eea87. Report an issue: GitHub.

Appendix: source

Thrown at endpoints/agent/udpagent.go:385

	err = a.loadBaseConfig()
	if err != nil {
		return err
	}
	err = a.loadDHPConfig()
	if err != nil {
		return err
	}

	var prk []byte
	if a.config.PrivateKeyBase64 == "" {
		// An empty private key is only acceptable in the register bootstrap
		// flow (allowMissingConfig): use a throwaway key that ReinitWithKey
		// replaces immediately after Start returns. For run/dhp this is a
		// real misconfiguration — fail loudly instead of silently starting
		// with a random key and empty identity.
		if !a.allowMissingConfig {
			log.Error("no private key configured in etc/config.toml")
			return fmt.Errorf("no private key configured; check etc/config.toml")
		}
		prk = core.NewECDH(core.ECC_CURVE25519).PrivateKey()
	} else {
		prk, err = base64.StdEncoding.DecodeString(a.config.PrivateKeyBase64)
		if err != nil {
			log.Error("private key parse error %v\n", err)
			return fmt.Errorf("private key parse error %v", err)
		}
	}

	a.device = core.NewDevice(core.NHP_AGENT, prk, nil)
	if a.device == nil {
		log.Critical("failed to create device %v\n", err)
		return fmt.Errorf("failed to create device %v", err)
	}

	// start device routines
	a.device.Start()

View on GitHub (pinned to 6e04ca5ff0)