{"record":{"id":"10a77f43dd60cdc7","repo":"OpenNHP/opennhp","slug":"peer-expired-type-s-pubkey-s","errorCode":null,"errorMessage":"peer expired (type=%s, pubkey=%s)","messagePattern":"peer expired \\(type=(.+?), pubkey=(.+?)\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nhp/core/responder.go","lineNumber":510,"sourceCode":"\t\t\t// registered via NHP-REG stored in SQLite).\n\t\t\tppd.device.optionMutex.Lock()\n\t\t\tfallback := ppd.device.option.PeerLookupFallback\n\t\t\tppd.device.optionMutex.Unlock()\n\t\t\tif fallback != nil && fallback(peerPk, ppd.HeaderType) {\n\t\t\t\tlog.Info(\"validatePeer: %s peer accepted via fallback, pubkey=%s\",\n\t\t\t\t\tpeerDeviceTypeName, peerPkBase64)\n\t\t\t\t// Skip expiry/address checks for fallback peers.\n\t\t\t\tgoto peerAccepted\n\t\t\t}\n\t\t\tlog.Error(\"validatePeer: %s peer not found in peer pool, pubkey=%s\",\n\t\t\t\tpeerDeviceTypeName, peerPkBase64)\n\t\t\terr = fmt.Errorf(\"peer not found in peer pool (type=%s, pubkey=%s)\", peerDeviceTypeName, peerPkBase64)\n\t\t\treturn err\n\t\t}\n\n\t\tif peer.IsExpired() {\n\t\t\tlog.Error(\"validatePeer: %s peer expired, pubkey=%s\", peerDeviceTypeName, peerPkBase64)\n\t\t\terr = fmt.Errorf(\"peer expired (type=%s, pubkey=%s)\", peerDeviceTypeName, peerPkBase64)\n\t\t\treturn err\n\t\t}\n\n\t\tif !ppd.ConnData.CheckRecvAddress(ppd.LocalInitTime, ppd.ConnData.RemoteAddr) {\n\t\t\tlog.Error(\"validatePeer: %s peer address mismatch on connection, pubkey=%s, remoteAddr=%s\",\n\t\t\t\tpeerDeviceTypeName, peerPkBase64, ppd.ConnData.RemoteAddr)\n\t\t\terr = fmt.Errorf(\"peer does not match its previous address on this connection (type=%s, pubkey=%s)\", peerDeviceTypeName, peerPkBase64)\n\t\t\treturn err\n\t\t}\n\t\tppd.ConnData.UpdateRecvAddress(ppd.LocalInitTime, ppd.ConnData.RemoteAddr)\n\t\tpeer.UpdateRecv(ppd.LocalInitTime)\n\tpeerAccepted:\n\t}\n\n\tppd.RemotePubKey = peerPk\n\tif ppd.ConnPeerPublicKey != nil {\n\t\tcopy((*ppd.ConnPeerPublicKey)[:], peerPk)\n\t}","sourceCodeStart":492,"sourceCodeEnd":528,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/nhp/core/responder.go#L492-L528","documentation":"The peer's public key was found in the pool, but the peer record reports itself expired (Peer.IsExpired). The device treats expiry as an authorization failure: the previously registered agent/server/AC must re-register or be refreshed before its messages are processed. This guards against stale registrations lingering after lease/renewal windows pass.","triggerScenarios":"An agent registered via NHP_REG whose expiry timestamp passed and that did not re-register; a long-running deployment where peer TTLs in the pool/SQLite store lapsed during downtime; clock skew between nodes making a still-valid peer look expired; a peer removed by a cleanup job while still in use.","commonSituations":"Agents offline longer than their registration TTL then resuming traffic; system clock jumps (NTP resets, VM snapshots) on the receiving node; server maintenance windows exceeding peer lease duration; expired entries in the dynamic-registration SQLite store never purged or renewed.","solutions":["Have the peer re-register (resend NHP_REG/NHP_OTP) to refresh its record, then retry the operation.","Check both hosts' clocks (NTP) — skew beyond the TTL makes valid peers look expired.","Extend or disable the peer TTL in the peer-pool/registration store configuration if long-lived peers are expected.","Purge and re-provision stale entries: restart the dynamic-registration store sync or re-run the key deployment for that peer."],"exampleFix":"// before: agent resumes after TTL lapse with stale registration\n// agent\nclient.Knock(...) // peer expired (type=agent, pubkey=...)\n// after: re-register first\nclient.Register() // refreshes peer record/expiry\nclient.Knock(...)","handlingStrategy":"retry","validationCode":"// client-side: check remaining lease before sending\nif time.Now().Unix() > registrationExpiryUnix-60 {\n\t_ = client.Register() // refresh proactively\n}","typeGuard":null,"tryCatchPattern":"err := client.SendKnock(server)\nif err != nil && strings.Contains(err.Error(), \"peer expired\") {\n\tif rerr := client.Register(); rerr == nil {\n\t\terr = client.SendKnock(server)\n\t}\n}","preventionTips":["Schedule periodic re-registration well inside the peer TTL.","Enforce NTP on all nodes to avoid clock-skew-driven expiry.","Set TTLs longer than the longest expected maintenance/downtime window.","Alert on expiry rejections so stale registrations are refreshed before users notice.","Purge and re-provision expired entries in the registration store on startup."],"tags":["go","zero-trust","peer-expiry","registration","clock-skew"],"backgroundTag":"jwt-token-expired","analyzedSha":"6e04ca5ff03222a699c24205cd4bf8fee9af7ffe","analyzedAt":"2026-09-07T15:44:59.941Z","contentChangedAt":"2026-09-07T15:44:59.941Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}