{"record":{"id":"87ee0bdb42ae87b2","repo":"henrygd/beszel","slug":"invalid-signature-check-key-value","errorCode":null,"errorMessage":"invalid signature - check KEY value","messagePattern":"invalid signature - check KEY value","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agent/client.go","lineNumber":250,"sourceCode":"\t\tserverAddr := client.agent.connectionManager.serverOptions.Addr\n\t\t_, response.Port, _ = net.SplitHostPort(serverAddr)\n\t}\n\n\treturn client.sendResponse(response, requestID)\n}\n\n// verifySignature verifies the signature of the token using the public keys.\nfunc (client *WebSocketClient) verifySignature(signature []byte) (err error) {\n\tfor _, pubKey := range client.agent.keys {\n\t\tsig := ssh.Signature{\n\t\t\tFormat: pubKey.Type(),\n\t\t\tBlob:   signature,\n\t\t}\n\t\tif err = pubKey.Verify([]byte(client.token), &sig); err == nil {\n\t\t\treturn nil\n\t\t}\n\t}\n\treturn errors.New(\"invalid signature - check KEY value\")\n}\n\n// Close closes the WebSocket connection gracefully.\n// This method is safe to call multiple times.\nfunc (client *WebSocketClient) Close() {\n\tif client.Conn != nil {\n\t\t_ = client.Conn.WriteClose(1000, nil)\n\t}\n}\n\n// handleHubRequest routes the request to the appropriate handler using the handler registry.\nfunc (client *WebSocketClient) handleHubRequest(msg *common.HubRequest[cbor.RawMessage], requestID *uint32) error {\n\tctx := &HandlerContext{\n\t\tClient:       client,\n\t\tAgent:        client.agent,\n\t\tRequest:      msg,\n\t\tRequestID:    requestID,\n\t\tHubVerified:  client.hubVerified,","sourceCodeStart":232,"sourceCodeEnd":268,"githubUrl":"https://github.com/henrygd/beszel/blob/b38fb7dafa60812cc22e6a84ce313e94f1ce0a32/agent/client.go#L232-L268","documentation":"During the auth challenge, the hub sends a challenge that the client signs with its private key; verifySignature verifies the returned signature (or signs and verifies the token) against the KEY-derived public key. If no signature scheme succeeds (pubKey.Verify fails for the token), the library concludes the key/token combination is wrong and throws this error. It does not necessarily mean the wire data is corrupt — most often the configured KEY does not correspond to the token or the hub's expectation.","triggerScenarios":"handleAuthChallenge receives a challenge; the client signs the token with its private key but verification against pubKey fails — KEY is wrong, mismatched with TOKEN, the token is malformed/expired for signing purposes, or the signature algorithm/encoding differs from what the hub expects.","commonSituations":"Rotating TOKEN without updating KEY (or vice versa) on the hub; copy-paste truncating the base64 KEY; hub and agent using different key algorithms (ed25519 vs ecdsa); agent pointed at the wrong hub environment (staging key vs prod hub).","solutions":["Regenerate/confirm that KEY and TOKEN were issued together from the hub and paste the full untruncated values.","Compare the agent's KEY with the public key registered on the hub for this agent ID.","Verify the token is well-formed and not expired; re-fetch a fresh token from the hub.","Confirm both sides use the same signature algorithm and encoding; update agent or hub versions if they diverge."],"exampleFix":"// before\nHUB_URL=wss://hub.prod\nKEY=\"Kf2...\"   # key from staging\nTOKEN=\"eyJ...\"  # issued by prod hub\n// after\nHUB_URL=wss://hub.prod\nKEY=\"<prod key matching this TOKEN>\"\nTOKEN=\"eyJ...\"","handlingStrategy":"try-catch","validationCode":"// before connecting, sanity-check the key material\nkey, err := base64.StdEncoding.DecodeString(strings.TrimSpace(keyEnv))\nif err != nil {\n\treturn fmt.Errorf(\"KEY is not valid base64: %w\", err)\n}\nif len(key) != expectedKeyLen {\n\treturn fmt.Errorf(\"KEY length %d, expected %d — likely truncated\", len(key), expectedKeyLen)\n}","typeGuard":null,"tryCatchPattern":"err = verifySignature(client, challenge)\nif err != nil {\n\tif strings.Contains(err.Error(), \"invalid signature\") {\n\t\tlog.Error(\"auth failed: KEY does not match TOKEN/hub expectation; refetch credentials\")\n\t\t// do NOT blind-retry with the same key; rotate credentials first\n\t\treturn errAuthRetryableAfterRefresh\n\t}\n\treturn err\n}","preventionTips":["Issue KEY and TOKEN together from the hub and store/rotate them as a pair.","Paste keys in full; verify length and that no whitespace/newlines were introduced.","Pin and test against the same hub environment the credentials were issued for.","Keep agent and hub signature-algorithm versions in sync.","Log only the failure, never the key or token contents."],"tags":["authentication","signature","crypto","websocket"],"backgroundTag":"signature-verification-failed","analyzedSha":"b38fb7dafa60812cc22e6a84ce313e94f1ce0a32","analyzedAt":"2026-08-31T15:10:10.149Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}