v0.2.0
[0.2.0] - 2026-09-04
Added
-
A keyless heartbeat, on by default.
reportKeylessStateused to go out
only when the app called it, which in practice is once at startup — so an
Electron app or a resident Node service reported itself once and then looked
dead to the dashboard for as long as it ran:last_seennever moved past
first_seen, and the reported app version froze at whatever shipped that day.checkOnLaunch()now starts a cadence (keylessHeartbeatMs, default 6h to
match the Worker's server-side gate on keyless writes);startKeylessHeartbeat()
is public for apps that never callcheckOnLaunch. Each tick beacons only
when the device is keyless — a licensed device sends nothing and reports
liveness through/validate— and the timer keeps running across that
boundary so a lapsed license resumes on its own.Nothing to change in your app.
reportKeylessStatestill debounces to one
request per 24h, so the cadence costs nothing extra on the wire. The timer is
unref'd on Node/Bun so a six-hour interval can't keep a one-shot script
alive, andstopKeylessHeartbeat()disposes it. Pass
keylessHeartbeatMs: nullto opt out. -
Device dimensions on the telemetry fields:
archandos_version.
Requests that already carry telemetry (activate,validate, the keyless
beacon) now also report the CPU architecture as a canonical token (arm64/
x86_64; anything else is omitted) and the host OS's release version reduced
to its dotted-numeric form (a Linux kernel release like6.8.0-45-genericis
sent as6.8.0; on macOS it is the marketing version fromsw_vers, e.g.
26.1, matching what the Swift and Rust SDKs report — not the Darwin kernel
version, which would place the same macOS release in a different bucket. The
sw_verscall runs at most once per process, and where it cannot run the
version is omitted rather than reported in the other vocabulary). Both fields
are optional on the wire and only sent where a host OS exists to ask: under
Node, Bun, Electron and Deno — on Deno the macOS version needs--allow-run,
and is omitted rather than prompting for it. Browser pages and edge isolates
send neither — there
is no reliable source there that isn't also a fingerprinting surface, and
this SDK keeps its stance of not reading one.device_classis never sent;
the backend derives it from the OS. No app code changes. -
Coarse device capacity on the same telemetry fields:
cpu_coresand
memory. Requests that already carry telemetry report which bucket the
machine falls in —1-2,3-4,5-8,9-16,17+cores, and<4GB,
4-8GB,8-16GB,16-32GB,32-64GB,64GB+of RAM. The exact core count
and the exact byte figure never leave the machine: a licensing SDK reporting
precise hardware specs reads as fingerprinting, so only the bucket is sent.
Bucket edges match the other SDKs exactly, so one machine population never
splits across two buckets. Both fields are optional on the wire and only sent
where a host OS exists to ask (Node, Bun, Electron, Deno). Browser pages and
edge isolates send neither:navigator.hardwareConcurrencyand
navigator.deviceMemoryare documented fingerprinting surfaces and this SDK
declines to read them, the same stance it takes onarch. No app code
changes.