{"record":{"id":"aa35b7b1cd5f729e","repo":"OpenNHP/opennhp","slug":"keystore-find-agent-by-pubkey-w","errorCode":null,"errorMessage":"keystore: find agent by pubkey: %w","messagePattern":"keystore: find agent by pubkey: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"endpoints/server/keystore.go","lineNumber":464,"sourceCode":"\t\trec.ExpiresAt = &expiresAt.Int64\n\t}\n\treturn rec, nil\n}\n\n// FindAgentByPublicKey returns true if the given base64-encoded public key\n// is registered, active, and not expired. This is the gate consulted by\n// the noise-layer peer validation fallback; an expired key behaves as if\n// the agent were never registered.\nfunc (s *AgentKeyStore) FindAgentByPublicKey(pubKeyBase64 string) (bool, error) {\n\tvar count int\n\terr := s.db.QueryRow(\n\t\t`SELECT COUNT(*) FROM agent_keys\n\t\t WHERE public_key = ? AND active = 1\n\t\t   AND (expires_at IS NULL OR expires_at > ?)`,\n\t\tpubKeyBase64, time.Now().Unix(),\n\t).Scan(&count)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"keystore: find agent by pubkey: %w\", err)\n\t}\n\treturn count > 0, nil\n}\n\n// IsAgentRegistered returns true if the user+device pair has an active,\n// non-expired registered key.\nfunc (s *AgentKeyStore) IsAgentRegistered(userId, deviceId string) (bool, error) {\n\tvar count int\n\terr := s.db.QueryRow(\n\t\t`SELECT COUNT(*) FROM agent_keys\n\t\t WHERE usr_id = ? AND dev_id = ? AND active = 1\n\t\t   AND (expires_at IS NULL OR expires_at > ?)`,\n\t\tuserId, deviceId, time.Now().Unix(),\n\t).Scan(&count)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"keystore: check agent registered: %w\", err)\n\t}\n\treturn count > 0, nil","sourceCodeStart":446,"sourceCodeEnd":482,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/endpoints/server/keystore.go#L446-L482","documentation":"FindAgentByPublicKey wraps failures of the COUNT(*) query that checks whether a base64 public key belongs to an active, non-expired agent. Only DB-level failures produce this error; a zero count returns (false, nil). It is consulted by the noise-layer peer validation fallback, so an error here can block peer authentication.","triggerScenarios":"Calling FindAgentByPublicKey(pubKeyBase64) when the DB connection is closed, the agent_keys table is missing, the database is locked, or the driver returns a general query error.","commonSituations":"SQLite file locked during backup jobs; schema mismatch after an upgrade; disk-full conditions preventing even a SELECT from executing; running the server against an uninitialized config path.","solutions":["Log the wrapped driver error to determine if it is 'no such table', 'database is locked', or 'database is closed'.","Run schema migration before starting the noise-layer listener that calls this function.","Enable WAL mode and busy_timeout on the SQLite handle to reduce lock contention.","Confirm the server process owns/opens the DB file it is configured to use.","Retry transient lock errors with backoff at the caller (peer validation) level."],"exampleFix":"null","handlingStrategy":"retry","validationCode":"if pubKeyBase64 == \"\" { return false, errors.New(\"empty public key\") }\nif err := db.Ping(); err != nil { return false, err }","typeGuard":"ok, err := store.FindAgentByPublicKey(pubKey)\nif err != nil {\n    // fail closed, but log the wrapped cause\n    log.Errorf(\"peer validation failed: %v\", err)\n    return false\n}","tryCatchPattern":"ok, err := store.FindAgentByPublicKey(pub)\nif err != nil {\n    if isLockTimeout(err) { return store.FindAgentByPublicKey(pub) } // one retry\n    return false, err\n}","preventionTips":["Configure SQLite busy_timeout so short write locks do not fail reads","Ensure migrations run before the noise listener starts accepting peers","Fail closed on error but alert loudly — this gate controls peer authentication","Monitor DB lock latency to catch contention early"],"tags":["go","database","sqlite","authentication"],"backgroundTag":"database-query-failed","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"}