NTP Check
Boot-time clock-drift verification against an external NTP / NTS-KE reference. The audit chain's monotonicCounter orders events deterministically even when the wall clock jumps, but recordedAt is the human-readable timestamp auditors rely on — a clock silently off by hours (container with no RTC sync, NTP daemon stopped) makes the audit trail misleading without ever surfacing as an error.
What this does: sends a single SNTPv4 query over UDP/123 (RFC 5905) to one or more configured servers, computes drift as serverTransmit - localMidpoint (round-trip-corrected), returns the drift in milliseconds. Falls through a server list in order; the first success wins.
What this does NOT do: continuous synchronization (the host OS's NTP daemon does that), authenticated NTP / NTS / autokey (the external reference is trust-on-first-query), or median-of-N server reconciliation (single-shot only).
Policy thresholds at boot — wired into b.db.init:
drift |x| < warnMs (5 min default) → info, continue drift |x| in [warnMs, fatalMs) → warning, continue drift |x| >= fatalMs (1 hr default) → refuse to boot (BLAMEJS_NTP_STRICT=1) NTP unreachable → warning, continue (network may not allow UDP/123 outbound)
b.ntpCheck.monitor runs the same check on a recurring interval after boot and emits system.ntp.checked / system.ntp.drift_warn / system.ntp.drift_fatal / system.ntp.unreachable audit events plus an ntp.drift_ms observability gauge — so silent clock drift mid-flight surfaces in the same evidence stream as boot drift.
b.ntpCheck.setThresholds(opts) #
{
warnMs: 300000, // ms; absolute drift at-or-above this logs warn
fatalMs: 3600000, // ms; absolute drift at-or-above this refuses boot
}
Override the warn / fatal drift thresholds applied by bootCheck and monitor. Validates that both values are non-negative finite numbers and that warnMs <= fatalMs (a fatal floor below the warning threshold would mean every warning is also fatal — likely a typo). Throws TypeError on bad shapes and RangeError on the ordering invariant.
b.ntpCheck.setThresholds({
warnMs: 60000,
fatalMs: 600000,
});
var t = b.ntpCheck.getThresholds();
// → { warnMs: 60000, fatalMs: 600000 }
b.ntpCheck.getThresholds() #
Read the currently-effective warn / fatal drift thresholds. Returns a fresh object so mutating the result doesn't accidentally rewrite framework state.
var t = b.ntpCheck.getThresholds();
// → { warnMs: 300000, fatalMs: 3600000 }
b.ntpCheck.querySingle(server, opts) #
{
port: 123, // UDP port (almost always 123)
timeoutMs: 3000, // single-query timeout
}
Send one SNTPv4 query to a named server over UDP/123 and resolve with { driftMs, serverTimeMs, server } (round-trip-corrected drift). Rejects with { code, message } where code is one of ntp/timeout (no reply within timeoutMs), ntp/refused (DNS / connection error), ntp/bad-reply (packet too short), or ntp/unsynchronized (Stratum-16 peer with zero transmit timestamp). IPv4 / IPv6 socket family is selected from the host literal so an fd00::... server doesn't fail with EINVAL.
b.ntpCheck.querySingle("time.cloudflare.com", { timeoutMs: 2000 })
.then(function (r) { console.log("drift", r.driftMs, "ms"); })
.catch(function (e) { console.error("ntp", e.code, e.message); });
b.ntpCheck.checkDrift(opts) #
{
servers: ["time.cloudflare.com", "pool.ntp.org"],
port: 123,
timeoutMs: 3000,
}
Walk a server list in order; resolve with the first successful drift measurement ({ driftMs, serverTimeMs, server }). When every server in the list fails, resolves with { driftMs: null, error } so the caller — typically bootCheck — can decide whether unreachable NTP is fatal or a soft warning.
var result = await b.ntpCheck.checkDrift({
servers: ["time.cloudflare.com", "pool.ntp.org"],
});
// → { driftMs: 12, serverTimeMs: 1714694400000, server: "time.cloudflare.com" }
b.ntpCheck.bootCheck(opts) #
{
servers: ["time.cloudflare.com", "pool.ntp.org"],
port: 123,
timeoutMs: 3000,
driftWarnMs: 300000, // override registered warn threshold
driftFatalMs: 3600000, // override registered fatal threshold
}
Boot-time clock-drift check that integrates with the framework's logging policy. Resolves with { ok, severity, driftMs, server, message } where severity is info / warning / fatal. The framework's b.db.init calls this and refuses to boot when ok === false and the operator has set BLAMEJS_NTP_STRICT=1. NTP unreachable returns severity: "warning" (network may not allow UDP/123 outbound) so the boot doesn't fail closed without operator intent.
var result = await b.ntpCheck.bootCheck({
servers: ["time.cloudflare.com"],
driftWarnMs: 60000,
driftFatalMs: 600000,
});
// → { ok: true, severity: "info", driftMs: 12,
// server: "time.cloudflare.com",
// message: "clock drift +12ms from time.cloudflare.com" }
b.ntpCheck.monitor(opts) #
{
intervalMs: 900000, // tick cadence
servers: ["time.cloudflare.com", "pool.ntp.org"],
driftWarnMs: 2000,
driftFatalMs: 30000,
audit: true, // emit audit events
onDrift: function (result) {}, // operator hook
}
Periodic drift monitor — runs bootCheck on a recurring interval and emits audit + observability events on threshold crossings. Returns a handle with .stop() for graceful shutdown. Audit emissions: system.ntp.checked on every tick, system.ntp.drift_warn and system.ntp.drift_fatal on threshold crossings, system.ntp.unreachable when every server in the list failed. Observability gauge ntp.drift_ms rides every successful check. The optional onDrift hook fires only when severity is warning or fatal, so operators can page on drift without inspecting every healthy tick.
var mon = b.ntpCheck.monitor({
intervalMs: 900000,
servers: ["time.cloudflare.com", "pool.ntp.org"],
driftWarnMs: 2000,
driftFatalMs: 30000,
onDrift: function (r) { console.warn("ntp drift", r.driftMs); },
});
await mon.stop();
Last updated 2026-08-08T16:39:49.652Z by seeder.