{"record":{"id":"140dc71c30e239d4","repo":"Pumpkin-MC/Pumpkin","slug":"timestamp-drift-exceeded-drift-was-drift-s-max","errorCode":null,"errorMessage":"Timestamp drift exceeded: drift was {drift}s (max allowed is {MAX_CLOCK_DRIFT_SECS}s)","messagePattern":"Timestamp drift exceeded: drift was (.+?)s \\(max allowed is (.+?)s\\)","errorType":"error_code","errorClass":"TelemetryVerificationError","httpStatus":null,"severity":"warning","filePath":"crates/pumpkin/src/telemetry.rs","lineNumber":123,"sourceCode":"#[must_use]\npub fn sign_telemetry_payload(\n    signing_key: &SigningKey,\n    timestamp_str: &str,\n    body_bytes: &[u8],\n) -> (String, String) {\n    let signed_data = compute_signed_data(timestamp_str, body_bytes);\n    let signature = signing_key.sign(&signed_data);\n    let pubkey_hex = hex::encode(signing_key.verifying_key().to_bytes());\n    let sig_hex = hex::encode(signature.to_bytes());\n    (pubkey_hex, sig_hex)\n}\n\n/// Errors that can occur during telemetry request signature verification.\n#[derive(Debug, thiserror::Error, PartialEq, Eq)]\npub enum TelemetryVerificationError {\n    #[error(\"Invalid timestamp string format\")]\n    InvalidTimestamp,\n    #[error(\n        \"Timestamp drift exceeded: drift was {drift}s (max allowed is {MAX_CLOCK_DRIFT_SECS}s)\"\n    )]\n    ClockDriftExceeded { drift: u64 },\n    #[error(\"Invalid public key hex encoding\")]\n    InvalidPublicKeyHex,\n    #[error(\"Invalid public key bytes: {0}\")]\n    InvalidPublicKey(String),\n    #[error(\"Invalid signature hex encoding\")]\n    InvalidSignatureHex,\n    #[error(\"Invalid signature bytes: {0}\")]\n    InvalidSignature(String),\n    #[error(\"Signature verification failed: {0}\")]\n    VerificationFailed(String),\n}\n\n/// Verifies a signed telemetry request against an Ed25519 public key and timestamp.\n///\n/// Ensures clock drift between `current_time_secs` and `timestamp_str` does not exceed `±300` seconds.","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/Pumpkin-MC/Pumpkin/blob/8d4639e25a57c15e47448ec327c780d41bbf2356/crates/pumpkin/src/telemetry.rs#L105-L141","documentation":"TelemetryVerificationError::ClockDriftExceeded { drift } is returned during telemetry signature verification when the request's timestamp is valid but differs from the server's current time by more than MAX_CLOCK_DRIFT_SECS. The drift value in the error reports the actual observed difference. This replay-protection check rejects stale or future-dated requests even when their signature is otherwise valid.","triggerScenarios":"A signed telemetry request arrives with a timestamp older/newer than the allowed window: the client machine's clock is skewed, a request was queued/retried past the window, or a captured request is replayed later.","commonSituations":"Servers or clients with unsynchronized clocks (no NTP); long network queues or offline buffering of telemetry that is sent too late; container hosts whose clocks drift; deliberate replay attempts.","solutions":["Enable NTP/time sync on the client and server machines so their clocks agree","Regenerate and resend the request with a fresh timestamp instead of retrying a stale signed request","If offline buffering is intentional, flush telemetry before it exceeds the MAX_CLOCK_DRIFT_SECS window","Reduce clock skew in containers by syncing the host clock and avoiding paused VMs/snapshots with stale time"],"exampleFix":"// before\nlet ts = cached_timestamp; // minutes old, signature reuse\n// after\nlet ts = Utc::now(); // re-sign with fresh timestamp before sending\nlet sig = sign_request(&key, &body, &ts);","handlingStrategy":"retry","validationCode":"fn drift_ok(sent_at: chrono::DateTime<Utc>, max: u64) -> bool {\n    (Utc::now() - sent_at).num_seconds().unsigned_abs() <= max\n}\n// call before signing: assert!(drift_ok(now, MAX_CLOCK_DRIFT_SECS))","typeGuard":null,"tryCatchPattern":"match verify_signature(&req) {\n    Err(TelemetryVerificationError::ClockDriftExceeded { drift }) => {\n        info!(\"request drifted {drift}s; asking client to re-sign with fresh timestamp\");\n        retry_with_fresh_timestamp();\n    }\n    other => other?,\n}","preventionTips":["Run NTP time sync on all machines producing telemetry","Re-sign and resend with a fresh timestamp rather than retrying stale signed payloads","Flush buffered telemetry before it exceeds the allowed drift window","Keep container/VM hosts' clocks synchronized with the host machine"],"tags":["telemetry","signature-verification","clock-skew","replay-protection"],"backgroundTag":"request-timeout","analyzedSha":"8d4639e25a57c15e47448ec327c780d41bbf2356","analyzedAt":"2026-09-09T15:32:22.916Z","contentChangedAt":"2026-09-09T15:32:22.916Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}