@antondziuin/talos-sdk 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -6
- package/dist/index.cjs +212 -123
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -4
- package/dist/index.d.ts +9 -4
- package/dist/index.js +212 -123
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts","../src/errors.ts","../src/fingerprint.ts","../src/state.ts","../src/version.ts","../src/diagnostics.ts","../src/offline.ts","../src/talos-compact.ts"],"sourcesContent":["import { createHash, randomBytes } from \"node:crypto\";\nimport { createReadStream, readFileSync } from \"node:fs\";\nimport { open, rename, rm } from \"node:fs/promises\";\nimport os from \"node:os\";\nimport { type KeyObject } from \"node:crypto\";\n\nimport { TalosApiError, TalosError, TalosSignatureError } from \"./errors.js\";\nimport { collectFingerprint, type FingerprintData } from \"./fingerprint.js\";\nimport {\n StateStore,\n defaultStatePath,\n hashLicenseKey,\n stateKeyMaterial,\n type StoredBinding,\n} from \"./state.js\";\nimport { SDK_ID } from \"./version.js\";\nimport { diagnosticReport } from \"./diagnostics.js\";\nimport {\n generateMachineKey,\n signMachineDoc,\n LICENSE_FILE_KIND,\n RECEIPT_DOMAIN,\n REQUEST_DOMAIN,\n type MachineKey,\n type SignedDoc,\n} from \"./offline.js\";\nimport {\n b64urlEncode,\n publicKeyFromBase64,\n verifyCachedToken,\n verifyKeyset,\n verifyManifest,\n verifyToken,\n} from \"./talos-compact.js\";\n\n/**\n * Tolerance for a slow or slightly-off clock, in seconds. Matches the token\n * verifier's; the point is to be forgiving about drift while still catching a\n * clock that has been moved.\n */\nconst CLOCK_SKEW_SECONDS = 60;\n\n/**\n * Fallback check-in cadence, used only until the first heartbeat reports the\n * policy's own. Matches the server's `policies.heartbeat_interval_s` default.\n */\nconst DEFAULT_HEARTBEAT_SECONDS = 600;\n\n/**\n * What a response has to offer this SDK.\n *\n * `headers` and `body` are optional because a test double need not implement a\n * `ReadableStream`; `arrayBuffer` is not, because the artifact download has to\n * read bytes and an optional method there just moves the failure to run time —\n * which is exactly what this shape is being widened to prevent.\n */\nexport interface FetchResponseLike {\n status: number;\n text(): Promise<string>;\n arrayBuffer(): Promise<ArrayBuffer>;\n headers?: { get(name: string): string | null };\n /** A streaming body, when the implementation has one (the global `fetch`\n * does). Used to enforce the size cap without buffering first. */\n body?: { getReader(): { read(): Promise<{ done: boolean; value?: Uint8Array }>; cancel(): Promise<unknown> } } | null;\n}\n\n/** Minimal fetch signature (Node 18+ global fetch, or an injected one). */\nexport type FetchLike = (\n url: string,\n init: {\n method: string;\n headers: Record<string, string>;\n body?: string;\n /**\n * Aborts the request once {@link TalosOptions.httpTimeoutMs} elapses.\n *\n * Optional so an injected `fetch` that predates this field still satisfies\n * the type — but one that ignores the signal has no timeout, and the\n * licensing call it drops is on somebody's launch path.\n */\n signal?: AbortSignal;\n },\n) => Promise<FetchResponseLike>;\n\n/**\n * Default cap on a licensing call, in milliseconds.\n *\n * Ten seconds: long enough that a slow network is not mistaken for a dead one,\n * short enough that an application blocked on it still starts. Node's `fetch`\n * has no default at all, which is the state this replaces.\n */\nconst DEFAULT_HTTP_TIMEOUT_MS = 10_000;\n\nexport interface TalosOptions {\n /** The product's public token (`tpt_…`) from the portal. */\n productToken: string;\n /**\n * The product's **anchor** public key, base64 (from the portal's Integration\n * page). The rotation-safe root: the SDK fetches the anchor-signed keyset and\n * learns the current license/release keys from it, so rotating a signing key\n * never breaks a deployed app. Strongly recommended — pin this. If set,\n * {@link publicKey} is optional (a fallback for when the keyset is unavailable).\n */\n anchorPublicKey?: string;\n /**\n * The product's Ed25519 license public key, base64. A direct pin used to\n * verify activation/validation tokens when no keyset is loaded. Optional if\n * {@link anchorPublicKey} is set.\n */\n publicKey?: string;\n /**\n * The product's **release**-signing public key, base64. Direct pin used to\n * verify update manifests when no keyset is loaded. Falls back to\n * {@link publicKey} when omitted.\n */\n releasePublicKey?: string;\n /** Base URL of the Talos (or self-hosted) server. */\n serverUrl: string;\n /** Optional app version reported in telemetry/heartbeats. */\n appVersion?: string;\n /**\n * The name shown for this seat in the developer's portal. Defaults to\n * `os.hostname()`.\n *\n * Unlike the hostname the SDK folds into the fingerprint, this one is stored\n * and displayed as written — it is what a support agent reads when a customer\n * says \"release the one on my old laptop\", and without it every seat on a\n * licence is an indistinguishable hash prefix. Pass `\"\"` to send nothing if a\n * machine name would be more identifying than your users expect.\n */\n hostname?: string;\n /**\n * Optional fetch implementation (defaults to the global `fetch`).\n *\n * Used for **every** network call, artifact downloads included. It did not\n * used to be: `downloadUpdate` reached for `globalThis.fetch` directly, so a\n * proxy, a custom CA bundle, request logging or a test double applied to\n * every licensing call and was silently bypassed by the one call that pulls a\n * multi-megabyte binary and is likeliest to need it.\n */\n fetch?: FetchLike;\n /**\n * The update channel this install follows (`stable` by default, `beta` and\n * whatever else the product publishes).\n *\n * The default for {@link TalosClient.checkForUpdate}, and the channel a\n * heartbeat names when something is subscribed to `updateAvailable` — a beta\n * install has to ask about its own channel or it would be told about\n * stable's releases.\n */\n updateChannel?: string;\n /**\n * How long a licensing call may take before it is abandoned, in milliseconds.\n * Default 10000; `0` disables it.\n *\n * Node's `fetch` has **no** default timeout, so before this existed a server\n * that accepted the connection and never answered left `validate()` pending\n * forever — which in a desktop application is a splash screen that never goes\n * away, on the one code path a user cannot skip. Passing a custom `fetch` was\n * the only way to bound it, which is a lot of ceremony for one number.\n *\n * It does **not** apply to `downloadUpdate`. A total-duration cap on a\n * multi-megabyte artifact is not a safeguard, it is a size limit disguised as\n * one: the same 30 seconds that is generous for a licensing call fails every\n * download on a slow connection. The download is bounded by the manifest's\n * signed `size` instead, which is the bound that actually matters there.\n */\n httpTimeoutMs?: number;\n /**\n * Extra fingerprint components, merged over the ones the SDK collects.\n *\n * Node cannot read hardware identifiers on Windows or macOS without native\n * modules, so on those hosts the server falls back to exact matching and a\n * NIC change forces a re-activation. If your app can supply a stable\n * identifier — an Electron native module, or a value you already hold — pass\n * it here (e.g. `{ machine_guid: \"<value>\" }`) and the server can keep the\n * seat across hardware changes instead. Values are hashed like any other\n * component, so raw identifiers never leave the machine. See\n * `docs/FINGERPRINT.md` for the recognised ids and their weights.\n */\n fingerprintComponents?: Record<string, string>;\n /**\n * Where to keep this machine's activation across restarts. Defaults to a\n * per-product file under the OS user-data directory (see\n * {@link defaultStatePath}).\n */\n statePath?: string;\n /**\n * Persist the machine binding (default `true`).\n *\n * Turn it off only for a process that should not leave a licence on disk — a\n * test, or a short-lived worker. The cost is that every start must\n * `activate` again, which rotates the activation secret and is refused while\n * the tenant is suspended, so a long-lived desktop app wants this on.\n */\n persistState?: boolean;\n}\n\n/** One downloadable artifact of an available update (from a verified manifest). */\nexport interface UpdateArtifact {\n platform: string;\n arch: string;\n kind: string;\n filename: string;\n /** Expected size in bytes (from the signed manifest). */\n size: number;\n /** Expected SHA-256, lowercase hex (from the signed manifest). */\n sha256: string;\n /** Untrusted transport path; the downloaded bytes are verified against `sha256`. */\n downloadPath?: string;\n}\n\n/** What applying a patch has to produce. */\nexport interface UpdateDeltaTarget {\n /** The `kind` of the full artifact this rebuilds (`installer`, `archive`, …). */\n kind: string;\n filename: string;\n /** Expected size of the rebuilt file, in bytes (from the signed manifest). */\n size: number;\n /**\n * Expected SHA-256 of the rebuilt file, lowercase hex (from the signed\n * manifest). **Check it after applying** — see {@link verifyAppliedUpdate}.\n * Verifying only the patch leaves whatever comes out of the patcher\n * unverified, which is the whole thing the signed manifest exists to prevent.\n */\n sha256: string;\n}\n\n/**\n * A patch that turns one already-installed version into this release, from the\n * signed manifest.\n *\n * Talos hosts and signs patches; it does not produce or apply them. The diff\n * format belongs to whatever applies it (Velopack, Squirrel, MSIX, bsdiff,\n * `zstd --patch-from`), and this SDK deliberately does not run installers.\n */\nexport interface UpdateDelta {\n platform: string;\n arch: string;\n /** The installed version this patch applies to, exactly as published. */\n from: string;\n /** `release_seq` of the version it applies to (always below this release's). */\n fromSeq: number;\n filename: string;\n /** Expected size of the patch itself, in bytes. */\n size: number;\n /** Expected SHA-256 of the patch itself, lowercase hex. */\n sha256: string;\n target: UpdateDeltaTarget;\n /** Untrusted transport path; the bytes are verified against `sha256`. */\n downloadPath?: string;\n}\n\n/** A verified available update (its metadata all comes from the signed manifest). */\nexport interface UpdateInfo {\n version: string;\n releaseSeq: number;\n mandatory: boolean;\n notesUrl: string | null;\n artifacts: UpdateArtifact[];\n /**\n * Patches this release ships, if any. Never mixed into {@link artifacts}: a\n * patch is not something a host can install, and an SDK that picks \"the\n * artifact for this platform\" would hand one over as if it were a build.\n */\n deltas: UpdateDelta[];\n}\n\n/** Which patch to use, when a release ships more than one. */\nexport interface DeltaOptions {\n /**\n * The version currently installed. Defaults to `appVersion` from the client\n * options — the same value the update check reports.\n */\n from?: string;\n /**\n * Which shipped file to rebuild, when a release carries patches for several\n * (an installer and a portable archive, say). Defaults to the only one, and\n * is required when there is more than one.\n */\n targetKind?: string;\n}\n\n/**\n * How far a download has got, passed to the `onProgress` callback of\n * {@link TalosClient.downloadUpdate} and {@link TalosClient.downloadDelta}.\n *\n * `totalBytes` is the size the **signed manifest** names, not the server's\n * `content-length`. The download host is untrusted, and a bar driven by a header\n * it controls can be parked at 100% while bytes are still arriving. It is also\n * the cap the read is bounded by, so `bytesReceived` never exceeds it.\n */\nexport interface DownloadProgress {\n /** Bytes verified and written so far. */\n bytesReceived: number;\n /** Total size, in bytes, from the signed manifest. */\n totalBytes: number;\n}\n\n/** Called as an artifact download advances. */\nexport type DownloadProgressCallback = (progress: DownloadProgress) => void;\n\n/**\n * The licence transitions a host can subscribe to with {@link TalosClient.on}.\n *\n * Edges, not levels. `onState` already delivers the verdict on every check-in,\n * and an application that wants \"tell me when grace starts\" has to diff those\n * itself — getting that right (not re-warning every ten minutes for the whole\n * outage) is exactly the bookkeeping a library should do once instead of every\n * host doing it slightly wrong.\n */\nexport interface TalosEventMap {\n /** The licence stopped being usable; the payload says why. */\n licenseInvalid: LicenseState;\n /** Running on a cached verdict because the server could not be reached. */\n gracePeriodStarted: LicenseState;\n /** The offline window closed: the cached verdict may no longer be honoured. */\n gracePeriodExpired: LicenseState;\n /** The system clock moved back behind a signed server time already seen. */\n clockTamperDetected: LicenseState;\n /**\n * A release newer than this install is waiting, noticed by a heartbeat rather\n * than by being asked. The payload is the same verified {@link UpdateInfo}\n * {@link TalosClient.checkForUpdate} returns.\n */\n updateAvailable: UpdateInfo;\n}\n\nexport type TalosEventName = keyof TalosEventMap;\n\n/** Handler for a licence transition; receives the state that was adopted. */\nexport type LicenseListener = (state: LicenseState) => void;\n\n/** A held floating seat. Renew before {@link expiresAt} or it is reclaimed. */\nexport interface Lease {\n /** Server-side lease id (from the signed token). */\n id: string;\n /** This process's instance id. */\n instanceId: string;\n /** Unix seconds when the seat is reclaimed unless renewed. */\n expiresAt: number;\n /** Renew after this many seconds (server-chosen, ttl/3). */\n renewAfterSeconds: number;\n seatsInUse: number;\n maxSeats: number;\n}\n\nexport interface AcquireLeaseOptions {\n /**\n * Identifies this running process. Re-acquiring with the same value renews\n * instead of taking a second seat. Defaults to a random per-client id.\n */\n instanceId?: string;\n /** Auto-renew in the background (default true). Call {@link TalosClient.releaseLease} to stop. */\n autoRenew?: boolean;\n /** Called if a renewal fails and the seat is lost (e.g. revoked, server down). */\n onLeaseLost?: (reason: string) => void;\n}\n\nexport interface CheckInOptions {\n /** Called after every successful check-in, with the fresh verdict. */\n onState?: (state: LicenseState) => void;\n /**\n * Called when a check-in fails outright — the server answered with an error,\n * or the network is down *and* there is nothing left to fall back on.\n */\n onError?: (error: unknown) => void;\n}\n\nexport interface CheckForUpdateOptions {\n /** Currently installed version (informational). */\n currentVersion?: string;\n /**\n * Override the anti-downgrade floor for this call.\n *\n * Optional, and normally omitted: the SDK persists the highest `release_seq`\n * it has seen this install running and uses that. Pass one when the host\n * knows something the SDK cannot — a build installed by a package manager, or\n * a first run after adopting the SDK on an already-updated fleet.\n */\n currentReleaseSeq?: number;\n /** Update channel; defaults to `stable`. */\n channel?: string;\n}\n\n/** The current, cryptographically-verified license verdict. */\nexport class LicenseState {\n constructor(\n /**\n * `valid | expired | revoked | suspended | deactivated | machine_revoked |\n * invalid`, plus two verdicts only the client can reach:\n * `grace_expired` (offline past the licence's grace window) and\n * `clock_tampered` (the system clock moved back behind a signed server\n * time we had already seen).\n */\n readonly decision: string,\n /** Entitlement bag (server-defined). */\n readonly features: Record<string, unknown>,\n /** Business expiry (unix seconds), or null for perpetual. */\n readonly licenseExpiresAt: number | null,\n /**\n * True when this verdict came from the local cache rather than a live\n * server — the licence is being honoured on trust, inside the window the\n * policy allows.\n */\n readonly isOffline: boolean = false,\n /**\n * Unix seconds after which the app should validate again. Chosen by the\n * server and carried in a signed claim, so it cannot be lengthened in\n * transit.\n */\n readonly revalidateAfter: number | null = null,\n /** Unix seconds until which this verdict may be honoured offline. */\n readonly graceUntil: number | null = null,\n ) {}\n\n get isValid(): boolean {\n return this.decision === \"valid\";\n }\n\n isFeatureEnabled(name: string): boolean {\n const v = this.features[name];\n if (typeof v === \"boolean\") return v;\n if (typeof v === \"number\") return v !== 0;\n if (typeof v === \"string\") return v.length > 0;\n return false;\n }\n\n getEntitlement(name: string): unknown {\n return this.features[name];\n }\n}\n\n/**\n * Online-first Talos client. Every trust decision is a signed token verified\n * against the pinned product key, so a fake/MITM server cannot forge an\n * acceptable answer. One instance per product; holds the per-machine activation\n * secret after {@link TalosClient.activate}.\n */\n/**\n * How long an adopted keyset is trusted before a refresh is attempted.\n *\n * The upper bound on how long a revoked signing key keeps being accepted by an\n * install that stays online. A day is the trade: shorter multiplies load on a\n * public endpoint serving a document that changes about once a year, longer\n * widens the window in which a compromised key still works.\n */\nconst KEYSET_MAX_AGE_SECONDS = 24 * 60 * 60;\n\nexport class TalosClient {\n private readonly options: TalosOptions;\n private readonly anchorKey: KeyObject | null;\n private readonly publicKey: KeyObject | null;\n private readonly releaseKey: KeyObject | null;\n private readonly fingerprint: string;\n private readonly fp: FingerprintData;\n private readonly doFetch: FetchLike;\n\n // Learned from the anchor-signed keyset; take precedence over the direct pins.\n private keysetLicenseKeys: KeyObject[] = [];\n private keysetReleaseKeys: KeyObject[] = [];\n\n /**\n * SHA-256 of the key this machine activated with, or null.\n *\n * The key itself is deliberately not held: after activation the server\n * resolves the licence from the activation secret, so keeping the key would\n * mean a plaintext credential in memory and on disk for the sake of one\n * string comparison in `activate`.\n */\n private licenseKeyHash: string | null = null;\n private activationSecret: string | null = null;\n private machineId: string | null = null;\n private readonly store: StateStore | null;\n /** Everything persisted for this product; written back as one file. */\n private binding: StoredBinding | null = null;\n /**\n * True while the adopted keyset came off disk. One refresh per process\n * upgrades it, so a rotation is picked up, but a client that cannot reach the\n * server keeps verifying with what it already trusts.\n */\n private keysetFromCache = false;\n /**\n * The `iat` of the adopted keyset, in unix seconds, or 0 if unknown.\n *\n * Kept so a keyset can go stale. Without it the refresh happens once per\n * process, which is fine for a desktop app that restarts and useless for the\n * process this SDK is most often *in* — a server or an Electron main that runs\n * for weeks. A key revoked in the portal would never reach it.\n */\n private keysetIssuedAt = 0;\n /** The policy's heartbeat cadence, from the last heartbeat's signed claims. */\n private heartbeatInterval: number | null = null;\n private checkInTimer: ReturnType<typeof setTimeout> | null = null;\n /** Bumped on stop so an in-flight tick cannot re-arm a cancelled loop. */\n private checkInGeneration = 0;\n private lease: Lease | null = null;\n private renewTimer: ReturnType<typeof setTimeout> | null = null;\n /** Bumped on release so an in-flight renewal cannot resurrect the seat. */\n private leaseGeneration = 0;\n /** Default instance id for floating leases: one per client object. */\n private readonly instanceId: string = b64urlEncode(randomBytes(9));\n current: LicenseState | null = null;\n\n /**\n * Return a local JSON support report without credentials, identifiers or\n * exception text. Pass a caught error for classification. Makes no requests\n * and does not change licensing state; `decision` is the last known result.\n */\n createDiagnosticReport(error?: unknown): string {\n return diagnosticReport(this.isActivated, this.options.anchorPublicKey != null && this.options.anchorPublicKey !== \"\", this.current, error);\n }\n private readonly listeners = new Map<TalosEventName, Set<(payload: never) => void>>();\n /** The newest release this client has already announced. */\n private announcedReleaseSeq = 0;\n\n constructor(options: TalosOptions) {\n if (!options.productToken) throw new TalosError(\"productToken required\");\n if (!options.serverUrl) throw new TalosError(\"serverUrl required\");\n if (!options.anchorPublicKey && !options.publicKey) {\n throw new TalosError(\"anchorPublicKey or publicKey required\");\n }\n this.options = options;\n this.anchorKey = options.anchorPublicKey\n ? publicKeyFromBase64(options.anchorPublicKey)\n : null;\n this.publicKey = options.publicKey ? publicKeyFromBase64(options.publicKey) : null;\n // Direct release pin falls back to the license pin (keyset overrides both).\n this.releaseKey = options.releasePublicKey\n ? publicKeyFromBase64(options.releasePublicKey)\n : this.publicKey;\n this.fp = collectFingerprint(options.productToken, options.fingerprintComponents);\n this.fingerprint = this.fp.composite;\n this.doFetch = options.fetch ?? (globalThis.fetch as unknown as FetchLike);\n if (!this.doFetch) throw new TalosError(\"no fetch available (need Node 18+ or options.fetch)\");\n\n // Restore the machine binding synchronously, so a client constructed at\n // startup is already usable: `validate()` works with no `activate()` call\n // and no user prompt for the licence key.\n this.store =\n options.persistState === false\n ? null\n : new StateStore(\n options.statePath ?? defaultStatePath(options.productToken),\n options.productToken,\n stateKeyMaterial(this.fp),\n );\n const stored = this.store?.load() ?? null;\n if (stored) {\n this.binding = stored;\n // Empty means absent, and the distinction is load-bearing now that a\n // binding can exist without an activation: these fields are stored as\n // strings and default to `\"\"`, an air-gapped install has no secret and no\n // id, and an install that has just handed its seat back has neither\n // either. Kept as `\"\"` they are truthy against a `!== null` test, and\n // `isActivated` would answer yes for an install holding nothing.\n this.licenseKeyHash = stored.licenseKeyHash || null;\n this.activationSecret = stored.activationSecret || null;\n this.machineId = stored.machineId || null;\n // Adopt the cached keyset before anything asks for a verification key.\n // Without it an anchor-only client has nothing to verify a cached\n // decision with, and the offline path is unreachable in exactly the\n // situation it exists for.\n if (stored.keyset) {\n try {\n this.adoptKeyset(stored.keyset);\n this.keysetFromCache = this.keysetLicenseKeys.length > 0;\n } catch {\n // A keyset that no longer verifies under the pinned anchor is not a\n // startup failure: fall back to fetching one.\n }\n }\n this.promoteDownloadedRelease();\n }\n }\n\n /**\n * A release that was downloaded and is now the version running: promote its\n * `release_seq` to the anti-downgrade mark.\n *\n * This is the whole of \"the SDK knows what is installed\". The SDK downloads\n * and verifies bytes; the host installs them, its own way, usually across a\n * restart — so installation is not an event the SDK can observe. What it can\n * observe is the result, because `appVersion` is the host saying which\n * version it is. When that equals the version whose bytes were fetched, the\n * download became an install.\n *\n * Only forward, and that is the point: yanking a release or lowering its\n * rollout stops it being *offered*, and must not walk an install that already\n * applied it back onto an older build.\n */\n private promoteDownloadedRelease(): void {\n const pending = this.binding?.pendingRelease;\n if (!pending || pending.version !== this.options.appVersion) return;\n const mark = Math.max(this.binding?.releaseSeqHighWater ?? 0, pending.releaseSeq);\n this.persist({ releaseSeqHighWater: mark, pendingRelease: undefined });\n }\n\n /**\n * The `release_seq` no update may go at or below.\n *\n * The host's explicit value wins when it gives one — it knows things the SDK\n * cannot, such as a build installed by a package manager — and the persisted\n * mark is the default that makes anti-downgrade work for the hosts that give\n * nothing, which is every example and most applications.\n */\n private releaseFloor(explicit?: number): number | undefined {\n return explicit ?? this.binding?.releaseSeqHighWater;\n }\n\n /**\n * Whether this machine already holds an activation — restored from disk on\n * construction, so this is the startup question: prompt for a licence key, or\n * go straight to {@link validate}?\n */\n /**\n * Whether this install holds a licence for this machine.\n *\n * True for an online activation (the activation secret is held) **and** for an\n * air-gapped install that has loaded a licence file. Widened rather than\n * split, because what a host does with this is decide whether to ask the user\n * for a licence key — and an air-gapped machine that already holds a valid\n * file must not be asked for one it has no way to redeem.\n */\n get isActivated(): boolean {\n return this.activationSecret !== null || this.binding?.offlineToken != null;\n }\n\n /** The server's id for this machine, once activated. */\n get currentMachineId(): string | null {\n return this.machineId;\n }\n\n isFeatureEnabled(name: string): boolean {\n return this.current?.isFeatureEnabled(name) ?? false;\n }\n\n /**\n * Fetch and verify the product's keyset (signed by the pinned anchor) and\n * adopt its current license/release keys. Called automatically during\n * {@link activate}; call it again after a server-side key rotation to keep\n * verifying without any config change. No-op if no `anchorPublicKey` is pinned.\n */\n async refreshKeyset(): Promise<void> {\n if (!this.anchorKey) return;\n const body = await this.post(\"/v1/client/keyset\", {\n product_token: this.options.productToken,\n });\n this.adoptKeyset(body[\"keyset\"]);\n this.keysetFromCache = false;\n if (typeof body[\"keyset\"] === \"string\") this.persist({ keyset: body[\"keyset\"] });\n }\n\n /**\n * Make sure a verification key exists before a response has to be verified.\n *\n * A restored binding goes straight to `validate`, skipping `activate` — which\n * is the only place the keyset used to be adopted. An anchor-only client (the\n * recommended pinning, and the one that survives key rotation) therefore had\n * nothing to verify with on the app's *second* launch, and every call failed\n * with \"keyset unavailable\". Only persistence made that reachable: before it,\n * `activate` always ran first.\n *\n * The keyset is also cached on disk, so the *first* call of a launch with no\n * network can still verify a cached decision. A cached one is refreshed once\n * per process so a rotation is picked up, but a client that cannot reach the\n * server keeps verifying with what it already trusts.\n */\n private async ensureKeyset(): Promise<void> {\n if (!this.anchorKey) return;\n if (this.keysetLicenseKeys.length > 0 && !this.keysetFromCache && !this.keysetIsStale()) {\n return;\n }\n try {\n await this.refreshKeyset();\n } catch (e) {\n // A refresh that cannot reach the server must not break a client that\n // already has keys it trusts — that would make a network problem look\n // like a licensing failure, which is the one thing the whole grace design\n // exists to avoid. With nothing adopted at all there is no fallback, so\n // the failure is the caller's to see.\n if (this.keysetLicenseKeys.length === 0) throw e;\n }\n }\n\n /**\n * Whether the adopted keyset is old enough to be worth re-fetching.\n *\n * This is what makes revoking a compromised signing key mean anything to an\n * install: a revoked key is omitted from the keyset, so a client only stops\n * trusting it once it fetches a newer one. Refreshing only on the first call\n * of a process bounds that by \"next restart\", which for a long-running host is\n * not a bound at all.\n *\n * A day, not an hour: the endpoint is public and unauthenticated, and every\n * install on earth hitting it hourly is a self-inflicted load pattern for a\n * document that changes about once a year. The honest description of the\n * guarantee is in `docs/SECURITY.md` — revocation is bounded, not instant.\n */\n private keysetIsStale(): boolean {\n if (!this.keysetIssuedAt) return true;\n return Date.now() / 1000 - this.keysetIssuedAt > KEYSET_MAX_AGE_SECONDS;\n }\n\n /** Verify a keyset token against the anchor and adopt its keys. */\n private adoptKeyset(token: unknown): void {\n if (!this.anchorKey || typeof token !== \"string\") return;\n const claims = verifyKeyset(this.anchorKey, this.options.productToken, token);\n // From the signed claims, not from a local clock reading taken when the\n // fetch happened: a client whose clock is wrong would otherwise decide its\n // keyset is fresh forever, or refetch on every call.\n const iat = claims[\"iat\"];\n this.keysetIssuedAt = typeof iat === \"number\" ? iat : 0;\n const licenseKeys: KeyObject[] = [];\n const releaseKeys: KeyObject[] = [];\n for (const entry of asArray(claims[\"keys\"])) {\n const o = entry as Record<string, unknown>;\n if (typeof o[\"pk\"] !== \"string\") continue;\n const key = publicKeyFromBase64(o[\"pk\"]);\n if (o[\"purpose\"] === \"license\") licenseKeys.push(key);\n else if (o[\"purpose\"] === \"release\") releaseKeys.push(key);\n }\n this.keysetLicenseKeys = licenseKeys;\n this.keysetReleaseKeys = releaseKeys;\n }\n\n /** Keys to verify license tokens with: the keyset if loaded, else the direct pin. */\n private licenseVerifyKeys(): KeyObject[] {\n if (this.keysetLicenseKeys.length) return this.keysetLicenseKeys;\n if (this.publicKey) return [this.publicKey];\n throw new TalosSignatureError(\"no license verification key (keyset unavailable)\");\n }\n\n /** Keys to verify manifests with: the keyset if loaded, else the direct pin. */\n private releaseVerifyKeys(): KeyObject[] {\n if (this.keysetReleaseKeys.length) return this.keysetReleaseKeys;\n if (this.releaseKey) return [this.releaseKey];\n throw new TalosSignatureError(\"no release verification key (keyset unavailable)\");\n }\n\n /**\n * Ensure this machine is activated on `licenseKey`, and return the verified\n * state. Safe to call on every start.\n *\n * When a binding for the same key is already held, this **re-validates**\n * instead of re-activating. Same one round trip, three different outcomes:\n * the activation secret is not rotated (so a second copy of the app on the\n * same machine does not knock the first one out), no seat accounting is\n * touched, and — because `activate` is the only client route refused for a\n * suspended tenant — the developer's own paying users keep working while\n * their vendor's account is suspended, which is what \"graduated suspension\"\n * was supposed to mean.\n *\n * It falls back to a real activation when the stored binding is no longer\n * usable: the seat was released from the portal, or the secret was rotated\n * elsewhere.\n */\n async activate(licenseKey: string): Promise<LicenseState> {\n if (this.isActivated && this.licenseKeyHash === hashLicenseKey(licenseKey)) {\n try {\n const state = await this.validate();\n // `deactivated` means the developer released the seat; taking it again\n // is exactly what re-activating is for. Every other verdict —\n // including `machine_revoked` — is the server's answer, and re-\n // activating would only replace it with a less specific error.\n if (state.decision !== \"deactivated\") return state;\n } catch (e) {\n // 401: the secret was rotated elsewhere. 404: the machine (or licence)\n // is gone. Anything else is a real failure and must not be papered\n // over by silently spending a seat.\n const stale =\n e instanceof TalosApiError && (e.statusCode === 401 || e.statusCode === 404);\n if (!stale) throw e;\n }\n this.forgetBinding();\n }\n const nonce = newNonce();\n const body = await this.post(\"/v1/client/activate\", {\n product_token: this.options.productToken,\n license_key: licenseKey,\n fingerprint: { composite: this.fingerprint, components: this.fp.components },\n nonce,\n hostname: this.reportedHostname(),\n os: `${os.type()} ${os.release()}`,\n arch: os.arch(),\n app_version: this.options.appVersion,\n sdk_version: SDK_ID,\n });\n\n // Adopt the keyset delivered with activation, then verify the activation\n // token against the current license keys (so a rotated key still works).\n this.adoptKeyset(body[\"keyset\"]);\n const claims = verifyToken(\n this.licenseVerifyKeys(),\n \"activation\",\n this.options.productToken,\n nonce,\n body[\"activation_token\"] as string,\n );\n // The key is not kept. From here the activation secret identifies both the\n // machine and its licence, so nothing needs the key again until somebody\n // types a different one.\n this.licenseKeyHash = hashLicenseKey(licenseKey);\n this.activationSecret = body[\"activation_secret\"] as string;\n this.machineId = (body[\"machine_id\"] as string) ?? null;\n this.binding = {\n licenseKeyHash: this.licenseKeyHash,\n activationSecret: this.activationSecret,\n machineId: this.machineId ?? \"\",\n fingerprint: this.fingerprint,\n // A fresh activation invalidates any cached verdict for the previous\n // binding; keep only the keyset, which is per product, not per machine.\n keyset: typeof body[\"keyset\"] === \"string\" ? (body[\"keyset\"] as string) : this.binding?.keyset,\n timeHighWater: this.binding?.timeHighWater,\n // The machine's own identity survives an online activation, because it is\n // an identity and not a verdict: an install that reaches the network once\n // must not have to repeat the whole air-gapped ceremony with its vendor\n // the next time it is unplugged. The clock mark survives for the same\n // reason a mark exists at all — it only moves forward.\n machineKey: this.binding?.machineKey,\n machinePublicKey: this.binding?.machinePublicKey,\n offlineSeen: this.binding?.offlineSeen,\n // The offline *file* does not: a live activation supersedes it, and\n // keeping it would leave two answers to \"what licenses this machine\".\n };\n this.advanceClock(claims);\n this.store?.save(this.binding);\n return this.adopt(stateFromClaims(claims, \"valid\", false));\n }\n\n /**\n * This machine's own keypair, generated once and kept in the state file.\n *\n * The air-gapped counterpart of the activation secret: a machine that cannot\n * reach a server is never issued one, so it makes an identity of its own and\n * the vendor binds the file to it.\n */\n private machineKey(): MachineKey {\n const held = this.binding?.machineKey;\n const pub = this.binding?.machinePublicKey;\n if (held && pub) return { privateKey: held, publicKey: pub };\n const key = generateMachineKey();\n this.binding = {\n ...this.bindingBase(),\n machineKey: key.privateKey,\n machinePublicKey: key.publicKey,\n };\n this.store?.save(this.binding);\n return key;\n }\n\n /**\n * The binding as it stands, or an empty one to build on.\n *\n * An air-gapped install reaches its first write with nothing stored — no\n * licence key, no activation secret, no machine id — so there has to be\n * something for the offline fields to be added to.\n */\n private bindingBase(): StoredBinding {\n return (\n this.binding ?? {\n licenseKeyHash: \"\",\n activationSecret: \"\",\n machineId: \"\",\n fingerprint: this.fingerprint,\n }\n );\n }\n\n /**\n * Write the request an air-gapped machine hands to its vendor.\n *\n * Save the returned object as `talos-request.json`, carry it to whoever sold\n * the licence, and they upload it in the portal. What comes back is a\n * `license.talos` for {@link loadOfflineLicense} — and only for *this*\n * machine: the request carries the fingerprint and the public half of a\n * keypair generated here, and the file is bound to both.\n *\n * Calling it twice produces two requests under the same identity. The vendor\n * can issue against either; the second file supersedes the first without\n * spending a second seat.\n *\n * ```ts\n * writeFileSync(\"talos-request.json\", JSON.stringify(talos.createOfflineRequest(), null, 2));\n * ```\n *\n * @param licenseKey Optional. The operator picks the licence in the portal, so\n * it is not needed — but when it is present the server checks it against the\n * licence being issued against, which catches issuing from the wrong tab.\n */\n createOfflineRequest(licenseKey?: string): SignedDoc {\n const key = this.machineKey();\n return signMachineDoc(key, REQUEST_DOMAIN, {\n product_token: this.options.productToken,\n license_key: licenseKey,\n fingerprint: { composite: this.fingerprint, components: this.fp.components },\n machine_pubkey: key.publicKey,\n nonce: newNonce(),\n hostname: this.reportedHostname(),\n os: `${os.type()} ${os.release()}`,\n arch: os.arch(),\n app_version: this.options.appVersion,\n sdk_version: SDK_ID,\n created_at: nowSeconds(),\n });\n }\n\n /**\n * Adopt an air-gapped licence file issued for this machine.\n *\n * Everything is verified locally, because there is nothing to ask: the\n * anchor-signed keyset travels inside the file, the verdict is signed by a key\n * that keyset lists, and the file names both halves of this machine's identity\n * — its fingerprint and the public key it generated. The verdict is then kept,\n * so later starts are licensed with no further ceremony.\n *\n * **Throws** only for a file that is not a licence: unreadable, not one of\n * ours, addressed to another product, or a broken signature. A file that *is*\n * one but cannot license this machine right now comes back as a verdict —\n * `machine_mismatch` for another machine's file, `offline_file_expired` for\n * one past its horizon, `expired` for a licence that has ended, and\n * `clock_tampered` for a clock wound back behind what this install has already\n * seen. Those are things the user can be told and can act on, and turning\n * them into exceptions would make the launch path throw for a reason nobody\n * can catch usefully.\n */\n async loadOfflineLicense(pathOrBuffer: string | Buffer | Uint8Array): Promise<LicenseState> {\n const raw =\n typeof pathOrBuffer === \"string\" ? readFileSync(pathOrBuffer) : Buffer.from(pathOrBuffer);\n let doc: Record<string, unknown>;\n try {\n doc = JSON.parse(raw.toString(\"utf8\")) as Record<string, unknown>;\n } catch {\n throw new TalosError(\"not a Talos offline licence file\");\n }\n if (doc[\"kind\"] !== LICENSE_FILE_KIND || typeof doc[\"token\"] !== \"string\") {\n throw new TalosError(\"not a Talos offline licence file\");\n }\n // The keyset is inside the file because an air-gapped machine has nowhere\n // to fetch one from. It is adopted only if the pinned anchor signs it, so a\n // file carrying somebody else's keyset changes nothing about which keys\n // this client trusts.\n if (typeof doc[\"keyset\"] === \"string\") this.adoptKeyset(doc[\"keyset\"]);\n return this.adoptOfflineToken(doc[\"token\"], doc[\"keyset\"]);\n }\n\n /**\n * Hand this machine's air-gapped seat back.\n *\n * Save the returned object as `talos-deactivation.json` and send it to the\n * vendor; they upload it in the portal and the seat returns to the pool.\n * Signed by the same key the licence file is bound to, which is what makes it\n * worth anything to a server that has never spoken to this machine.\n *\n * **The licence is dropped here, before the document is returned** — so an\n * application that writes the file, or loses it, or never sends it has still\n * stopped using the seat it is giving up. A receipt proves intent, not\n * destruction: the vendor's copy of the file cannot be recalled, and if this\n * machine is later restored from a snapshot it keeps working until that file\n * expires. That is the accepted cost, and it is why the horizon is short.\n *\n * Throws when this install holds no offline licence — there is nothing to hand\n * back, and a document saying otherwise would be a lie the vendor acts on.\n */\n createOfflineDeactivation(): SignedDoc {\n const token = this.binding?.offlineToken;\n const key = this.binding?.machineKey;\n const pub = this.binding?.machinePublicKey;\n if (!token || !key || !pub) {\n throw new TalosError(\"no offline licence to hand back\");\n }\n // Read what the file says about itself before dropping it: the receipt has\n // to name the file and the machine, and after `forget` there is nothing to\n // read it from.\n let claims: Record<string, unknown>;\n try {\n claims = verifyCachedToken(\n this.licenseVerifyKeys(),\n \"offline\",\n this.options.productToken,\n token,\n );\n } catch {\n throw new TalosError(\"the stored offline licence no longer verifies\");\n }\n\n // Drop it first. The order is the point: see above.\n this.binding = { ...this.bindingBase(), offlineToken: undefined };\n this.current = null;\n this.store?.save(this.binding);\n\n return signMachineDoc({ privateKey: key, publicKey: pub }, RECEIPT_DOMAIN, {\n fid: claims[\"fid\"],\n mid: claims[\"mid\"],\n fph: this.fingerprint,\n local_time: nowSeconds(),\n });\n }\n\n /**\n * Verify the offline file against this machine and this clock, and adopt the\n * verdict. Runs on load and again on every {@link validate}, because the two\n * things that can change afterwards — the clock and the calendar — are\n * exactly what it checks.\n */\n private adoptOfflineToken(token: string, keyset?: unknown): LicenseState {\n // `verifyCachedToken` rather than `verifyToken`: it checks the signature,\n // `typ`, `aud` and `nbf`, and leaves `exp` and the nonce to the caller. Both\n // omissions are right here. The nonce belonged to a request document that\n // may be weeks in the past and is not kept — it is the *server* that needs\n // it, where it is unique per account and stops a captured request minting a\n // second file. And `exp` is a verdict rather than an exception, below.\n const claims = verifyCachedToken(\n this.licenseVerifyKeys(),\n \"offline\",\n this.options.productToken,\n token,\n );\n\n const mismatch = (): LicenseState =>\n this.adopt(new LicenseState(\"machine_mismatch\", {}, null, true, null, null));\n // Both halves, exactly. The fingerprint is what stops the whole state\n // directory being copied to another computer, and the key is what stops the\n // file alone being worth anything — neither is redundant, and accepting\n // either on its own would open the case the other one closes.\n if (typeof claims[\"mpk\"] !== \"string\" || claims[\"mpk\"] !== this.binding?.machinePublicKey) {\n return mismatch();\n }\n if (claims[\"fph\"] !== this.fingerprint) return mismatch();\n\n const now = nowSeconds();\n // Both marks are lower bounds on \"now\" that this install has already\n // observed: one signed by the server, one its own. A clock behind either\n // has moved backwards.\n const mark = Math.max(this.binding?.timeHighWater ?? 0, this.binding?.offlineSeen ?? 0);\n if (mark > 0 && now + CLOCK_SKEW_SECONDS < mark) {\n return this.adopt(new LicenseState(\"clock_tampered\", {}, null, true, null, null));\n }\n\n const lic = (claims[\"lic\"] ?? {}) as Record<string, unknown>;\n const features = (lic[\"features\"] ?? {}) as Record<string, unknown>;\n const licExp = num(lic[\"lic_exp\"]);\n const exp = num(claims[\"exp\"]);\n\n // Persist before deciding: the file is this machine's, so it is worth\n // keeping even when today's answer is \"expired\" — the licence may be\n // renewed and a new file issued, and in the meantime the app can say which\n // file it is holding.\n this.machineId = typeof claims[\"mid\"] === \"string\" ? claims[\"mid\"] : this.machineId;\n const base = this.bindingBase();\n this.binding = {\n ...base,\n machineId: this.machineId ?? base.machineId,\n offlineToken: token,\n offlineSeen: Math.max(base.offlineSeen ?? 0, now),\n ...(typeof keyset === \"string\" ? { keyset } : {}),\n };\n this.advanceClock(claims);\n this.store?.save(this.binding);\n\n // The file's own horizon, which is the whole enforcement: there is no\n // server to re-ask, so this deadline is the only thing that ever ends it.\n // Distinct from `expired`, which is the *licence* ending — the remedies are\n // different (a new file versus a renewal) and the app should be able to say\n // which one to ask for.\n if (exp === null || now > exp) {\n return this.adopt(\n new LicenseState(\"offline_file_expired\", features, licExp, true, null, exp),\n );\n }\n if (licExp !== null && now > licExp) {\n return this.adopt(new LicenseState(\"expired\", features, licExp, true, null, exp));\n }\n return this.adopt(new LicenseState(\"valid\", features, licExp, true, null, exp));\n }\n\n /**\n * The heartbeat cadence the server's policy asks for, in seconds, taken from\n * the last heartbeat's **signed** claims — so it cannot be lengthened in\n * transit by anyone wanting a fleet to check in less often.\n *\n * `null` until the first heartbeat of this process.\n */\n get heartbeatIntervalSeconds(): number | null {\n return this.heartbeatInterval;\n }\n\n /**\n * Subscribe to a licence transition. Returns a function that unsubscribes.\n *\n * ```ts\n * const off = talos.on(\"gracePeriodStarted\", (s) => showOfflineBanner(s.graceUntil));\n * ```\n *\n * Fires from every path that adopts a verdict — `activate`, `validate`,\n * `heartbeat`, and the offline fallback — not only from the background loop,\n * so a host that runs its own timer gets the same transitions. The listener\n * runs synchronously inside that call; a listener that throws is ignored (see\n * {@link emit}).\n *\n * `updateAvailable` is the one event that is not a licence transition: it\n * carries an {@link UpdateInfo}, and subscribing to it is what makes the\n * heartbeat ask the server whether a release is waiting.\n */\n on<E extends TalosEventName>(\n event: E,\n listener: (payload: TalosEventMap[E]) => void,\n ): () => void {\n let set = this.listeners.get(event);\n if (!set) {\n set = new Set();\n this.listeners.set(event, set);\n }\n // The registry is one map for five payload types; `never` is the only\n // parameter every listener is assignable to, and the cast back at emit is\n // where the type is put on again.\n const fn = listener as (payload: never) => void;\n set.add(fn);\n return () => {\n set.delete(fn);\n };\n }\n\n /**\n * Adopt a verdict as the current one, announcing the transition it makes.\n *\n * The single funnel every path goes through, which is what makes the events\n * trustworthy: a verdict reached anywhere else would be a transition nobody\n * hears about.\n */\n private adopt(state: LicenseState): LicenseState {\n const event = transitionEvent(this.current, state);\n this.current = state;\n if (event) this.emit(event, state);\n return state;\n }\n\n private emit<E extends TalosEventName>(event: E, payload: TalosEventMap[E]): void {\n for (const listener of this.listeners.get(event) ?? []) {\n try {\n (listener as (p: TalosEventMap[E]) => void)(payload);\n } catch {\n // The verdict is already decided, and this is being raised from inside\n // `validate`, whose own catch treats a throw as an unreachable server.\n // A host's broken banner code must not turn a live \"revoked\" answer\n // into offline grace.\n }\n }\n }\n\n /**\n * Run the check-ins the server's policy asks for, in the background.\n *\n * Two knobs have existed on the wire since Phase 1 and reached no client:\n * `heartbeat_interval_s` (how often an install reports in — the telemetry\n * rate, and the upper bound on how long a revocation takes to arrive) and\n * `reval_after` (when the entitlement bag and the offline cache should be\n * refreshed). Both are signed. This loop is what makes them a lever the\n * developer can actually pull: change the policy, and deployed apps follow.\n *\n * One timer serves both. Each tick re-validates if the signed `reval_after`\n * has passed, and otherwise sends a heartbeat — which is also what renews the\n * cached verdict that {@link validate} falls back on, so an app that runs\n * this stays inside its grace window without the host writing any scheduling\n * of its own.\n *\n * Opt-in, and idempotent: calling it again replaces the running loop.\n */\n startCheckIns(opts: CheckInOptions = {}): void {\n this.stopCheckIns();\n const generation = ++this.checkInGeneration;\n const tick = async (): Promise<void> => {\n try {\n const due = this.current?.revalidateAfter;\n const state =\n due === null || due === undefined || nowSeconds() >= due\n ? await this.validate()\n : await this.heartbeat();\n if (generation !== this.checkInGeneration) return;\n opts.onState?.(state);\n } catch (e) {\n if (generation !== this.checkInGeneration) return;\n opts.onError?.(e);\n }\n // Re-arm even after a failure: a network outage must not silently end\n // the loop, or the app would never notice the connection coming back.\n if (generation === this.checkInGeneration) schedule();\n };\n const schedule = (): void => {\n const secs = Math.max(60, this.heartbeatInterval ?? DEFAULT_HEARTBEAT_SECONDS);\n this.checkInTimer = setTimeout(() => void tick(), secs * 1000);\n this.checkInTimer.unref?.();\n };\n schedule();\n }\n\n /** Stop the background check-ins started by {@link startCheckIns}. */\n stopCheckIns(): void {\n this.checkInGeneration += 1;\n if (this.checkInTimer) {\n clearTimeout(this.checkInTimer);\n this.checkInTimer = null;\n }\n }\n\n /** Drop the machine binding, in memory and on disk. */\n private forgetBinding(): void {\n this.licenseKeyHash = null;\n this.activationSecret = null;\n this.machineId = null;\n this.current = null;\n this.binding = null;\n this.store?.clear();\n }\n\n /** Merge fields into the persisted binding and write it back. */\n private persist(patch: Partial<StoredBinding>): void {\n if (!this.store || !this.binding) return;\n this.binding = { ...this.binding, ...patch };\n this.store.save(this.binding);\n }\n\n /**\n * Advance the server-time high-water mark from a signed claim.\n *\n * Only ever forward: this is the mark that keeps offline grace bounded when\n * the user owns the clock. `iat` is used when a token carries no explicit\n * `server_time` (activation does not) — both are the server's clock, signed.\n */\n private advanceClock(claims: Record<string, unknown>): void {\n const seen = num(claims[\"server_time\"]) ?? num(claims[\"iat\"]);\n if (seen === null) return;\n const current = this.binding?.timeHighWater ?? 0;\n if (seen > current) {\n if (this.binding) this.binding.timeHighWater = seen;\n }\n }\n\n async validate(): Promise<LicenseState> {\n // An air-gapped install has no server to ask and no secret to ask with. Its\n // file *is* the verdict — re-checked here rather than cached, because the\n // two things that can have changed since it was loaded are precisely what\n // that check is about: the clock and the calendar.\n if (!this.activationSecret && this.binding?.offlineToken) {\n return this.adoptOfflineToken(this.binding.offlineToken);\n }\n this.requireActivated();\n try {\n await this.ensureKeyset();\n const nonce = newNonce();\n const body = await this.post(\"/v1/client/validate\", {\n product_token: this.options.productToken,\n fingerprint: { composite: this.fingerprint, components: this.fp.components },\n activation_secret: this.activationSecret,\n nonce,\n // Sent here as well as on activation and heartbeat, because check-ins\n // are opt-in and this call is not: an application that validates on\n // launch and never calls `startCheckIns` would otherwise show, forever,\n // whatever version it activated on.\n app_version: this.options.appVersion,\n sdk_version: SDK_ID,\n });\n const token = body[\"validation_token\"] as string;\n const claims = verifyToken(\n this.licenseVerifyKeys(),\n \"validation\",\n this.options.productToken,\n nonce,\n token,\n );\n const decision =\n typeof claims[\"decision\"] === \"string\" ? (claims[\"decision\"] as string) : \"invalid\";\n this.advanceClock(claims);\n this.persist({ validation: token, timeHighWater: this.binding?.timeHighWater });\n return this.adopt(stateFromClaims(claims, decision, false));\n } catch (e) {\n // An HTTP status is an *answer* — \"revoked\", \"no such machine\", \"tenant\n // suspended\". Honouring a cached verdict over one would turn every\n // negative answer into 72 hours of grace, which is the opposite of what\n // grace is for. Only the absence of an answer falls back: a dead network,\n // or a response that fails verification (a garbled or forged reply tells\n // us nothing, so per the trust model it is treated as unreachable, never\n // as valid).\n if (e instanceof TalosApiError) throw e;\n const offline = this.offlineDecision();\n if (!offline) throw e;\n return this.adopt(offline);\n }\n }\n\n /**\n * The verdict from the local cache, or null when there is nothing usable.\n *\n * This is the whole offline story, and every check here is load-bearing:\n *\n * - the token is re-verified under the pinned key, so a cache edited on disk\n * is worthless;\n * - `fph` must be this machine, so copying a state file to a second computer\n * and pulling the network cable does not licence it;\n * - `grace_until` — the policy's `offline_grace_hours`, signed — bounds how\n * long a verdict may outlive its issuance;\n * - the licence's own expiry is re-checked, because a cached \"valid\" must not\n * outlive the subscription it was issued under;\n * - and the clock is checked against a mark that only moves forward, since\n * every deadline above is evaluated against a clock the user controls.\n */\n private offlineDecision(): LicenseState | null {\n const token = this.binding?.validation;\n if (!token) return null;\n const now = nowSeconds();\n\n const highWater = this.binding?.timeHighWater ?? 0;\n if (highWater > 0 && now + CLOCK_SKEW_SECONDS < highWater) {\n // Distinct from \"expired\": nothing has run out, the machine is lying\n // about the time. Say so, rather than reporting a licence problem the\n // user cannot act on.\n return new LicenseState(\"clock_tampered\", {}, null, true, null, null);\n }\n\n let claims: Record<string, unknown>;\n try {\n claims = verifyCachedToken(\n this.licenseVerifyKeys(),\n \"validation\",\n this.options.productToken,\n token,\n );\n } catch {\n return null;\n }\n if (claims[\"fph\"] !== this.fingerprint) return null;\n\n const graceUntil = num(claims[\"grace_until\"]);\n const cached = stateFromClaims(\n claims,\n typeof claims[\"decision\"] === \"string\" ? (claims[\"decision\"] as string) : \"invalid\",\n true,\n );\n if (graceUntil === null || now > graceUntil) {\n return new LicenseState(\n \"grace_expired\",\n cached.features,\n cached.licenseExpiresAt,\n true,\n cached.revalidateAfter,\n graceUntil,\n );\n }\n if (cached.licenseExpiresAt !== null && now > cached.licenseExpiresAt) {\n return new LicenseState(\n \"expired\",\n cached.features,\n cached.licenseExpiresAt,\n true,\n cached.revalidateAfter,\n graceUntil,\n );\n }\n return cached;\n }\n\n async deactivate(): Promise<void> {\n this.requireActivated();\n await this.post(\"/v1/client/deactivate\", {\n product_token: this.options.productToken,\n fingerprint: { composite: this.fingerprint, components: this.fp.components },\n activation_secret: this.activationSecret,\n });\n this.forgetBinding();\n }\n\n /**\n * Send a heartbeat: refreshes this machine's last-seen state, feeds the\n * developer's monitoring (version adoption / active installs), and returns the\n * current signed decision — so a revocation reaches the app within one\n * heartbeat interval. Call it periodically while the app runs.\n *\n * The response is a signed `typ=heartbeat` token, so a MITM cannot forge the\n * status or lengthen the interval.\n */\n /**\n * The machine name to report, or `undefined` to report none.\n *\n * An explicit `\"\"` means \"do not send\": omitting the field leaves whatever\n * the server already holds, because the server coalesces, so sending an empty\n * string would not clear it either — suppression has to happen here.\n */\n private reportedHostname(): string | undefined {\n const h = this.options.hostname ?? os.hostname();\n return h === \"\" ? undefined : h;\n }\n\n async heartbeat(): Promise<LicenseState> {\n this.requireActivated();\n await this.ensureKeyset();\n const nonce = newNonce();\n const body = await this.post(\"/v1/client/heartbeat\", {\n product_token: this.options.productToken,\n fingerprint: { composite: this.fingerprint, components: this.fp.components },\n activation_secret: this.activationSecret,\n nonce,\n app_version: this.options.appVersion,\n os: `${os.type()} ${os.release()}`,\n arch: os.arch(),\n sdk_version: SDK_ID,\n // Naming the channel is what asks the server for the update hint, and it\n // costs two queries there — so it is asked for only when somebody is\n // listening for the answer.\n update_channel: this.listeners.get(\"updateAvailable\")?.size\n ? this.updateChannel()\n : undefined,\n });\n const claims = verifyToken(\n this.licenseVerifyKeys(),\n \"heartbeat\",\n this.options.productToken,\n nonce,\n body[\"heartbeat_token\"] as string,\n );\n const decision =\n typeof claims[\"decision\"] === \"string\" ? (claims[\"decision\"] as string) : \"invalid\";\n // The cadence comes from the SIGNED claims, never the response envelope:\n // the whole point of signing it is that nobody in the middle can tell a\n // fleet to check in less often.\n const interval = num(claims[\"heartbeat_interval_s\"]);\n if (interval !== null && interval > 0) this.heartbeatInterval = interval;\n // Every signed server clock we see pushes the high-water mark forward, not\n // just validation's — a heartbeat is the most frequent call there is.\n this.advanceClock(claims);\n this.persist({ timeHighWater: this.binding?.timeHighWater });\n // A heartbeat carries the decision but not the entitlement bag; keep the\n // features from the last activate/validate.\n const state = this.adopt(\n new LicenseState(\n decision,\n this.current?.features ?? {},\n this.current?.licenseExpiresAt ?? null,\n false,\n this.current?.revalidateAfter ?? null,\n this.current?.graceUntil ?? null,\n ),\n );\n await this.announceUpdate(claims);\n return state;\n }\n\n /** The channel this install follows; `stable` unless set. */\n private updateChannel(): string {\n return this.options.updateChannel || \"stable\";\n }\n\n /**\n * Act on the heartbeat's `upd` hint: confirm it with a real update check and\n * emit `updateAvailable` for a release not announced yet.\n *\n * The hint is read from the **signed** claims, not the response envelope, and\n * it is only ever a prompt to go and look: the check applies this install's\n * anti-downgrade floor and verifies a manifest, neither of which the hint\n * does. A hint that turns out to be wrong costs one request and emits\n * nothing.\n *\n * A failing check is swallowed. The heartbeat it rode in on has already\n * succeeded, and a licensing call must not start failing because the update\n * service is having a bad day.\n */\n private async announceUpdate(claims: Record<string, unknown>): Promise<void> {\n if (!this.listeners.get(\"updateAvailable\")?.size) return;\n if (claims[\"upd\"] !== true) return;\n let info: UpdateInfo | null;\n try {\n info = await this.checkForUpdate();\n } catch {\n return;\n }\n if (!info || info.releaseSeq <= this.announcedReleaseSeq) return;\n this.announcedReleaseSeq = info.releaseSeq;\n this.emit(\"updateAvailable\", info);\n }\n\n /**\n * Acquire a floating seat (concurrent-use license). The server arbitrates the\n * cap atomically, so this either returns a seat or throws\n * {@link TalosApiError} with `seat_limit_reached` (HTTP 409).\n *\n * By default the seat is auto-renewed in the background at the server-chosen\n * interval; a lost renewal invokes `onLeaseLost`. Always\n * {@link releaseLease} on clean exit — a crash simply lets the seat expire.\n */\n async acquireLease(opts: AcquireLeaseOptions = {}): Promise<Lease> {\n this.requireActivated();\n await this.ensureKeyset();\n const instanceId = opts.instanceId ?? this.instanceId;\n // Snapshot the generation: if releaseLease() runs while this request is in\n // flight, we must not adopt the seat we just took (and should hand it back).\n const generation = this.leaseGeneration;\n const nonce = newNonce();\n const body = await this.post(\"/v1/client/leases\", {\n product_token: this.options.productToken,\n fingerprint: { composite: this.fingerprint, components: this.fp.components },\n activation_secret: this.activationSecret,\n instance_id: instanceId,\n nonce,\n });\n\n // The lease token is signed: a MITM cannot fabricate a seat.\n const claims = verifyToken(\n this.licenseVerifyKeys(),\n \"lease\",\n this.options.productToken,\n nonce,\n body[\"lease_token\"] as string,\n );\n\n // The signed grant must be for the instance and machine we asked about —\n // otherwise a server could hand every process one shared seat's token.\n if (claims[\"inst\"] !== instanceId || claims[\"fph\"] !== this.fingerprint) {\n throw new TalosSignatureError(\"lease token does not match this instance\");\n }\n\n // Seat facts come from the SIGNED claims, never the unsigned envelope, so a\n // MITM cannot misreport occupancy. `renew_after_s` is only a scheduling\n // hint; the budget is derived from the token's own clock (`exp` vs\n // `server_time`) so a wrong local clock cannot collapse the renew loop.\n const expiresAt = typeof claims[\"exp\"] === \"number\" ? (claims[\"exp\"] as number) : 0;\n const serverNow =\n typeof claims[\"server_time\"] === \"number\"\n ? (claims[\"server_time\"] as number)\n : Math.floor(Date.now() / 1000);\n const budget = Math.max(1, expiresAt - serverNow);\n const hinted = Number(body[\"renew_after_s\"] ?? 0);\n const lease: Lease = {\n id: String(claims[\"lease\"] ?? \"\"),\n instanceId,\n expiresAt,\n renewAfterSeconds:\n hinted > 0 && hinted <= budget ? hinted : Math.max(1, Math.floor(budget / 3)),\n seatsInUse: Number(claims[\"seats_in_use\"] ?? 0),\n maxSeats: Number(claims[\"max_seats\"] ?? 0),\n };\n\n if (generation !== this.leaseGeneration) {\n // Released while this was in flight: hand the seat straight back instead\n // of holding it until the TTL expires, and never re-adopt it locally.\n void this.releaseInstance(instanceId).catch(() => {});\n return lease;\n }\n this.lease = lease;\n\n if (opts.autoRenew !== false) this.scheduleRenew(opts);\n return lease;\n }\n\n /** Best-effort DELETE of one instance's seat (used for stale-renewal cleanup). */\n private async releaseInstance(instanceId: string): Promise<void> {\n if (!this.activationSecret) return;\n await this.request(\"DELETE\", \"/v1/client/leases\", {\n product_token: this.options.productToken,\n fingerprint: { composite: this.fingerprint, components: this.fp.components },\n activation_secret: this.activationSecret,\n instance_id: instanceId,\n });\n }\n\n /** The currently held floating seat, if any. */\n get currentLease(): Lease | null {\n return this.lease;\n }\n\n /** Release the floating seat (clean exit) and stop auto-renewal. */\n async releaseLease(): Promise<void> {\n // Bump the generation FIRST: an already in-flight renewal will see that it\n // is stale and neither re-adopt the seat nor re-arm the timer. Without this\n // a renewal racing the release resurrects the lease for the process\n // lifetime — the seat would stay held long after the app \"released\" it.\n this.leaseGeneration += 1;\n this.stopRenew();\n const lease = this.lease;\n this.lease = null;\n if (!lease) return;\n await this.releaseInstance(lease.instanceId);\n }\n\n private scheduleRenew(opts: AcquireLeaseOptions): void {\n this.stopRenew();\n const generation = this.leaseGeneration;\n const instanceId = this.lease?.instanceId;\n const delay = Math.max(1, this.lease?.renewAfterSeconds ?? 200) * 1000;\n this.renewTimer = setTimeout(() => {\n void (async () => {\n try {\n await this.acquireLease({ ...opts, instanceId, autoRenew: false });\n // Released (or re-acquired) while we were awaiting → do not re-adopt.\n if (generation !== this.leaseGeneration) return;\n this.scheduleRenew(opts);\n } catch (e) {\n if (generation !== this.leaseGeneration) return;\n this.lease = null;\n opts.onLeaseLost?.(e instanceof Error ? e.message : \"lease renewal failed\");\n }\n })();\n }, delay);\n // Never hold the event loop open for a background renewal.\n this.renewTimer.unref?.();\n }\n\n private stopRenew(): void {\n if (this.renewTimer) {\n clearTimeout(this.renewTimer);\n this.renewTimer = null;\n }\n }\n\n /**\n * Ask whether a newer release is available. Returns a verified {@link UpdateInfo}\n * (all metadata comes from the manifest signed by the pinned release key), or\n * `null` when up to date.\n *\n * Enforces anti-downgrade: a manifest whose `release_seq` is at or below the\n * floor is rejected as no update. The floor is the persisted high-water mark\n * — the highest `release_seq` this install has been seen running — unless\n * {@link CheckForUpdateOptions.currentReleaseSeq} overrides it.\n */\n async checkForUpdate(opts: CheckForUpdateOptions = {}): Promise<UpdateInfo | null> {\n this.requireActivated();\n await this.ensureKeyset();\n const body = await this.post(\"/v1/client/updates/check\", {\n product_token: this.options.productToken,\n fingerprint: { composite: this.fingerprint, components: this.fp.components },\n activation_secret: this.activationSecret,\n current_version: opts.currentVersion ?? this.options.appVersion,\n current_release_seq: this.releaseFloor(opts.currentReleaseSeq),\n channel: opts.channel ?? this.updateChannel(),\n });\n if (body[\"update_available\"] !== true) return null;\n\n const manifestToken = body[\"manifest\"];\n if (typeof manifestToken !== \"string\") {\n throw new TalosSignatureError(\"update available but no manifest\");\n }\n // Trust only the signed manifest for version/seq/artifact metadata.\n const m = verifyManifest(this.releaseVerifyKeys(), this.options.productToken, manifestToken);\n const releaseSeq = typeof m[\"release_seq\"] === \"number\" ? (m[\"release_seq\"] as number) : 0;\n const floor = this.releaseFloor(opts.currentReleaseSeq);\n if (floor !== undefined && releaseSeq <= floor) {\n // Anti-downgrade, checked here as well as on the server: the server's\n // answer is the one an attacker replaces, and this one is checked against\n // a mark on this machine and a manifest signed by the pinned key.\n return null;\n }\n\n // Untrusted transport map: platform/arch/kind → download path.\n const downloads = new Map<string, string>();\n for (const d of asArray(body[\"artifacts\"])) {\n const o = d as Record<string, unknown>;\n if (typeof o[\"download_path\"] === \"string\") {\n downloads.set(selKey(o[\"platform\"], o[\"arch\"], o[\"kind\"]), o[\"download_path\"]);\n }\n }\n\n const artifacts: UpdateArtifact[] = asArray(m[\"artifacts\"]).map((a) => {\n const o = a as Record<string, unknown>;\n const platform = String(o[\"platform\"] ?? \"\");\n const arch = String(o[\"arch\"] ?? \"\");\n const kind = String(o[\"kind\"] ?? \"\");\n return {\n platform,\n arch,\n kind,\n filename: String(o[\"filename\"] ?? \"\"),\n size: typeof o[\"size\"] === \"number\" ? (o[\"size\"] as number) : 0,\n sha256: String(o[\"sha256\"] ?? \"\"),\n downloadPath: downloads.get(selKey(platform, arch, kind)),\n };\n });\n\n // Patches, keyed by what distinguishes them from each other: the version\n // they apply to and the file they rebuild. A release that supports upgrading\n // from three versions and ships two files carries six.\n const deltaPaths = new Map<string, string>();\n for (const d of asArray(body[\"deltas\"])) {\n const o = d as Record<string, unknown>;\n if (typeof o[\"download_path\"] === \"string\") {\n deltaPaths.set(\n deltaKey(o[\"platform\"], o[\"arch\"], o[\"from\"], o[\"target_kind\"]),\n o[\"download_path\"],\n );\n }\n }\n\n const deltas: UpdateDelta[] = asArray(m[\"deltas\"]).map((d) => {\n const o = d as Record<string, unknown>;\n const t = (o[\"target\"] ?? {}) as Record<string, unknown>;\n const platform = String(o[\"platform\"] ?? \"\");\n const arch = String(o[\"arch\"] ?? \"\");\n const from = String(o[\"from\"] ?? \"\");\n const targetKind = String(t[\"kind\"] ?? \"\");\n return {\n platform,\n arch,\n from,\n fromSeq: typeof o[\"from_seq\"] === \"number\" ? (o[\"from_seq\"] as number) : 0,\n filename: String(o[\"filename\"] ?? \"\"),\n size: typeof o[\"size\"] === \"number\" ? (o[\"size\"] as number) : 0,\n sha256: String(o[\"sha256\"] ?? \"\"),\n target: {\n kind: targetKind,\n filename: String(t[\"filename\"] ?? \"\"),\n size: typeof t[\"size\"] === \"number\" ? (t[\"size\"] as number) : 0,\n sha256: String(t[\"sha256\"] ?? \"\"),\n },\n downloadPath: deltaPaths.get(deltaKey(platform, arch, from, targetKind)),\n };\n });\n\n return {\n version: String(m[\"version\"] ?? \"\"),\n releaseSeq,\n mandatory: m[\"mandatory\"] === true,\n notesUrl: typeof m[\"notes_url\"] === \"string\" ? (m[\"notes_url\"] as string) : null,\n artifacts,\n deltas,\n };\n }\n\n /**\n * The patch that upgrades this machine, or `null` if there is none.\n *\n * Matches on the *exact* published version string, not on version ordering: a\n * patch applies to the bytes it was built against, and \"close enough\" produces\n * a corrupt file rather than an error. So a machine whose installed version is\n * not one the release ships a patch from simply downloads the full build.\n */\n selectDelta(info: UpdateInfo, opts: DeltaOptions = {}): UpdateDelta | null {\n const from = opts.from ?? this.options.appVersion;\n if (!from) return null;\n const { platform, arch } = hostTarget();\n const matches = info.deltas.filter(\n (d) => d.platform === platform && d.arch === arch && d.from === from,\n );\n if (opts.targetKind) return matches.find((d) => d.target.kind === opts.targetKind) ?? null;\n // Ambiguity is refused rather than guessed. Picking one of two patches for\n // an installer and a portable archive would download megabytes and rebuild\n // the file the host was not updating.\n return matches.length === 1 ? matches[0]! : null;\n }\n\n /**\n * Download a patch and verify its bytes against the manifest's SHA-256, then\n * write it to `destPath`. Returns the patch, whose `target.sha256` is what the\n * rebuilt file must hash to.\n *\n * Does NOT apply it. Applying is the host's job — and so is checking the\n * result, which {@link verifyAppliedUpdate} does. A patch verified on the way\n * in and a result nobody checks is a signed chain that stops one step short of\n * the binary that runs.\n */\n async downloadDelta(\n info: UpdateInfo,\n destPath: string,\n opts: DeltaOptions & { delta?: UpdateDelta; onProgress?: DownloadProgressCallback } = {},\n ): Promise<UpdateDelta> {\n const chosen = opts.delta ?? this.selectDelta(info, opts);\n if (!chosen) throw new TalosError(\"no patch applies to this install\");\n await this.fetchVerified(\n chosen.downloadPath,\n chosen.size,\n chosen.sha256,\n destPath,\n opts.onProgress,\n );\n this.rememberDownloaded(info);\n return chosen;\n }\n\n /**\n * Download one artifact of an update and verify its bytes against the manifest\n * SHA-256, then write it to `destPath`. Returns the chosen artifact. Does NOT\n * apply or execute anything — verification and installation are the host app's\n * responsibility.\n *\n * `onProgress` is called as the bytes arrive, which is the only way a host can\n * show a bar: an installer takes minutes on a domestic connection, and a\n * promise that says nothing until it settles is indistinguishable from one that\n * has hung. Totals come from the signed manifest — see {@link DownloadProgress}.\n */\n async downloadUpdate(\n info: UpdateInfo,\n destPath: string,\n artifact?: UpdateArtifact,\n onProgress?: DownloadProgressCallback,\n ): Promise<UpdateArtifact> {\n this.requireActivated();\n const chosen = artifact ?? pickArtifact(info.artifacts);\n if (!chosen) throw new TalosError(\"no artifact matches this platform\");\n await this.fetchVerified(chosen.downloadPath, chosen.size, chosen.sha256, destPath, onProgress);\n this.rememberDownloaded(info);\n return chosen;\n }\n\n /**\n * Note which release's bytes are now on disk, so the next start can tell\n * whether they were installed.\n *\n * Written after the hash check, never before: a download that failed\n * verification is not a release this install is about to be running, and\n * recording it would let a corrupt or substituted file raise the\n * anti-downgrade floor past a good build.\n */\n private rememberDownloaded(info: UpdateInfo): void {\n if (info.releaseSeq <= (this.binding?.releaseSeqHighWater ?? 0)) return;\n this.persist({ pendingRelease: { version: info.version, releaseSeq: info.releaseSeq } });\n }\n\n /**\n * Fetch one file over the entitled download route and put it at `destPath`\n * only once its bytes match the size and hash the signed manifest names.\n * Shared by full builds and patches, which differ in what they are for and not\n * at all in how their bytes are checked.\n *\n * The bytes go straight to a `.talos-part` file beside the destination, hashed\n * as they arrive, and are renamed into place only once the hash matches. Two\n * things follow. A multi-gigabyte installer is never held in memory — the whole\n * body used to be buffered before it was checked, so the largest artifact a\n * product could ship was bounded by the smallest machine it ran on. And\n * `destPath` never holds unverified bytes, not even briefly: a download that\n * fails leaves whatever was there before rather than a truncated file a host\n * might run.\n */\n private async fetchVerified(\n downloadPath: string | undefined,\n size: number,\n sha256: string,\n destPath: string,\n onProgress?: DownloadProgressCallback,\n ): Promise<void> {\n if (!downloadPath) throw new TalosError(\"artifact has no download path\");\n const url = this.options.serverUrl.replace(/\\/+$/, \"\") + downloadPath;\n const res = await this.doFetch(url, {\n method: \"GET\",\n headers: {\n \"x-talos-product-token\": this.options.productToken,\n \"x-talos-activation-secret\": this.activationSecret!,\n \"x-talos-fingerprint\": this.fingerprint,\n },\n });\n if (res.status < 200 || res.status >= 300) throw new TalosApiError(res.status, undefined);\n\n // The download channel is untrusted (could stream an unbounded body to\n // exhaust memory before the hash check). Cap the read at the manifest's\n // trusted `size`, then verify the SHA-256 from the signed manifest.\n const contentLength = res.headers?.get(\"content-length\");\n if (contentLength && Number(contentLength) > size) {\n throw new TalosSignatureError(\"artifact exceeds manifest size\");\n }\n\n const partPath = destPath + \".talos-part\";\n const handle = await open(partPath, \"w\");\n const hash = createHash(\"sha256\");\n let received = 0;\n let closed = false;\n const take = async (chunk: Uint8Array): Promise<void> => {\n received += chunk.byteLength;\n if (received > size) throw new TalosSignatureError(\"artifact exceeds manifest size\");\n hash.update(chunk);\n await handle.write(chunk);\n onProgress?.({ bytesReceived: received, totalBytes: size });\n };\n try {\n const body = res.body;\n if (body) {\n const reader = body.getReader();\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done || !value) break;\n await take(value);\n }\n } catch (e) {\n await reader.cancel().catch(() => {});\n throw e;\n }\n } else {\n // Nothing to meter, so this one arrives whole. The real `fetch` always\n // has a stream; this is the path an injected implementation takes.\n await take(new Uint8Array(await res.arrayBuffer()));\n }\n\n if (hash.digest(\"hex\") !== sha256) {\n throw new TalosSignatureError(\"artifact hash mismatch (tampered or wrong bytes)\");\n }\n // Closed before the rename: Windows will not move a file that is open.\n await handle.close();\n closed = true;\n await rename(partPath, destPath);\n } catch (e) {\n if (!closed) await handle.close().catch(() => {});\n // A part file left behind is one the next download has to reason about,\n // and one nobody who finds it in an install directory can.\n await rm(partPath, { force: true }).catch(() => {});\n throw e;\n }\n }\n\n private requireActivated(): void {\n if (!this.activationSecret) {\n throw new TalosError(\"not activated: call activate() first\");\n }\n }\n\n private post(path: string, body: unknown): Promise<Record<string, unknown>> {\n return this.request(\"POST\", path, body);\n }\n\n /**\n * The abort signal for one licensing call, or `undefined` when disabled.\n *\n * A fresh one per request rather than one shared: `AbortSignal.timeout`\n * starts counting when it is created, so a signal made once in the\n * constructor would already have fired by the second call.\n */\n private timeoutSignal(): AbortSignal | undefined {\n const ms = this.options.httpTimeoutMs ?? DEFAULT_HTTP_TIMEOUT_MS;\n if (!ms || ms <= 0) return undefined;\n // Present since Node 17.3. Guarded rather than assumed because `fetch` may\n // be injected in a runtime that has one and not the other, and a missing\n // timeout is better than a crash on the launch path.\n return typeof AbortSignal.timeout === \"function\" ? AbortSignal.timeout(ms) : undefined;\n }\n\n private async request(\n method: string,\n path: string,\n body: unknown,\n ): Promise<Record<string, unknown>> {\n let res: FetchResponseLike;\n try {\n res = await this.doFetch(this.options.serverUrl.replace(/\\/+$/, \"\") + path, {\n method,\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(body),\n signal: this.timeoutSignal(),\n });\n } catch (e) {\n // An abort is a network failure, not a licensing verdict, and it has to\n // read as one: `validate` turns a network failure into the grace-period\n // path, and an unrecognised exception type there would take the\n // application down instead of letting it keep running offline.\n if (e instanceof Error && (e.name === \"TimeoutError\" || e.name === \"AbortError\")) {\n throw new TalosError(\n `request timed out after ${this.options.httpTimeoutMs ?? DEFAULT_HTTP_TIMEOUT_MS}ms`,\n );\n }\n throw e;\n }\n const text = await res.text();\n if (res.status < 200 || res.status >= 300) {\n let errorCode: string | undefined;\n try {\n errorCode = (JSON.parse(text) as { error?: string }).error;\n } catch {\n errorCode = undefined;\n }\n throw new TalosApiError(res.status, errorCode);\n }\n return text ? (JSON.parse(text) as Record<string, unknown>) : {};\n }\n}\n\nfunction stateFromClaims(\n claims: Record<string, unknown>,\n decision: string,\n isOffline: boolean,\n): LicenseState {\n const lic = (claims[\"lic\"] ?? {}) as Record<string, unknown>;\n const features = (lic[\"features\"] ?? {}) as Record<string, unknown>;\n const exp = num(lic[\"lic_exp\"]);\n // Validation carries absolute deadlines; activation carries the same policy\n // as hours beside its `iat`. Prefer the absolute form and derive the other,\n // so both tokens answer \"when should I come back?\" the same way.\n const iat = num(claims[\"iat\"]) ?? nowSeconds();\n const revalAfter = num(claims[\"reval_after\"]) ?? hoursAfter(iat, lic[\"reval_h\"]);\n const graceUntil = num(claims[\"grace_until\"]) ?? hoursAfter(iat, lic[\"grace_h\"]);\n return new LicenseState(decision, features, exp, isOffline, revalAfter, graceUntil);\n}\n\nfunction hoursAfter(base: number, hours: unknown): number | null {\n const h = num(hours);\n return h === null ? null : base + h * 3600;\n}\n\nfunction num(v: unknown): number | null {\n return typeof v === \"number\" && Number.isFinite(v) ? v : null;\n}\n\nfunction nowSeconds(): number {\n return Math.floor(Date.now() / 1000);\n}\n\n/**\n * Which transition, if any, moving from `prev` to `next` amounts to.\n *\n * At most one: the four are mutually exclusive by decision, and a repeat of the\n * verdict already held is not a transition at all — an application offline for a\n * week gets one `gracePeriodStarted`, not one per check-in. A *different* bad\n * verdict does fire again (`expired` after `suspended` is a different thing to\n * tell the user about), which is why this compares the decision and not just\n * validity.\n */\nfunction transitionEvent(prev: LicenseState | null, next: LicenseState): TalosEventName | null {\n if (next.decision === \"clock_tampered\") {\n return prev?.decision === \"clock_tampered\" ? null : \"clockTamperDetected\";\n }\n if (next.decision === \"grace_expired\") {\n return prev?.decision === \"grace_expired\" ? null : \"gracePeriodExpired\";\n }\n if (next.isValid) {\n // Grace begins when a cached verdict is what is keeping the application\n // running, not when the network drops: a failed call that falls back on\n // nothing throws, and never reaches here.\n if (!next.isOffline) return null;\n return prev?.isOffline && prev.isValid ? null : \"gracePeriodStarted\";\n }\n return prev?.decision === next.decision ? null : \"licenseInvalid\";\n}\n\nfunction newNonce(): string {\n return b64urlEncode(randomBytes(16));\n}\n\nfunction asArray(v: unknown): unknown[] {\n return Array.isArray(v) ? v : [];\n}\n\nfunction selKey(platform: unknown, arch: unknown, kind: unknown): string {\n return `${String(platform)}/${String(arch)}/${String(kind)}`;\n}\n\nfunction deltaKey(platform: unknown, arch: unknown, from: unknown, targetKind: unknown): string {\n return `${String(platform)}/${String(arch)}/${String(from)}/${String(targetKind)}`;\n}\n\n/** The platform and arch names this machine matches artifacts by. */\nfunction hostTarget(): { platform: string; arch: string } {\n const map: Record<string, string> = { win32: \"windows\", darwin: \"macos\", linux: \"linux\" };\n return { platform: map[os.platform()] ?? os.platform(), arch: os.arch() };\n}\n\n/** Best artifact for the current platform (installer preferred), or undefined. */\nfunction pickArtifact(artifacts: UpdateArtifact[]): UpdateArtifact | undefined {\n const { platform, arch } = hostTarget();\n const matches = artifacts.filter((a) => a.platform === platform && a.arch === arch);\n return matches.find((a) => a.kind === \"installer\") ?? matches[0];\n}\n\n/**\n * Check that applying a patch produced the file the signed manifest said it\n * would. Throws {@link TalosSignatureError} if not.\n *\n * This is the last link in the chain and the easiest one to leave out. The patch\n * itself was verified on the way in, but a patcher can fail halfway, apply to\n * the wrong base file, or be handed bytes from somewhere else entirely — and\n * every one of those produces a file that nothing has vouched for. Reads the\n * file in chunks, so a multi-gigabyte install is not brought into memory.\n *\n * Needs no licence and no activation: run it from the updater process, after the\n * application it belongs to has exited.\n */\nexport async function verifyAppliedUpdate(filePath: string, delta: UpdateDelta): Promise<void> {\n const hash = createHash(\"sha256\");\n const stream = createReadStream(filePath);\n await new Promise<void>((resolve, reject) => {\n stream.on(\"data\", (chunk) => hash.update(chunk));\n stream.on(\"error\", reject);\n stream.on(\"end\", () => resolve());\n });\n const actual = hash.digest(\"hex\");\n if (actual !== delta.target.sha256) {\n throw new TalosSignatureError(\n `applied update does not match the signed manifest: expected ${delta.target.sha256}, got ${actual}`,\n );\n }\n}\n","/** Base class for all Talos SDK errors. */\nexport class TalosError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"TalosError\";\n }\n}\n\n/**\n * A server response failed cryptographic verification against the pinned\n * product key (bad signature, wrong nonce/audience/type, or expired). Treated\n * like a network failure — never as \"valid\".\n */\nexport class TalosSignatureError extends TalosError {\n constructor(message: string) {\n super(message);\n this.name = \"TalosSignatureError\";\n }\n}\n\n/** A non-success HTTP response from the Talos runtime API. */\nexport class TalosApiError extends TalosError {\n readonly statusCode: number;\n readonly errorCode: string | undefined;\n\n constructor(statusCode: number, errorCode: string | undefined) {\n super(`talos api error ${statusCode}${errorCode ? `: ${errorCode}` : \"\"}`);\n this.name = \"TalosApiError\";\n this.statusCode = statusCode;\n this.errorCode = errorCode;\n }\n}\n","import { createHash, createHmac } from \"node:crypto\";\nimport { readFileSync, statSync } from \"node:fs\";\nimport os, { type NetworkInterfaceInfo } from \"node:os\";\n\n/** A machine fingerprint: the composite plus the per-component hashes. */\nexport interface FingerprintData {\n /** What the server matches on. Derived from {@link components}. */\n composite: string;\n /** `{component_id: hash}` — lets the server tolerate hardware drift. */\n components: Record<string, string>;\n}\n\n/**\n * Collect a machine fingerprint.\n *\n * Each component is HMAC'd with a product-scoped key before it leaves the\n * machine, so the server stores hashes and never the underlying identifiers,\n * and the same hardware yields different hashes for different products.\n *\n * **Coverage is thin here, and honestly so.** Node has no hardware access\n * without native modules, and this SDK ships with zero dependencies and never\n * spawns a subprocess. On Linux it can read `/etc/machine-id`, which is enough\n * for the server to recognise a machine across a hardware change. On Windows and\n * macOS it cannot reach anything comparable, so those hosts stay on exact\n * matching: change a NIC and the user re-activates. If your app can do better —\n * Electron with a native module, or a value you already have — pass\n * `fingerprintComponents` in the options and the server will use it. See\n * `docs/FINGERPRINT.md` for the recognised ids and their weights.\n *\n * None of this resists a determined attacker: these values are produced on the\n * client and can be forged. They exist to recognise honest hardware churn.\n */\nexport function collectFingerprint(\n productToken: string,\n extra?: Record<string, string>,\n): FingerprintData {\n const key = createHash(\"sha256\").update(\"talos-fp|\" + productToken).digest();\n\n const raw: Record<string, string | undefined> = {\n machine_guid: machineGuid(),\n smbios_uuid: readFile(\"/sys/class/dmi/id/product_uuid\"),\n disk_serial: readFile(\"/sys/class/dmi/id/board_serial\"),\n mac_set: macSet(),\n cpu: cpu(),\n mem_bucket: memBucket(),\n os_install: osInstall(),\n hostname: safe(() => os.hostname()),\n ...extra,\n };\n\n const components: Record<string, string> = {};\n for (const id of Object.keys(raw).sort()) {\n const value = raw[id];\n if (value === undefined || value === null || value.trim() === \"\") continue;\n // 16 bytes distinguishes components and keeps the request small; this is a\n // matching token, not a secret.\n components[id] = createHmac(\"sha256\", key)\n .update(id + \"=\" + value.trim())\n .digest(\"hex\")\n .slice(0, 32);\n }\n\n // The composite is a digest of the hashed components, so a host reporting the\n // same parts always yields the same composite and the server's exact-match\n // fast path keeps working.\n const canonical = Object.entries(components)\n .map(([k, v]) => `${k}=${v}`)\n .join(\"\\n\");\n return {\n composite: \"fp2:\" + createHash(\"sha256\").update(canonical).digest(\"hex\"),\n components,\n };\n}\n\n/** Per-install OS identity; survives hardware changes. Linux only here. */\nfunction machineGuid(): string | undefined {\n return readFile(\"/etc/machine-id\") ?? readFile(\"/var/lib/dbus/machine-id\");\n}\n\n/**\n * Sorted physical MACs. Internal interfaces are skipped: loopback and the\n * virtual adapters a VPN raises and drops would otherwise report drift on every\n * connection.\n */\nfunction macSet(): string | undefined {\n return safe(() => {\n const macs = Object.values(os.networkInterfaces())\n .flat()\n .filter((i): i is NetworkInterfaceInfo => Boolean(i) && !i!.internal)\n .map((i) => i.mac)\n .filter((m) => m && m !== \"00:00:00:00:00:00\")\n .filter((m, i, all) => all.indexOf(m) === i)\n .sort();\n return macs.length === 0 ? undefined : macs.join(\",\");\n });\n}\n\nfunction cpu(): string | undefined {\n return safe(() => {\n const cpus = os.cpus();\n if (!cpus || cpus.length === 0) return undefined;\n return `${cpus[0].model}|${cpus.length}`;\n });\n}\n\n/**\n * Installed RAM rounded down to a power of two GiB. Coarse on purpose: exact\n * byte counts shift when firmware reserves memory, which would look like drift\n * on a machine nobody touched.\n */\nfunction memBucket(): string | undefined {\n return safe(() => {\n const gib = Math.floor(os.totalmem() / 1024 ** 3);\n if (gib < 1) return undefined;\n let bucket = 1;\n while (bucket * 2 <= gib) bucket *= 2;\n return String(bucket);\n });\n}\n\n/** Install timestamp, approximated by the OS directory's creation time. */\nfunction osInstall(): string | undefined {\n const path = os.platform() === \"win32\" ? process.env.SystemRoot ?? \"C:\\\\Windows\" : \"/etc\";\n return safe(() => {\n const birth = statSync(path).birthtimeMs;\n // Filesystems that do not record a birth time report 0 (or the epoch);\n // a constant would be worse than nothing, since every host would agree.\n return birth > 0 ? String(Math.floor(birth / 1000)) : undefined;\n });\n}\n\nfunction readFile(path: string): string | undefined {\n return safe(() => {\n const text = readFileSync(path, \"utf8\").trim();\n return text.length === 0 ? undefined : text;\n });\n}\n\n/**\n * Run a probe, treating any failure as \"component unavailable\". A missing\n * component costs a little matching weight; an exception escaping into a\n * licensing call would cost the user their session.\n */\nfunction safe<T>(f: () => T | undefined): T | undefined {\n try {\n return f();\n } catch {\n return undefined;\n }\n}\n","import { createCipheriv, createDecipheriv, createHash, hkdfSync } from \"node:crypto\";\nimport { randomBytes } from \"node:crypto\";\nimport { chmodSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport os from \"node:os\";\n\nimport type { FingerprintData } from \"./fingerprint.js\";\n\n/**\n * The machine binding the server hands back at activation, held across process\n * restarts.\n *\n * Without this the binding died with the process and every application start\n * had to call `activate` again — which burns a round trip the protocol was\n * designed to avoid, rotates the activation secret out from under any other\n * running copy, and (because `activate` is the one client route refused for a\n * suspended tenant) turns the platform owner's deliberately graduated\n * suspension into a hard kill switch for the developer's own paying users.\n */\nexport interface StoredBinding {\n /**\n * SHA-256 of the licence key as it was typed, hex — **not** the key.\n *\n * The key itself used to live here, so that the next call could send it. It\n * no longer has to: the server resolves a licence from the activation secret,\n * which is the credential these calls authenticate with anyway. What was left\n * was a plaintext licence key in a file on every customer's machine, which is\n * a credential-shaped file and exactly what an infostealer collects.\n *\n * A hash is kept because one thing still needs it: `activate()` called twice\n * with the same key must validate rather than spend a second seat, and that\n * comparison is all the stored value was ever used for. The hash cannot be\n * replayed to the server — the server matches an HMAC under its own pepper —\n * so this is a value with exactly one use and no resale.\n *\n * Hashed as typed, without normalising, because the comparison it replaces\n * was a string equality on the same input: normalising here would change when\n * a re-activation is avoided, which is not what this change is about.\n */\n licenseKeyHash: string;\n activationSecret: string;\n machineId: string;\n /**\n * The fingerprint composite at activation time. Informational: it is *not*\n * required to match on restore, because a machine whose hardware drifted is\n * exactly the case the server's fuzzy matcher exists to resolve. Discarding\n * the binding here would re-activate the customer whose disk was replaced —\n * the support ticket the drift budget was built to prevent.\n */\n fingerprint: string;\n /**\n * The last anchor-signed keyset token.\n *\n * Cached because an anchor-only client — the pinning that survives key\n * rotation — otherwise has no verification key at all until it can reach the\n * server, which is exactly the situation the offline path exists for. The\n * anchor is compiled into the app, the keyset is signed by it, and the\n * validation token is signed by a key it lists: the whole chain verifies\n * offline against a root the network cannot influence.\n */\n keyset?: string;\n /** The last verified `typ=validation` token — the offline verdict. */\n validation?: string;\n /**\n * The highest server clock ever seen, in unix seconds, taken from a signed\n * claim.\n *\n * This is what makes offline grace bounded. Grace is evaluated against the\n * local clock, which the user controls; with no mark that only moves forward,\n * winding the clock back would renew a 72-hour window indefinitely. A\n * monotonic timer is not a substitute — it resets on reboot and on a VM\n * snapshot restore, which is the case that matters.\n */\n timeHighWater?: number;\n /**\n * The highest `release_seq` this install is known to be running, and the\n * floor `checkForUpdate` refuses to go below.\n *\n * Anti-downgrade only works against a mark that outlives the process, and\n * until this existed there was none: the floor came entirely from\n * `currentReleaseSeq`, which the host had to compute and pass, and which no\n * example passed. `docs/SECURITY.md` described a high-water mark that did not\n * exist, so \"yanking a release does not recall it from installs that already\n * applied it\" rested on the host having implemented the mark itself.\n */\n releaseSeqHighWater?: number;\n /**\n * A release whose bytes were downloaded and verified but which is not\n * running yet: the version string from its signed manifest, and its\n * `release_seq`.\n *\n * The SDK downloads; the host installs, in its own way, on its own schedule,\n * usually after a restart. So \"installed\" is not an event the SDK can see —\n * but the *result* is, because the host tells it which version it is on every\n * time a client is constructed. When `appVersion` matches what was downloaded,\n * the download became an install and the mark advances.\n *\n * The alternative was a `markInstalled()` for the host to call, and the whole\n * defect here is a mark that depended on the host doing something nobody\n * documented and no example did.\n */\n pendingRelease?: { version: string; releaseSeq: number };\n /**\n * This machine's own Ed25519 private key (PKCS#8 DER, base64), generated at\n * its first offline request.\n *\n * The air-gapped counterpart of `activationSecret`, and it exists for the same\n * reason that one does: an install has to be able to prove it is the install a\n * decision was made for. It never leaves this file — only the public half\n * travels, inside `talos-request.json` — and losing the file means repeating\n * the ceremony with the vendor, because the identity has exactly one copy by\n * construction.\n */\n machineKey?: string;\n /** The raw public half, standard base64. Checked against the file's `mpk`. */\n machinePublicKey?: string;\n /** The last verified `typ=offline` licence file. */\n offlineToken?: string;\n /**\n * The highest local clock ever seen while running on an offline file, in unix\n * seconds.\n *\n * Deliberately separate from {@link timeHighWater}, which holds *server*\n * times taken from signed claims. An air-gapped install sees exactly one\n * server time in its life — the `iat` of its file — so the server mark stops\n * moving, and nothing would notice a clock wound back to the day after\n * issuance to keep a year-long file alive forever.\n *\n * Mixing the two would be worse than useless: a machine whose clock ran fast\n * would inflate the server mark, and correcting the clock afterwards would\n * then read as tampering on an install that had done nothing wrong.\n *\n * What this bounds is **rollback, not freeze**. A user who stops their clock\n * keeps a file past its expiry; a user who winds it back is told\n * `clock_tampered`. That is the honest limit of enforcing a deadline against\n * a clock its owner controls, and it is the price of offline.\n */\n offlineSeen?: number;\n}\n\nconst MAGIC = Buffer.from(\"TLS1\");\nconst IV_LEN = 12;\nconst TAG_LEN = 16;\nconst SALT = Buffer.from(\"talos-state-v1\");\n\n/**\n * Where the binding lives, per product, following each OS's convention.\n *\n * The directory is named by a hash of the product token rather than the token\n * itself: it keeps the path filesystem-safe without assuming anything about the\n * token's alphabet, and two products never collide.\n */\nexport function defaultStatePath(productToken: string): string {\n return statePathFor(productToken, os.platform(), os.homedir(), process.env);\n}\n\n/**\n * The path, from a host described rather than asked — so the layout on all three\n * platforms is testable from any one of them, and the C# SDK has something to\n * agree with. (Its `DefaultPathFor` is the same three branches; the same product\n * on the same machine must not have two bindings in two places.)\n *\n * Not exported from the package: this exists for the tests on both sides.\n */\nexport function statePathFor(\n productToken: string,\n platform: string,\n home: string,\n env: Record<string, string | undefined>,\n): string {\n const slot = createHash(\"sha256\").update(productToken).digest(\"hex\").slice(0, 16);\n switch (platform) {\n case \"win32\":\n return join(env[\"LOCALAPPDATA\"] || join(home, \"AppData\", \"Local\"), \"Talos\", slot, \"state.bin\");\n case \"darwin\":\n return join(home, \"Library\", \"Application Support\", \"Talos\", slot, \"state.bin\");\n default:\n // Lower-case, unlike the other two: this is the directory a Linux user's\n // other applications keep company with.\n return join(env[\"XDG_DATA_HOME\"] || join(home, \".local\", \"share\"), \"talos\", slot, \"state.bin\");\n }\n}\n\n/**\n * Key material for the state file, chosen for **stability** rather than\n * strength: a hardware change must not lock the user out of their own stored\n * licence key. `machine_guid` and `smbios_uuid` survive component swaps;\n * `os_install` survives everything short of a reinstall, which invalidates the\n * other two anyway.\n *\n * These values are already HMAC'd per product by the fingerprint collector, so\n * nothing here reveals an identifier the server does not already hold.\n *\n * Where a host offers none of them the file is obfuscated rather than bound to\n * the machine, and that is the honest description. It matters less than it\n * sounds: the server resolves a machine by fingerprint and demands the\n * activation secret, so a state file copied to a different computer does not\n * validate there. This layer is tamper-evidence and resistance to casual\n * copying — never secrecy from someone who owns the machine.\n */\nexport function stateKeyMaterial(fp: FingerprintData): string {\n return (\n fp.components[\"machine_guid\"] ??\n fp.components[\"smbios_uuid\"] ??\n fp.components[\"os_install\"] ??\n \"talos-unbound\"\n );\n}\n\n/**\n * SHA-256 of a licence key, hex.\n *\n * Used only to recognise \"the same key as last time\" without keeping the key.\n * Not a credential and not accepted by anything: the server matches an HMAC\n * computed under a pepper this process has never seen.\n */\nexport function hashLicenseKey(key: string): string {\n return createHash(\"sha256\").update(key, \"utf8\").digest(\"hex\");\n}\n\n/** Reads and writes one product's machine binding. */\nexport class StateStore {\n private readonly key: Buffer;\n\n constructor(\n readonly path: string,\n private readonly productToken: string,\n keyMaterial: string,\n ) {\n this.key = Buffer.from(\n hkdfSync(\"sha256\", Buffer.from(keyMaterial), SALT, Buffer.from(productToken), 32),\n );\n }\n\n /**\n * Load the binding, or null if there is none.\n *\n * Never throws. A state file that is missing, truncated, from another\n * product, written by a future version, or simply corrupt must degrade to\n * \"not activated yet\" — the app then activates again. Throwing here would\n * turn a damaged cache into a launch failure.\n */\n load(): StoredBinding | null {\n let raw: Buffer;\n try {\n raw = readFileSync(this.path);\n } catch {\n return null;\n }\n try {\n if (raw.length < MAGIC.length + IV_LEN + TAG_LEN) return null;\n if (!raw.subarray(0, MAGIC.length).equals(MAGIC)) return null;\n const iv = raw.subarray(MAGIC.length, MAGIC.length + IV_LEN);\n const tag = raw.subarray(MAGIC.length + IV_LEN, MAGIC.length + IV_LEN + TAG_LEN);\n const body = raw.subarray(MAGIC.length + IV_LEN + TAG_LEN);\n const d = createDecipheriv(\"aes-256-gcm\", this.key, iv);\n // The product token is authenticated, not just stored: a file belonging\n // to another product cannot be silently adopted.\n d.setAAD(Buffer.from(this.productToken));\n d.setAuthTag(tag);\n const plain = Buffer.concat([d.update(body), d.final()]).toString(\"utf8\");\n const o = JSON.parse(plain) as Record<string, unknown>;\n if (o[\"v\"] !== 1) return null;\n // An older file carries `licenseKey` in the clear. Upgrade it in place\n // rather than discarding the binding: the customer keeps their seat, and\n // the plaintext key stops existing on disk at the next save — which is\n // the point of the change, and it must not cost anybody a re-activation\n // to get.\n const legacyKey = typeof o[\"licenseKey\"] === \"string\" ? o[\"licenseKey\"] : \"\";\n const b: StoredBinding = {\n licenseKeyHash:\n typeof o[\"licenseKeyHash\"] === \"string\" && o[\"licenseKeyHash\"]\n ? o[\"licenseKeyHash\"]\n : legacyKey\n ? hashLicenseKey(legacyKey)\n : \"\",\n activationSecret: String(o[\"activationSecret\"] ?? \"\"),\n machineId: String(o[\"machineId\"] ?? \"\"),\n fingerprint: String(o[\"fingerprint\"] ?? \"\"),\n };\n if (typeof o[\"keyset\"] === \"string\") b.keyset = o[\"keyset\"];\n if (typeof o[\"validation\"] === \"string\") b.validation = o[\"validation\"];\n if (typeof o[\"timeHighWater\"] === \"number\") b.timeHighWater = o[\"timeHighWater\"];\n if (typeof o[\"releaseSeqHighWater\"] === \"number\") {\n b.releaseSeqHighWater = o[\"releaseSeqHighWater\"];\n }\n // Read field by field rather than trusted wholesale: this file is on a\n // disk the user owns, so every value out of it is input. A `pendingRelease`\n // with a string where a number belongs would make the anti-downgrade\n // comparison silently lexicographic.\n const pending = o[\"pendingRelease\"] as Record<string, unknown> | undefined;\n if (\n pending &&\n typeof pending[\"version\"] === \"string\" &&\n typeof pending[\"releaseSeq\"] === \"number\"\n ) {\n b.pendingRelease = { version: pending[\"version\"], releaseSeq: pending[\"releaseSeq\"] };\n }\n if (typeof o[\"machineKey\"] === \"string\") b.machineKey = o[\"machineKey\"];\n if (typeof o[\"machinePublicKey\"] === \"string\") {\n b.machinePublicKey = o[\"machinePublicKey\"];\n }\n if (typeof o[\"offlineToken\"] === \"string\") b.offlineToken = o[\"offlineToken\"];\n if (typeof o[\"offlineSeen\"] === \"number\") b.offlineSeen = o[\"offlineSeen\"];\n // What makes a binding worth keeping is that it holds a licence or the\n // identity behind one. The activation secret is that for an online\n // install; an air-gapped install has none — it was never issued one — and\n // requiring it here would throw away the offline file and the machine key\n // on every start, which is the whole state of an air-gapped install.\n return b.activationSecret || b.offlineToken || b.machineKey ? b : null;\n } catch {\n return null;\n }\n }\n\n /**\n * Persist the binding. Best-effort by design: a read-only or full disk costs\n * the user a re-activation next launch, and must not fail the activation\n * that just succeeded.\n */\n save(b: StoredBinding): boolean {\n try {\n const plain = Buffer.from(JSON.stringify({ v: 1, ...b }), \"utf8\");\n const iv = randomBytes(IV_LEN);\n const c = createCipheriv(\"aes-256-gcm\", this.key, iv);\n c.setAAD(Buffer.from(this.productToken));\n const body = Buffer.concat([c.update(plain), c.final()]);\n const out = Buffer.concat([MAGIC, iv, c.getAuthTag(), body]);\n\n mkdirSync(dirname(this.path), { recursive: true, mode: 0o700 });\n // Write-then-rename: a crash mid-write leaves the previous binding\n // intact rather than a half file that reads as \"never activated\".\n const tmp = `${this.path}.tmp`;\n writeFileSync(tmp, out, { mode: 0o600 });\n try {\n chmodSync(tmp, 0o600); // no-op on Windows; explicit on POSIX umasks\n } catch {\n /* ignore */\n }\n renameSync(tmp, this.path);\n return true;\n } catch {\n return false;\n }\n }\n\n /** Forget the binding (deactivation). */\n clear(): void {\n try {\n rmSync(this.path, { force: true });\n } catch {\n /* ignore */\n }\n }\n}\n","/**\n * This SDK's own version, as reported to the server on activate and heartbeat.\n *\n * A constant rather than a read of `package.json`: the package ships dual\n * ESM/CJS through a bundler, so neither `import.meta.url` nor `__dirname`\n * resolves to the package root in both outputs, and a runtime read that works\n * in one build and throws in the other is worse than no read at all.\n *\n * `test/version.test.ts` asserts this equals `package.json`, so it cannot\n * quietly go stale — which is what the previous hard-coded `\"node/0.0.0\"`\n * literal at the call site was doing, and why the column it fills was worthless\n * for answering \"who is still on the old client?\".\n */\nexport const SDK_VERSION = \"0.2.0\";\n\n/** The wire form: language tag plus version, e.g. `node/0.2.0`. */\nexport const SDK_ID = `node/${SDK_VERSION}`;\n","import { TalosApiError, TalosSignatureError } from \"./errors.js\";\nimport type { LicenseState } from \"./client.js\";\nimport { SDK_ID } from \"./version.js\";\n\nconst decisions = new Set([\"valid\", \"expired\", \"revoked\", \"suspended\", \"deactivated\", \"machine_revoked\", \"invalid\", \"grace_expired\", \"clock_tampered\"]);\nconst errors = new Set([\"product_not_found\", \"license_not_found\", \"license_expired\", \"license_revoked\", \"license_suspended\", \"machine_revoked\", \"activation_limit_reached\", \"seat_limit_reached\", \"deactivation_limit_reached\", \"tenant_suspended\", \"tenant_closed\", \"invalid_activation_secret\", \"rate_limited\"]);\n\n/** An allowlisted local snapshot. Never serialize options, errors or bindings wholesale. */\nexport function diagnosticReport(activated: boolean, anchorPinned: boolean, state: LicenseState | null, error?: unknown): string {\n const code = error instanceof TalosApiError\n ? (errors.has(error.errorCode ?? \"\") ? error.errorCode! : \"api_error\")\n : error instanceof TalosSignatureError ? \"signature_verification_failed\"\n : error != null ? \"client_error\" : null;\n const decision = state == null ? null : decisions.has(state.decision) ? state.decision : \"unknown\";\n const advice: Record<string, string> = {\n signature_verification_failed: \"Check the pinned anchor, product configuration and system time. Keep signature verification enabled.\",\n activation_limit_reached: \"Open the license's devices in the portal. Release an unused device or review its activation limit.\",\n seat_limit_reached: \"Close an unused app session or review the concurrent seat limit.\",\n license_expired: \"Renew the license in the portal, then validate again.\",\n expired: \"Renew the license in the portal, then validate again.\",\n license_revoked: \"Revocation is permanent. Ask the vendor for a replacement license.\",\n revoked: \"Revocation is permanent. Ask the vendor for a replacement license.\",\n license_suspended: \"Ask the vendor to review the suspension before activating a new device.\",\n suspended: \"Ask the vendor to review the suspension.\",\n machine_revoked: \"Ask the vendor to review this device in the portal.\",\n grace_expired: \"Reconnect to the configured Talos server and validate. Do not extend the local grace deadline.\",\n clock_tampered: \"Correct the system clock and validate online.\",\n client_error: \"Check connectivity, server configuration and local state access. The exception text is intentionally excluded.\",\n };\n const action = advice[code ?? decision ?? \"\"]\n ?? (code ? \"Review the licensing call and configuration with the vendor. HTTP errors are not signed license decisions.\"\n : state?.isOffline ? \"The last decision was offline. Check connectivity and validate online; this snapshot cannot identify the transport failure.\"\n : !activated ? \"Activate with a license key, then validate the saved activation.\"\n : decision === \"valid\" ? \"The last licensing call returned valid. Check the portal for this device's recorded validation.\"\n : \"Run a licensing check and generate a new report. Ask the vendor to review an invalid decision.\");\n const timestamp = (value: number | null | undefined) => typeof value === \"number\" && Number.isFinite(value) ? value : null;\n return JSON.stringify({\n schema_version: 1,\n sdk: SDK_ID,\n generated_at: Math.floor(Date.now() / 1000),\n activated,\n anchor_pinned: anchorPinned,\n decision,\n is_offline: state?.isOffline ?? null,\n license_expires_at: timestamp(state?.licenseExpiresAt),\n grace_until: timestamp(state?.graceUntil),\n error_code: code,\n http_status: error instanceof TalosApiError && Number.isInteger(error.statusCode) && error.statusCode >= 100 && error.statusCode <= 599 ? error.statusCode : null,\n next_action: action,\n }, null, 2);\n}\n","import { createPrivateKey, generateKeyPairSync, sign as cryptoSign, type KeyObject } from \"node:crypto\";\n\nimport { b64urlEncode } from \"./talos-compact.js\";\n\n/**\n * The air-gapped exchange, and the identity that makes it possible.\n *\n * Online, a machine proves who it is with the `activation_secret` the server\n * hands back from `activate`. A machine that cannot reach a server never gets\n * one — so it makes its own: an Ed25519 pair at the first offline request,\n * public half in `talos-request.json`, private half never leaving this disk. The\n * file the portal issues is bound to that key as well as to the fingerprint, and\n * anything this machine sends back afterwards is signed by it.\n *\n * The document format deliberately is **not** Talos-compact. That format's `typ`\n * set is the closed list of things the *platform* signs, and putting a machine's\n * key into the same namespace as a product's is exactly the confusion the tag\n * set exists to prevent. What is copied is the part that matters: the payload\n * travels base64url-encoded and is verified as received, never re-serialised, so\n * three implementations cannot disagree about JSON key order and turn that into\n * a signature one of them rejects.\n */\n\n/** Domain tag of `talos-request.json`; also part of what is signed. */\nexport const REQUEST_DOMAIN = \"talos-offline-request\";\n\n/**\n * Domain tag of `talos-deactivation.json`, the document that hands a seat back.\n *\n * The second thing a machine key signs, and the reason the domain is inside the\n * signed bytes rather than beside them: without it a captured request would be a\n * valid receipt for the same machine, and returning a seat is not what its owner\n * asked for when they asked for a licence.\n */\nexport const RECEIPT_DOMAIN = \"talos-offline-deactivation\";\n/** `kind` of the file the portal issues back. */\nexport const LICENSE_FILE_KIND = \"talos-offline-license\";\nconst DOC_VERSION = 1;\n\n/** A machine's own keypair, in the two forms this SDK needs. */\nexport interface MachineKey {\n /** PKCS#8 DER, base64 — how the private half is kept in the state file. */\n privateKey: string;\n /** The raw 32-byte public key, standard base64 — how it travels. */\n publicKey: string;\n}\n\n/** A document this machine signed: what gets written to disk and carried. */\nexport interface SignedDoc {\n v: number;\n kind: string;\n /** base64url of the payload JSON. */\n req: string;\n /** base64url Ed25519 signature over `\"<kind>.1.<req>\"`. */\n sig: string;\n}\n\n/**\n * Generate the machine's identity.\n *\n * Once per install, at the first offline request. Losing it means the customer\n * repeats the ceremony — which is the honest consequence of an identity that is\n * only ever held in one place, and the reason it lives in the same encrypted\n * state file as everything else this SDK persists.\n */\nexport function generateMachineKey(): MachineKey {\n const { privateKey, publicKey } = generateKeyPairSync(\"ed25519\");\n return {\n privateKey: privateKey.export({ format: \"der\", type: \"pkcs8\" }).toString(\"base64\"),\n // The raw key is the last 32 bytes of the SPKI DER; every other Ed25519\n // implementation in this project speaks raw-32, including the server's.\n publicKey: publicKey.export({ format: \"der\", type: \"spki\" }).subarray(-32).toString(\"base64\"),\n };\n}\n\nfunction privateKeyObject(pkcs8Base64: string): KeyObject {\n return createPrivateKey({\n key: Buffer.from(pkcs8Base64, \"base64\"),\n format: \"der\",\n type: \"pkcs8\",\n });\n}\n\n/** The exact ASCII bytes a machine-signed document is signed over. */\nexport function signedMessage(domain: string, payloadB64: string): string {\n return `${domain}.${DOC_VERSION}.${payloadB64}`;\n}\n\n/** Sign a payload object as a machine document. */\nexport function signMachineDoc(\n key: MachineKey,\n domain: string,\n payload: Record<string, unknown>,\n): SignedDoc {\n const req = b64urlEncode(Buffer.from(JSON.stringify(payload), \"utf8\"));\n const sig = cryptoSign(null, Buffer.from(signedMessage(domain, req), \"ascii\"), privateKeyObject(key.privateKey));\n return { v: DOC_VERSION, kind: domain, req, sig: b64urlEncode(sig) };\n}\n","import { createPublicKey, verify as cryptoVerify, type KeyObject } from \"node:crypto\";\n\nimport { TalosSignatureError } from \"./errors.js\";\n\n// DER SubjectPublicKeyInfo prefix for a raw Ed25519 public key.\nconst SPKI_PREFIX = Buffer.from(\"302a300506032b6570032100\", \"hex\");\nconst CLOCK_SKEW_SECONDS = 60;\n\n/** Wrap a base64-encoded raw 32-byte Ed25519 public key as a KeyObject. */\nexport function publicKeyFromBase64(b64: string): KeyObject {\n const raw = Buffer.from(b64, \"base64\");\n if (raw.length !== 32) {\n throw new Error(\"public key must decode to 32 bytes\");\n }\n const der = Buffer.concat([SPKI_PREFIX, raw]);\n return createPublicKey({ key: der, format: \"der\", type: \"spki\" });\n}\n\nfunction b64urlDecode(s: string): Buffer {\n return Buffer.from(s.replace(/-/g, \"+\").replace(/_/g, \"/\"), \"base64\");\n}\n\nexport function b64urlEncode(buf: Buffer): string {\n return buf.toString(\"base64\").replace(/=+$/, \"\").replace(/\\+/g, \"-\").replace(/\\//g, \"_\");\n}\n\ntype Keys = KeyObject | KeyObject[];\n\n/**\n * Parse a Talos-compact token, check scheme/version/`typ`, and verify the\n * signature against ANY of `keys` (a keyset may carry several valid keys during\n * rotation). Returns the decoded claims; the caller checks aud/nonce/exp.\n */\nfunction parseAndVerify(keys: Keys, token: string, expectedTyp: string): Record<string, unknown> {\n const list = Array.isArray(keys) ? keys : [keys];\n const parts = token.split(\".\");\n if (parts.length !== 5) throw new TalosSignatureError(\"malformed token\");\n if (parts[0] !== \"talos\") throw new TalosSignatureError(\"unexpected scheme\");\n if (parts[1] !== \"1\") throw new TalosSignatureError(\"unsupported wire version\");\n if (parts[2] !== expectedTyp) throw new TalosSignatureError(\"token type mismatch\");\n if (list.length === 0) throw new TalosSignatureError(\"no verification key\");\n\n const lastDot = token.lastIndexOf(\".\");\n const prefix = Buffer.from(token.slice(0, lastDot), \"ascii\");\n const sig = b64urlDecode(parts[4]!);\n const ok = list.some((k) => cryptoVerify(null, prefix, k, sig));\n if (!ok) throw new TalosSignatureError(\"signature verification failed\");\n\n return JSON.parse(b64urlDecode(parts[3]!).toString(\"utf8\")) as Record<string, unknown>;\n}\n\n/**\n * Verify a Talos-compact token\n * (`talos.<ver>.<typ>.<b64url(payload)>.<b64url(sig)>`, Ed25519 over the exact\n * ASCII prefix). `keys` may be a single pinned key or a keyset (any of which may\n * have signed it). IGNORES any `kid` and enforces `typ`, `aud`, the echoed\n * nonce, and `nbf`/`exp`.\n */\nexport function verifyToken(\n keys: Keys,\n expectedTyp: string,\n expectedAud: string,\n expectedNonce: string,\n token: string,\n): Record<string, unknown> {\n const claims = parseAndVerify(keys, token, expectedTyp);\n if (claims[\"aud\"] !== expectedAud) throw new TalosSignatureError(\"audience mismatch\");\n if (claims[\"non\"] !== expectedNonce) {\n throw new TalosSignatureError(\"nonce mismatch (possible replay)\");\n }\n // Required, not \"checked if present\". Every live token the server mints\n // carries both, so a token without them did not come from a server that\n // knows what it is doing — and treating a missing `exp` as \"no expiry\" turns\n // one server-side omission into a verdict that is valid forever on every\n // install in the field. Cached tokens are a different path (see\n // `verifyCachedToken`), where `exp` deliberately does not apply.\n const now = Math.floor(Date.now() / 1000);\n const nbf = claims[\"nbf\"];\n if (typeof nbf !== \"number\") throw new TalosSignatureError(\"token has no nbf\");\n if (now + CLOCK_SKEW_SECONDS < nbf) {\n throw new TalosSignatureError(\"token not yet valid\");\n }\n const exp = claims[\"exp\"];\n if (typeof exp !== \"number\") throw new TalosSignatureError(\"token has no exp\");\n if (now - CLOCK_SKEW_SECONDS > exp) {\n throw new TalosSignatureError(\"token expired\");\n }\n return claims;\n}\n\n/**\n * Verify a token restored from the local cache, for use while offline.\n *\n * Two checks from {@link verifyToken} deliberately do not apply. The nonce\n * belonged to the request that fetched this token, and `exp` is the short window\n * in which it counts as *fresh* — a cached decision is by definition being used\n * after that. What bounds offline use instead is the `grace_until` claim, which\n * the caller enforces, together with the persisted server-time high-water mark\n * that stops the system clock being wound back to extend it.\n *\n * Everything binding the token to this machine still holds: the signature under\n * a pinned key, `typ`, `aud`, `nbf`, and the caller's fingerprint check.\n */\nexport function verifyCachedToken(\n keys: Keys,\n expectedTyp: string,\n expectedAud: string,\n token: string,\n): Record<string, unknown> {\n const claims = parseAndVerify(keys, token, expectedTyp);\n if (claims[\"aud\"] !== expectedAud) throw new TalosSignatureError(\"audience mismatch\");\n const nbf = claims[\"nbf\"];\n if (typeof nbf === \"number\" && Math.floor(Date.now() / 1000) + CLOCK_SKEW_SECONDS < nbf) {\n throw new TalosSignatureError(\"cached token not yet valid\");\n }\n return claims;\n}\n\n/**\n * Verify a signed update **manifest** (`typ=manifest`) against the pinned\n * release key(s). Unlike per-request tokens a manifest is a long-lived, static\n * artifact, so there is no nonce and no short `exp`; instead it carries a\n * `not_before` gate. Enforces scheme/version/`typ`, the signature, `aud`, and\n * `not_before`.\n */\nexport function verifyManifest(\n releaseKeys: Keys,\n expectedAud: string,\n token: string,\n): Record<string, unknown> {\n const claims = parseAndVerify(releaseKeys, token, \"manifest\");\n if (claims[\"aud\"] !== expectedAud) throw new TalosSignatureError(\"audience mismatch\");\n const notBefore = claims[\"not_before\"];\n if (typeof notBefore === \"number\") {\n const now = Math.floor(Date.now() / 1000);\n if (now + CLOCK_SKEW_SECONDS < notBefore) {\n throw new TalosSignatureError(\"manifest not yet released\");\n }\n }\n return claims;\n}\n\n/**\n * Verify a signed **keyset** (`typ=keyset`) against the pinned anchor key. The\n * keyset lists the product's current, rotatable license/release public keys, so\n * an SDK that pins only the anchor learns the signing keys from it — a rotated\n * key never breaks a deployed app. Enforces scheme/version/`typ`, the anchor\n * signature, and `aud`.\n */\nexport function verifyKeyset(\n anchorKey: KeyObject,\n expectedAud: string,\n token: string,\n): Record<string, unknown> {\n const claims = parseAndVerify(anchorKey, token, \"keyset\");\n if (claims[\"aud\"] !== expectedAud) throw new TalosSignatureError(\"audience mismatch\");\n return claims;\n}\n"],"mappings":"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","names":["createHash","randomBytes","readFileSync","os","createHash","readFileSync","os","CLOCK_SKEW_SECONDS","randomBytes","os","readFileSync","createHash"]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/errors.ts","../src/fingerprint.ts","../src/state.ts","../src/version.ts","../src/diagnostics.ts","../src/offline.ts","../src/talos-compact.ts"],"sourcesContent":["import { createHash, randomBytes } from \"node:crypto\";\nimport { createReadStream, readFileSync } from \"node:fs\";\nimport { open, rename, rm } from \"node:fs/promises\";\nimport os from \"node:os\";\nimport { type KeyObject } from \"node:crypto\";\n\nimport { TalosApiError, TalosError, TalosSignatureError } from \"./errors.js\";\nimport { collectFingerprint, type FingerprintData } from \"./fingerprint.js\";\nimport {\n StateStore,\n defaultStatePath,\n hashLicenseKey,\n stateKeyMaterial,\n type StoredBinding,\n} from \"./state.js\";\nimport { SDK_ID } from \"./version.js\";\nimport { diagnosticReport } from \"./diagnostics.js\";\nimport {\n generateMachineKey,\n signMachineDoc,\n LICENSE_FILE_KIND,\n RECEIPT_DOMAIN,\n REQUEST_DOMAIN,\n type MachineKey,\n type SignedDoc,\n} from \"./offline.js\";\nimport {\n b64urlEncode,\n publicKeyFromBase64,\n verifyCachedToken,\n verifyKeyset,\n verifyManifest,\n verifyToken,\n} from \"./talos-compact.js\";\n\n/**\n * Tolerance for a slow or slightly-off clock, in seconds. Matches the token\n * verifier's; the point is to be forgiving about drift while still catching a\n * clock that has been moved.\n */\nconst CLOCK_SKEW_SECONDS = 60;\n\n/**\n * Fallback check-in cadence, used only until the first heartbeat reports the\n * policy's own. Matches the server's `policies.heartbeat_interval_s` default.\n */\nconst DEFAULT_HEARTBEAT_SECONDS = 600;\n\n/**\n * What a response has to offer this SDK.\n *\n * `headers` and `body` are optional because a test double need not implement a\n * `ReadableStream`; `arrayBuffer` is not, because the artifact download has to\n * read bytes and an optional method there just moves the failure to run time —\n * which is exactly what this shape is being widened to prevent.\n */\nexport interface FetchResponseLike {\n status: number;\n text(): Promise<string>;\n arrayBuffer(): Promise<ArrayBuffer>;\n headers?: { get(name: string): string | null };\n /** A streaming body, when the implementation has one (the global `fetch`\n * does). Used to enforce the size cap without buffering first. */\n body?: { getReader(): { read(): Promise<{ done: boolean; value?: Uint8Array }>; cancel(): Promise<unknown> } } | null;\n}\n\n/** Minimal fetch signature (Node 18+ global fetch, or an injected one). */\nexport type FetchLike = (\n url: string,\n init: {\n method: string;\n headers: Record<string, string>;\n body?: string;\n /**\n * Aborts the request once {@link TalosOptions.httpTimeoutMs} elapses.\n *\n * Optional so an injected `fetch` that predates this field still satisfies\n * the type — but one that ignores the signal has no timeout, and the\n * licensing call it drops is on somebody's launch path.\n */\n signal?: AbortSignal;\n },\n) => Promise<FetchResponseLike>;\n\n/**\n * Default cap on a licensing call, in milliseconds.\n *\n * Ten seconds: long enough that a slow network is not mistaken for a dead one,\n * short enough that an application blocked on it still starts. Node's `fetch`\n * has no default at all, which is the state this replaces.\n */\nconst DEFAULT_HTTP_TIMEOUT_MS = 10_000;\n\nexport interface TalosOptions {\n /** The product's public token (`tpt_…`) from the portal. */\n productToken: string;\n /**\n * The product's **anchor** public key, base64 (from the portal's Integration\n * page). The rotation-safe root: the SDK fetches the anchor-signed keyset and\n * learns the current license/release keys from it, so rotating a signing key\n * never breaks a deployed app. Strongly recommended — pin this. If set,\n * {@link publicKey} is optional (a fallback for when the keyset is unavailable).\n */\n anchorPublicKey?: string;\n /**\n * The product's Ed25519 license public key, base64. A direct pin used to\n * verify activation/validation tokens when no keyset is loaded. Optional if\n * {@link anchorPublicKey} is set.\n */\n publicKey?: string;\n /**\n * The product's **release**-signing public key, base64. Direct pin used to\n * verify update manifests when no keyset is loaded. Falls back to\n * {@link publicKey} when omitted.\n */\n releasePublicKey?: string;\n /** Base URL of the Talos (or self-hosted) server. */\n serverUrl: string;\n /** Optional app version reported in telemetry/heartbeats. */\n appVersion?: string;\n /**\n * The name shown for this seat in the developer's portal. Defaults to\n * `os.hostname()`.\n *\n * Unlike the hostname the SDK folds into the fingerprint, this one is stored\n * and displayed as written — it is what a support agent reads when a customer\n * says \"release the one on my old laptop\", and without it every seat on a\n * licence is an indistinguishable hash prefix. Pass `\"\"` to send nothing if a\n * machine name would be more identifying than your users expect.\n */\n hostname?: string;\n /**\n * Optional fetch implementation (defaults to the global `fetch`).\n *\n * Used for **every** network call, artifact downloads included. It did not\n * used to be: `downloadUpdate` reached for `globalThis.fetch` directly, so a\n * proxy, a custom CA bundle, request logging or a test double applied to\n * every licensing call and was silently bypassed by the one call that pulls a\n * multi-megabyte binary and is likeliest to need it.\n */\n fetch?: FetchLike;\n /**\n * The update channel this install follows (`stable` by default, `beta` and\n * whatever else the product publishes).\n *\n * The default for {@link TalosClient.checkForUpdate}, and the channel a\n * heartbeat names when something is subscribed to `updateAvailable` — a beta\n * install has to ask about its own channel or it would be told about\n * stable's releases.\n */\n updateChannel?: string;\n /**\n * How long a licensing call may take before it is abandoned, in milliseconds.\n * Default 10000; `0` disables it.\n *\n * Node's `fetch` has **no** default timeout, so before this existed a server\n * that accepted the connection and never answered left `validate()` pending\n * forever — which in a desktop application is a splash screen that never goes\n * away, on the one code path a user cannot skip. Passing a custom `fetch` was\n * the only way to bound it, which is a lot of ceremony for one number.\n *\n * It does **not** apply to `downloadUpdate`. A total-duration cap on a\n * multi-megabyte artifact is not a safeguard, it is a size limit disguised as\n * one: the same 30 seconds that is generous for a licensing call fails every\n * download on a slow connection. The download is bounded by the manifest's\n * signed `size` instead, which is the bound that actually matters there.\n */\n httpTimeoutMs?: number;\n /**\n * Extra fingerprint components, merged over the ones the SDK collects.\n *\n * Node cannot read hardware identifiers on Windows or macOS without native\n * modules, so on those hosts the server falls back to exact matching and a\n * NIC change forces a re-activation. If your app can supply a stable\n * identifier — an Electron native module, or a value you already hold — pass\n * it here (e.g. `{ machine_guid: \"<value>\" }`) and the server can keep the\n * seat across hardware changes instead. Values are hashed like any other\n * component, so raw identifiers never leave the machine. See\n * `docs/FINGERPRINT.md` for the recognised ids and their weights.\n */\n fingerprintComponents?: Record<string, string>;\n /**\n * Where to keep this machine's activation across restarts. Defaults to a\n * per-product file under the OS user-data directory (see\n * {@link defaultStatePath}).\n */\n statePath?: string;\n /**\n * Persist the machine binding (default `true`).\n *\n * Turn it off only for a process that should not leave a licence on disk — a\n * test, or a short-lived worker. The cost is that every start must\n * `activate` again, which rotates the activation secret and is refused while\n * the tenant is suspended, so a long-lived desktop app wants this on.\n */\n persistState?: boolean;\n}\n\n/** One downloadable artifact of an available update (from a verified manifest). */\nexport interface UpdateArtifact {\n platform: string;\n arch: string;\n kind: string;\n filename: string;\n /** Expected size in bytes (from the signed manifest). */\n size: number;\n /** Expected SHA-256, lowercase hex (from the signed manifest). */\n sha256: string;\n /** Untrusted transport path; the downloaded bytes are verified against `sha256`. */\n downloadPath?: string;\n}\n\n/** What applying a patch has to produce. */\nexport interface UpdateDeltaTarget {\n /** The `kind` of the full artifact this rebuilds (`installer`, `archive`, …). */\n kind: string;\n filename: string;\n /** Expected size of the rebuilt file, in bytes (from the signed manifest). */\n size: number;\n /**\n * Expected SHA-256 of the rebuilt file, lowercase hex (from the signed\n * manifest). **Check it after applying** — see {@link verifyAppliedUpdate}.\n * Verifying only the patch leaves whatever comes out of the patcher\n * unverified, which is the whole thing the signed manifest exists to prevent.\n */\n sha256: string;\n}\n\n/**\n * A patch that turns one already-installed version into this release, from the\n * signed manifest.\n *\n * Talos hosts and signs patches; it does not produce or apply them. The diff\n * format belongs to whatever applies it (Velopack, Squirrel, MSIX, bsdiff,\n * `zstd --patch-from`), and this SDK deliberately does not run installers.\n */\nexport interface UpdateDelta {\n platform: string;\n arch: string;\n /** The installed version this patch applies to, exactly as published. */\n from: string;\n /** `release_seq` of the version it applies to (always below this release's). */\n fromSeq: number;\n filename: string;\n /** Expected size of the patch itself, in bytes. */\n size: number;\n /** Expected SHA-256 of the patch itself, lowercase hex. */\n sha256: string;\n target: UpdateDeltaTarget;\n /** Untrusted transport path; the bytes are verified against `sha256`. */\n downloadPath?: string;\n}\n\n/** A verified available update (its metadata all comes from the signed manifest). */\nexport interface UpdateInfo {\n version: string;\n releaseSeq: number;\n mandatory: boolean;\n notesUrl: string | null;\n artifacts: UpdateArtifact[];\n /**\n * Patches this release ships, if any. Never mixed into {@link artifacts}: a\n * patch is not something a host can install, and an SDK that picks \"the\n * artifact for this platform\" would hand one over as if it were a build.\n */\n deltas: UpdateDelta[];\n}\n\n/** Which patch to use, when a release ships more than one. */\nexport interface DeltaOptions {\n /**\n * The version currently installed. Defaults to `appVersion` from the client\n * options — the same value the update check reports.\n */\n from?: string;\n /**\n * Which shipped file to rebuild, when a release carries patches for several\n * (an installer and a portable archive, say). Defaults to the only one, and\n * is required when there is more than one.\n */\n targetKind?: string;\n}\n\n/**\n * How far a download has got, passed to the `onProgress` callback of\n * {@link TalosClient.downloadUpdate} and {@link TalosClient.downloadDelta}.\n *\n * `totalBytes` is the size the **signed manifest** names, not the server's\n * `content-length`. The download host is untrusted, and a bar driven by a header\n * it controls can be parked at 100% while bytes are still arriving. It is also\n * the cap the read is bounded by, so `bytesReceived` never exceeds it.\n */\nexport interface DownloadProgress {\n /** Bytes verified and written so far. */\n bytesReceived: number;\n /** Total size, in bytes, from the signed manifest. */\n totalBytes: number;\n}\n\n/** Called as an artifact download advances. */\nexport type DownloadProgressCallback = (progress: DownloadProgress) => void;\n\n/**\n * The licence transitions a host can subscribe to with {@link TalosClient.on}.\n *\n * Edges, not levels. `onState` already delivers the verdict on every check-in,\n * and an application that wants \"tell me when grace starts\" has to diff those\n * itself — getting that right (not re-warning every ten minutes for the whole\n * outage) is exactly the bookkeeping a library should do once instead of every\n * host doing it slightly wrong.\n */\nexport interface TalosEventMap {\n /** The licence stopped being usable; the payload says why. */\n licenseInvalid: LicenseState;\n /** Running on a cached verdict because the server could not be reached. */\n gracePeriodStarted: LicenseState;\n /** The offline window closed: the cached verdict may no longer be honoured. */\n gracePeriodExpired: LicenseState;\n /** The system clock moved back behind a signed server time already seen. */\n clockTamperDetected: LicenseState;\n /**\n * A release newer than this install is waiting, noticed by a heartbeat rather\n * than by being asked. The payload is the same verified {@link UpdateInfo}\n * {@link TalosClient.checkForUpdate} returns.\n */\n updateAvailable: UpdateInfo;\n}\n\nexport type TalosEventName = keyof TalosEventMap;\n\n/** Handler for a licence transition; receives the state that was adopted. */\nexport type LicenseListener = (state: LicenseState) => void;\n\n/** A held floating seat. Renew before {@link expiresAt} or it is reclaimed. */\nexport interface Lease {\n /** Server-side lease id (from the signed token). */\n id: string;\n /** This process's instance id. */\n instanceId: string;\n /** Unix seconds when the seat is reclaimed unless renewed. */\n expiresAt: number;\n /** Renew after this many seconds (server-chosen, ttl/3). */\n renewAfterSeconds: number;\n seatsInUse: number;\n maxSeats: number;\n}\n\nexport interface AcquireLeaseOptions {\n /**\n * Identifies this running process. Re-acquiring with the same value renews\n * instead of taking a second seat. Defaults to a random per-client id.\n */\n instanceId?: string;\n /** Auto-renew in the background (default true). Call {@link TalosClient.releaseLease} to stop. */\n autoRenew?: boolean;\n /** Called if a renewal fails and the seat is lost (e.g. revoked, server down). */\n onLeaseLost?: (reason: string) => void;\n}\n\nexport interface CheckInOptions {\n /** Called after every successful check-in, with the fresh verdict. */\n onState?: (state: LicenseState) => void;\n /**\n * Called when a check-in fails outright — the server answered with an error,\n * or the network is down *and* there is nothing left to fall back on.\n */\n onError?: (error: unknown) => void;\n}\n\nexport interface CheckForUpdateOptions {\n /** Currently installed version (informational). */\n currentVersion?: string;\n /**\n * Override the anti-downgrade floor for this call.\n *\n * Optional, and normally omitted: the SDK persists the highest `release_seq`\n * it has seen this install running and uses that. Pass one when the host\n * knows something the SDK cannot — a build installed by a package manager, or\n * a first run after adopting the SDK on an already-updated fleet.\n */\n currentReleaseSeq?: number;\n /** Update channel; defaults to `stable`. */\n channel?: string;\n}\n\n/** The current, cryptographically-verified license verdict. */\nexport class LicenseState {\n constructor(\n /**\n * `valid | expired | revoked | suspended | deactivated | machine_revoked |\n * invalid`, plus two verdicts only the client can reach:\n * `grace_expired` (offline past the licence's grace window) and\n * `clock_tampered` (the system clock moved back behind a signed server\n * time we had already seen).\n */\n readonly decision: string,\n /** Entitlement bag (server-defined). */\n readonly features: Record<string, unknown>,\n /** Business expiry (unix seconds), or null for perpetual. */\n readonly licenseExpiresAt: number | null,\n /**\n * True when this verdict came from the local cache rather than a live\n * server — the licence is being honoured on trust, inside the window the\n * policy allows.\n */\n readonly isOffline: boolean = false,\n /**\n * Unix seconds after which the app should validate again. Chosen by the\n * server and carried in a signed claim, so it cannot be lengthened in\n * transit.\n */\n readonly revalidateAfter: number | null = null,\n /** Unix seconds until which this verdict may be honoured offline. */\n readonly graceUntil: number | null = null,\n ) {}\n\n get isValid(): boolean {\n return this.decision === \"valid\";\n }\n\n isFeatureEnabled(name: string): boolean {\n const v = this.features[name];\n if (typeof v === \"boolean\") return v;\n if (typeof v === \"number\") return v !== 0;\n if (typeof v === \"string\") return v.length > 0;\n return false;\n }\n\n getEntitlement(name: string): unknown {\n return this.features[name];\n }\n}\n\n/**\n * Online-first Talos client. Every trust decision is a signed token verified\n * against the pinned product key, so a fake/MITM server cannot forge an\n * acceptable answer. One instance per product; holds the per-machine activation\n * secret after {@link TalosClient.activate}.\n */\n/**\n * How long an adopted keyset is trusted before a refresh is attempted.\n *\n * The upper bound on how long a revoked signing key keeps being accepted by an\n * install that stays online. A day is the trade: shorter multiplies load on a\n * public endpoint serving a document that changes about once a year, longer\n * widens the window in which a compromised key still works.\n */\nconst KEYSET_MAX_AGE_SECONDS = 24 * 60 * 60;\n\nexport class TalosClient {\n private readonly options: TalosOptions;\n private readonly anchorKey: KeyObject | null;\n private readonly publicKey: KeyObject | null;\n private readonly releaseKey: KeyObject | null;\n private readonly fingerprint: string;\n private readonly fp: FingerprintData;\n private readonly doFetch: FetchLike;\n\n // Learned from the anchor-signed keyset; take precedence over the direct pins.\n private keysetLicenseKeys: KeyObject[] = [];\n private keysetReleaseKeys: KeyObject[] = [];\n\n /**\n * SHA-256 of the key this machine activated with, or null.\n *\n * The key itself is deliberately not held: after activation the server\n * resolves the licence from the activation secret, so keeping the key would\n * mean a plaintext credential in memory and on disk for the sake of one\n * string comparison in `activate`.\n */\n private licenseKeyHash: string | null = null;\n private activationSecret: string | null = null;\n private machineId: string | null = null;\n private readonly store: StateStore | null;\n /** Everything persisted for this product; written back as one file. */\n private binding: StoredBinding | null = null;\n /**\n * True while the adopted keyset came off disk. One refresh per process\n * upgrades it, so a rotation is picked up, but a client that cannot reach the\n * server keeps verifying with what it already trusts.\n */\n private keysetFromCache = false;\n /**\n * The `iat` of the adopted keyset, in unix seconds, or 0 if unknown.\n *\n * Kept so a keyset can go stale. Without it the refresh happens once per\n * process, which is fine for a desktop app that restarts and useless for the\n * process this SDK is most often *in* — a server or an Electron main that runs\n * for weeks. A key revoked in the portal would never reach it.\n */\n private keysetIssuedAt = 0;\n private keysetToken: string | null = null;\n /** Invalidates online work started before a local licensing-mode change. */\n private licensingGeneration = 0;\n /** The policy's heartbeat cadence, from the last heartbeat's signed claims. */\n private heartbeatInterval: number | null = null;\n private checkInTimer: ReturnType<typeof setTimeout> | null = null;\n /** Bumped on stop so an in-flight tick cannot re-arm a cancelled loop. */\n private checkInGeneration = 0;\n private lease: Lease | null = null;\n private renewTimer: ReturnType<typeof setTimeout> | null = null;\n /** Bumped on release so an in-flight renewal cannot resurrect the seat. */\n private leaseGeneration = 0;\n /** Default instance id for floating leases: one per client object. */\n private readonly instanceId: string = b64urlEncode(randomBytes(9));\n current: LicenseState | null = null;\n\n /**\n * Return a local JSON support report without credentials, identifiers or\n * exception text. Pass a caught error for classification. Makes no requests\n * and does not change licensing state; `decision` is the last known result.\n */\n createDiagnosticReport(error?: unknown): string {\n return diagnosticReport(this.isActivated, this.options.anchorPublicKey != null && this.options.anchorPublicKey !== \"\", this.current, error);\n }\n private readonly listeners = new Map<TalosEventName, Set<(payload: never) => void>>();\n /** The newest release this client has already announced. */\n private announcedReleaseSeq = 0;\n\n constructor(options: TalosOptions) {\n if (!options.productToken) throw new TalosError(\"productToken required\");\n if (!options.serverUrl) throw new TalosError(\"serverUrl required\");\n if (!options.anchorPublicKey && !options.publicKey) {\n throw new TalosError(\"anchorPublicKey or publicKey required\");\n }\n this.options = options;\n this.anchorKey = options.anchorPublicKey\n ? publicKeyFromBase64(options.anchorPublicKey)\n : null;\n this.publicKey = options.publicKey ? publicKeyFromBase64(options.publicKey) : null;\n // Direct release pin falls back to the license pin (keyset overrides both).\n this.releaseKey = options.releasePublicKey\n ? publicKeyFromBase64(options.releasePublicKey)\n : this.publicKey;\n this.fp = collectFingerprint(options.productToken, options.fingerprintComponents);\n this.fingerprint = this.fp.composite;\n this.doFetch = options.fetch ?? (globalThis.fetch as unknown as FetchLike);\n if (!this.doFetch) throw new TalosError(\"no fetch available (need Node 18+ or options.fetch)\");\n\n // Restore the machine binding synchronously, so a client constructed at\n // startup is already usable: `validate()` works with no `activate()` call\n // and no user prompt for the licence key.\n this.store =\n options.persistState === false\n ? null\n : new StateStore(\n options.statePath ?? defaultStatePath(options.productToken),\n options.productToken,\n stateKeyMaterial(this.fp),\n );\n const stored = this.store?.load() ?? null;\n if (stored) {\n this.binding = stored;\n // Empty means absent, and the distinction is load-bearing now that a\n // binding can exist without an activation: these fields are stored as\n // strings and default to `\"\"`, an air-gapped install has no secret and no\n // id, and an install that has just handed its seat back has neither\n // either. Kept as `\"\"` they are truthy against a `!== null` test, and\n // `isActivated` would answer yes for an install holding nothing.\n this.licenseKeyHash = stored.licenseKeyHash || null;\n this.activationSecret = stored.activationSecret || null;\n this.machineId = stored.machineId || null;\n // Adopt the cached keyset before anything asks for a verification key.\n // Without it an anchor-only client has nothing to verify a cached\n // decision with, and the offline path is unreachable in exactly the\n // situation it exists for.\n if (stored.keyset) {\n try {\n this.adoptKeyset(stored.keyset);\n this.keysetFromCache = this.keysetLicenseKeys.length > 0;\n } catch {\n // A keyset that no longer verifies under the pinned anchor is not a\n // startup failure: fall back to fetching one.\n }\n }\n this.promoteDownloadedRelease();\n }\n }\n\n /**\n * A release that was downloaded and is now the version running: promote its\n * `release_seq` to the anti-downgrade mark.\n *\n * This is the whole of \"the SDK knows what is installed\". The SDK downloads\n * and verifies bytes; the host installs them, its own way, usually across a\n * restart — so installation is not an event the SDK can observe. What it can\n * observe is the result, because `appVersion` is the host saying which\n * version it is. When that equals the version whose bytes were fetched, the\n * download became an install.\n *\n * Only forward, and that is the point: yanking a release or lowering its\n * rollout stops it being *offered*, and must not walk an install that already\n * applied it back onto an older build.\n */\n private promoteDownloadedRelease(): void {\n const pending = this.binding?.pendingRelease;\n if (!pending || pending.version !== this.options.appVersion) return;\n const mark = Math.max(this.binding?.releaseSeqHighWater ?? 0, pending.releaseSeq);\n this.persist({ releaseSeqHighWater: mark, pendingRelease: undefined });\n }\n\n /**\n * The `release_seq` no update may go at or below.\n *\n * The host's explicit value wins when it gives one — it knows things the SDK\n * cannot, such as a build installed by a package manager — and the persisted\n * mark is the default that makes anti-downgrade work for the hosts that give\n * nothing, which is every example and most applications.\n */\n private releaseFloor(explicit?: number): number | undefined {\n return explicit ?? this.binding?.releaseSeqHighWater;\n }\n\n /**\n * Whether this machine already holds an activation — restored from disk on\n * construction, so this is the startup question: prompt for a licence key, or\n * go straight to {@link validate}?\n */\n /**\n * Whether this install holds a licence for this machine.\n *\n * True for an online activation (the activation secret is held) **and** for an\n * air-gapped install that has loaded a licence file. Widened rather than\n * split, because what a host does with this is decide whether to ask the user\n * for a licence key — and an air-gapped machine that already holds a valid\n * file must not be asked for one it has no way to redeem.\n */\n get isActivated(): boolean {\n return this.activationSecret !== null || this.binding?.offlineToken != null;\n }\n\n /** The server's id for this machine, once activated. */\n get currentMachineId(): string | null {\n return this.machineId;\n }\n\n isFeatureEnabled(name: string): boolean {\n return this.current?.isFeatureEnabled(name) ?? false;\n }\n\n /**\n * Fetch and verify the product's keyset (signed by the pinned anchor) and\n * adopt its current license/release keys. Called automatically during\n * {@link activate}; call it again after a server-side key rotation to keep\n * verifying without any config change. No-op if no `anchorPublicKey` is pinned.\n */\n async refreshKeyset(): Promise<void> {\n if (!this.anchorKey) return;\n const generation = this.licensingGeneration;\n const body = await this.post(\"/v1/client/keyset\", {\n product_token: this.options.productToken,\n });\n if (generation !== this.licensingGeneration) return;\n this.adoptKeyset(body[\"keyset\"]);\n this.keysetFromCache = false;\n if (this.keysetToken) this.persist({ keyset: this.keysetToken });\n }\n\n /**\n * Make sure a verification key exists before a response has to be verified.\n *\n * A restored binding goes straight to `validate`, skipping `activate` — which\n * is the only place the keyset used to be adopted. An anchor-only client (the\n * recommended pinning, and the one that survives key rotation) therefore had\n * nothing to verify with on the app's *second* launch, and every call failed\n * with \"keyset unavailable\". Only persistence made that reachable: before it,\n * `activate` always ran first.\n *\n * The keyset is also cached on disk, so the *first* call of a launch with no\n * network can still verify a cached decision. A cached one is refreshed once\n * per process so a rotation is picked up, but a client that cannot reach the\n * server keeps verifying with what it already trusts.\n */\n private async ensureKeyset(): Promise<void> {\n if (!this.anchorKey) return;\n if (this.keysetLicenseKeys.length > 0 && !this.keysetFromCache && !this.keysetIsStale()) {\n return;\n }\n try {\n await this.refreshKeyset();\n } catch (e) {\n // A refresh that cannot reach the server must not break a client that\n // already has keys it trusts — that would make a network problem look\n // like a licensing failure, which is the one thing the whole grace design\n // exists to avoid. With nothing adopted at all there is no fallback, so\n // the failure is the caller's to see.\n if (this.keysetLicenseKeys.length === 0) throw e;\n }\n }\n\n /**\n * Whether the adopted keyset is old enough to be worth re-fetching.\n *\n * This is what makes revoking a compromised signing key mean anything to an\n * install: a revoked key is omitted from the keyset, so a client only stops\n * trusting it once it fetches a newer one. Refreshing only on the first call\n * of a process bounds that by \"next restart\", which for a long-running host is\n * not a bound at all.\n *\n * A day, not an hour: the endpoint is public and unauthenticated, and every\n * install on earth hitting it hourly is a self-inflicted load pattern for a\n * document that changes about once a year. The honest description of the\n * guarantee is in `docs/SECURITY.md` — revocation is bounded, not instant.\n */\n private keysetIsStale(): boolean {\n if (!this.keysetIssuedAt) return true;\n return Date.now() / 1000 - this.keysetIssuedAt > KEYSET_MAX_AGE_SECONDS;\n }\n\n /** Verify a keyset token against the anchor and adopt its keys. */\n private adoptKeyset(token: unknown): void {\n if (!this.anchorKey || typeof token !== \"string\") return;\n const claims = verifyKeyset(this.anchorKey, this.options.productToken, token);\n // From the signed claims, not from a local clock reading taken when the\n // fetch happened: a client whose clock is wrong would otherwise decide its\n // keyset is fresh forever, or refetch on every call.\n const iat = claims[\"iat\"];\n const issuedAt = typeof iat === \"number\" ? iat : 0;\n if (issuedAt < this.keysetIssuedAt\n || (issuedAt === this.keysetIssuedAt && this.keysetToken !== null && token !== this.keysetToken)) return;\n const licenseKeys: KeyObject[] = [];\n const releaseKeys: KeyObject[] = [];\n for (const entry of asArray(claims[\"keys\"])) {\n const o = entry as Record<string, unknown>;\n if (typeof o[\"pk\"] !== \"string\") continue;\n const key = publicKeyFromBase64(o[\"pk\"]);\n if (o[\"purpose\"] === \"license\") licenseKeys.push(key);\n else if (o[\"purpose\"] === \"release\") releaseKeys.push(key);\n }\n this.keysetLicenseKeys = licenseKeys;\n this.keysetReleaseKeys = releaseKeys;\n this.keysetIssuedAt = issuedAt;\n this.keysetToken = token;\n }\n\n /** Keys to verify license tokens with: the keyset if loaded, else the direct pin. */\n private licenseVerifyKeys(): KeyObject[] {\n if (this.keysetLicenseKeys.length) return this.keysetLicenseKeys;\n if (this.publicKey) return [this.publicKey];\n throw new TalosSignatureError(\"no license verification key (keyset unavailable)\");\n }\n\n /** Keys to verify manifests with: the keyset if loaded, else the direct pin. */\n private releaseVerifyKeys(): KeyObject[] {\n if (this.keysetReleaseKeys.length) return this.keysetReleaseKeys;\n if (this.releaseKey) return [this.releaseKey];\n throw new TalosSignatureError(\"no release verification key (keyset unavailable)\");\n }\n\n /**\n * Ensure this machine is activated on `licenseKey`, and return the verified\n * state. Safe to call on every start.\n *\n * When a binding for the same key is already held, this **re-validates**\n * instead of re-activating. Same one round trip, three different outcomes:\n * the activation secret is not rotated (so a second copy of the app on the\n * same machine does not knock the first one out), no seat accounting is\n * touched, and — because `activate` is the only client route refused for a\n * suspended tenant — the developer's own paying users keep working while\n * their vendor's account is suspended, which is what \"graduated suspension\"\n * was supposed to mean.\n *\n * It falls back to a real activation when the stored binding is no longer\n * usable: the seat was released from the portal, or the secret was rotated\n * elsewhere.\n */\n async activate(licenseKey: string): Promise<LicenseState> {\n const generation = this.licensingGeneration;\n try {\n if (this.isActivated && this.licenseKeyHash === hashLicenseKey(licenseKey)) {\n try {\n const state = await this.validate();\n // `deactivated` means the developer released the seat; taking it again\n // is exactly what re-activating is for. Every other verdict —\n // including `machine_revoked` — is the server's answer, and re-\n // activating would only replace it with a less specific error.\n if (state.decision !== \"deactivated\") return state;\n } catch (e) {\n // 401: the secret was rotated elsewhere. 404: the machine (or licence)\n // is gone. Anything else is a real failure and must not be papered\n // over by silently spending a seat.\n const stale =\n e instanceof TalosApiError && (e.statusCode === 401 || e.statusCode === 404);\n if (!stale) throw e;\n }\n this.forgetBinding();\n }\n const nonce = newNonce();\n const body = await this.post(\"/v1/client/activate\", {\n product_token: this.options.productToken,\n license_key: licenseKey,\n fingerprint: { composite: this.fingerprint, components: this.fp.components },\n nonce,\n hostname: this.reportedHostname(),\n os: `${os.type()} ${os.release()}`,\n arch: os.arch(),\n app_version: this.options.appVersion,\n sdk_version: SDK_ID,\n });\n\n const switched = this.offlineSince(generation);\n if (switched) return switched;\n\n // Adopt the keyset delivered with activation, then verify the activation\n // token against the current license keys (so a rotated key still works).\n this.adoptKeyset(body[\"keyset\"]);\n const claims = verifyToken(\n this.licenseVerifyKeys(),\n \"activation\",\n this.options.productToken,\n nonce,\n body[\"activation_token\"] as string,\n );\n // The key is not kept. From here the activation secret identifies both the\n // machine and its licence, so nothing needs the key again until somebody\n // types a different one.\n this.licenseKeyHash = hashLicenseKey(licenseKey);\n this.activationSecret = body[\"activation_secret\"] as string;\n this.machineId = (body[\"machine_id\"] as string) ?? null;\n this.binding = {\n licenseKeyHash: this.licenseKeyHash,\n activationSecret: this.activationSecret,\n machineId: this.machineId ?? \"\",\n fingerprint: this.fingerprint,\n // A fresh activation invalidates any cached verdict for the previous\n // binding; keep only the keyset, which is per product, not per machine.\n keyset: this.keysetToken ?? this.binding?.keyset,\n timeHighWater: this.binding?.timeHighWater,\n // The machine's own identity survives an online activation, because it is\n // an identity and not a verdict: an install that reaches the network once\n // must not have to repeat the whole air-gapped ceremony with its vendor\n // the next time it is unplugged. The clock mark survives for the same\n // reason a mark exists at all — it only moves forward.\n machineKey: this.binding?.machineKey,\n machinePublicKey: this.binding?.machinePublicKey,\n offlineSeen: this.binding?.offlineSeen,\n // The offline *file* does not: a live activation supersedes it, and\n // keeping it would leave two answers to \"what licenses this machine\".\n };\n this.advanceClock(claims);\n this.store?.save(this.binding);\n return this.adopt(stateFromClaims(claims, \"valid\", false));\n } catch (e) {\n const switched = this.offlineSince(generation);\n if (switched) return switched;\n throw e;\n }\n }\n\n /**\n * This machine's own keypair, generated once and kept in the state file.\n *\n * The air-gapped counterpart of the activation secret: a machine that cannot\n * reach a server is never issued one, so it makes an identity of its own and\n * the vendor binds the file to it.\n */\n private machineKey(): MachineKey {\n const held = this.binding?.machineKey;\n const pub = this.binding?.machinePublicKey;\n if (held && pub) return { privateKey: held, publicKey: pub };\n const key = generateMachineKey();\n this.binding = {\n ...this.bindingBase(),\n machineKey: key.privateKey,\n machinePublicKey: key.publicKey,\n };\n this.store?.save(this.binding);\n return key;\n }\n\n /**\n * The binding as it stands, or an empty one to build on.\n *\n * An air-gapped install reaches its first write with nothing stored — no\n * licence key, no activation secret, no machine id — so there has to be\n * something for the offline fields to be added to.\n */\n private bindingBase(): StoredBinding {\n return (\n this.binding ?? {\n licenseKeyHash: \"\",\n activationSecret: \"\",\n machineId: \"\",\n fingerprint: this.fingerprint,\n }\n );\n }\n\n /**\n * Write the request an air-gapped machine hands to its vendor.\n *\n * Save the returned object as `talos-request.json`, carry it to whoever sold\n * the licence, and they upload it in the portal. What comes back is a\n * `license.talos` for {@link loadOfflineLicense} — and only for *this*\n * machine: the request carries the fingerprint and the public half of a\n * keypair generated here, and the file is bound to both.\n *\n * Calling it twice produces two requests under the same identity. The vendor\n * can issue against either; the second file supersedes the first without\n * spending a second seat.\n *\n * ```ts\n * writeFileSync(\"talos-request.json\", JSON.stringify(talos.createOfflineRequest(), null, 2));\n * ```\n *\n * @param licenseKey Optional. The operator picks the licence in the portal, so\n * it is not needed — but when it is present the server checks it against the\n * licence being issued against, which catches issuing from the wrong tab.\n */\n createOfflineRequest(licenseKey?: string): SignedDoc {\n const key = this.machineKey();\n return signMachineDoc(key, REQUEST_DOMAIN, {\n product_token: this.options.productToken,\n license_key: licenseKey,\n fingerprint: { composite: this.fingerprint, components: this.fp.components },\n machine_pubkey: key.publicKey,\n nonce: newNonce(),\n hostname: this.reportedHostname(),\n os: `${os.type()} ${os.release()}`,\n arch: os.arch(),\n app_version: this.options.appVersion,\n sdk_version: SDK_ID,\n created_at: nowSeconds(),\n });\n }\n\n /**\n * Adopt a machine-bound or portable offline licence file.\n *\n * Everything is verified locally, because there is nothing to ask: the\n * anchor-signed keyset travels inside the file, the verdict is signed by a key\n * that keyset lists. Machine-bound files name this machine's fingerprint and\n * public key; explicitly portable files need no machine request. The verdict\n * is kept, so later starts are licensed with no further ceremony.\n *\n * **Throws** only for a file that is not a licence: unreadable, not one of\n * ours, addressed to another product, or a broken signature. A file that *is*\n * one but cannot license this machine right now comes back as a verdict —\n * `machine_mismatch` for another machine's file, `offline_file_expired` for\n * one past its horizon, `expired` for a licence that has ended, and\n * `clock_tampered` for a clock wound back behind what this install has already\n * seen. Those are things the user can be told and can act on, and turning\n * them into exceptions would make the launch path throw for a reason nobody\n * can catch usefully.\n */\n async loadOfflineLicense(pathOrBuffer: string | Buffer | Uint8Array): Promise<LicenseState> {\n const raw =\n typeof pathOrBuffer === \"string\" ? readFileSync(pathOrBuffer) : Buffer.from(pathOrBuffer);\n let doc: Record<string, unknown>;\n try {\n doc = JSON.parse(raw.toString(\"utf8\")) as Record<string, unknown>;\n } catch {\n throw new TalosError(\"not a Talos offline licence file\");\n }\n if (doc[\"kind\"] !== LICENSE_FILE_KIND || typeof doc[\"token\"] !== \"string\") {\n throw new TalosError(\"not a Talos offline licence file\");\n }\n // The keyset is inside the file because an air-gapped machine has nowhere\n // to fetch one from. It is adopted only if the pinned anchor signs it, so a\n // file carrying somebody else's keyset changes nothing about which keys\n // this client trusts.\n const previous = {\n license: this.keysetLicenseKeys, release: this.keysetReleaseKeys,\n iat: this.keysetIssuedAt, token: this.keysetToken,\n };\n const restore = (): void => {\n this.keysetLicenseKeys = previous.license;\n this.keysetReleaseKeys = previous.release;\n this.keysetIssuedAt = previous.iat;\n this.keysetToken = previous.token;\n };\n try {\n if (typeof doc[\"keyset\"] === \"string\") this.adoptKeyset(doc[\"keyset\"]);\n const state = this.adoptOfflineToken(doc[\"token\"], this.keysetToken);\n if (!state.isValid) restore();\n return state;\n } catch (e) {\n restore();\n throw e;\n }\n }\n\n /**\n * Hand this machine's air-gapped seat back.\n *\n * Save the returned object as `talos-deactivation.json` and send it to the\n * vendor; they upload it in the portal and the seat returns to the pool.\n * Signed by the same key the licence file is bound to, which is what makes it\n * worth anything to a server that has never spoken to this machine.\n *\n * **The licence is dropped here, before the document is returned** — so an\n * application that writes the file, or loses it, or never sends it has still\n * stopped using the seat it is giving up. A receipt proves intent, not\n * destruction: the vendor's copy of the file cannot be recalled, and if this\n * machine is later restored from a snapshot it keeps working until that file\n * expires. That is the accepted cost, and it is why the horizon is short.\n *\n * Throws when this install holds no offline licence — there is nothing to hand\n * back, and a document saying otherwise would be a lie the vendor acts on.\n */\n createOfflineDeactivation(): SignedDoc {\n const token = this.binding?.offlineToken;\n if (!token) {\n throw new TalosError(\"no offline licence to hand back\");\n }\n // Read what the file says about itself before dropping it: the receipt has\n // to name the file and the machine, and after `forget` there is nothing to\n // read it from.\n let claims: Record<string, unknown>;\n try {\n claims = verifyCachedToken(\n this.licenseVerifyKeys(),\n \"offline\",\n this.options.productToken,\n token,\n );\n } catch {\n throw new TalosError(\"the stored offline licence no longer verifies\");\n }\n if (claims[\"binding\"] === \"portable\") {\n throw new TalosError(\"portable offline licences have no bound seat to return\");\n }\n const key = this.binding?.machineKey;\n const pub = this.binding?.machinePublicKey;\n if (!key || !pub) throw new TalosError(\"no offline licence to hand back\");\n\n // Drop it first. The order is the point: see above.\n this.binding = { ...this.bindingBase(), offlineToken: undefined };\n this.current = null;\n this.store?.save(this.binding);\n\n return signMachineDoc({ privateKey: key, publicKey: pub }, RECEIPT_DOMAIN, {\n fid: claims[\"fid\"],\n mid: claims[\"mid\"],\n fph: this.fingerprint,\n local_time: nowSeconds(),\n });\n }\n\n /**\n * Verify the offline file against this machine and this clock, and adopt the\n * verdict. Runs on load and again on every {@link validate}, because the two\n * things that can change afterwards — the clock and the calendar — are\n * exactly what it checks.\n */\n private adoptOfflineToken(token: string, keyset?: unknown): LicenseState {\n // `verifyCachedToken` rather than `verifyToken`: it checks the signature,\n // `typ`, `aud` and `nbf`, and leaves `exp` and the nonce to the caller. Both\n // omissions are right here. The nonce belonged to a request document that\n // may be weeks in the past and is not kept — it is the *server* that needs\n // it, where it is unique per account and stops a captured request minting a\n // second file. And `exp` is a verdict rather than an exception, below.\n const claims = verifyCachedToken(\n this.licenseVerifyKeys(),\n \"offline\",\n this.options.productToken,\n token,\n );\n\n const mismatch = (): LicenseState =>\n this.adopt(new LicenseState(\"machine_mismatch\", {}, null, true, null, null));\n const lic = (claims[\"lic\"] ?? {}) as Record<string, unknown>;\n const features = (lic[\"features\"] ?? {}) as Record<string, unknown>;\n const licExp = num(lic[\"lic_exp\"]);\n const exp = num(claims[\"exp\"]);\n const perpetual = claims[\"perpetual\"] === true\n && claims[\"exp\"] === null && lic[\"lic_exp\"] === null;\n const portable = claims[\"binding\"] === \"portable\";\n if (portable) {\n // Portability is a distinct signed grant, never a fallback for a file\n // with incomplete machine claims. Null machine claims are invalid too.\n const present = (key: string): boolean => Object.hasOwn(claims, key);\n const named = (key: string): boolean =>\n typeof claims[key] === \"string\" && (claims[key] as string).trim().length > 0;\n if (!perpetual || claims[\"decision\"] !== \"valid\"\n || present(\"fph\") || present(\"mpk\") || present(\"mid\")\n || !named(\"fid\") || !named(\"sub\")) return mismatch();\n } else if (claims[\"binding\"] !== undefined && claims[\"binding\"] !== \"machine\") {\n return mismatch();\n }\n // Machine-bound files require both halves. The fingerprint stops the state\n // directory being copied to another computer, and the key is what stops the\n // file alone being worth anything — neither is redundant, and accepting\n // either on its own would open the case the other one closes.\n if (!portable && (typeof claims[\"mpk\"] !== \"string\" || claims[\"mpk\"] !== this.binding?.machinePublicKey)) {\n return mismatch();\n }\n if (!portable && claims[\"fph\"] !== this.fingerprint) return mismatch();\n\n const now = nowSeconds();\n // Both marks are lower bounds on \"now\" that this install has already\n // observed: one signed by the server, one its own. A clock behind either\n // has moved backwards.\n const mark = Math.max(this.binding?.timeHighWater ?? 0, this.binding?.offlineSeen ?? 0);\n if (mark > 0 && now + CLOCK_SKEW_SECONDS < mark) {\n return this.adopt(new LicenseState(\"clock_tampered\", {}, null, true, null, null));\n }\n\n // Bounded files end at their own horizon. Explicit perpetual files need no\n // renewal; an omitted or malformed deadline still fails closed.\n // Distinct from `expired`, which is the *licence* ending — the remedies are\n // different (a new file versus a renewal) and the app should be able to say\n // which one to ask for.\n if (!perpetual && (exp === null || now > exp)) {\n return this.adopt(\n new LicenseState(\"offline_file_expired\", features, licExp, true, null, exp),\n );\n }\n if (licExp !== null && now > licExp) {\n return this.adopt(new LicenseState(\"expired\", features, licExp, true, null, exp));\n }\n\n // A valid offline file replaces an online activation. Invalid imports must\n // leave that activation and its cache intact. Keep machine keys and update\n // trust/anti-downgrade marks, which belong to the installation.\n this.licensingGeneration += 1;\n this.licenseKeyHash = null;\n this.activationSecret = null;\n this.heartbeatInterval = null;\n this.leaseGeneration += 1;\n this.stopRenew();\n this.lease = null;\n this.machineId = !portable && typeof claims[\"mid\"] === \"string\" ? claims[\"mid\"] : null;\n const base = this.bindingBase();\n this.binding = {\n ...base,\n licenseKeyHash: \"\",\n activationSecret: \"\",\n validation: undefined,\n machineId: this.machineId ?? \"\",\n fingerprint: this.fingerprint,\n offlineToken: token,\n offlineSeen: Math.max(base.offlineSeen ?? 0, now),\n ...(typeof keyset === \"string\" ? { keyset } : {}),\n };\n this.advanceClock(claims);\n this.store?.save(this.binding);\n return this.adopt(new LicenseState(\"valid\", features, licExp, true, null, exp));\n }\n\n /**\n * The heartbeat cadence the server's policy asks for, in seconds, taken from\n * the last heartbeat's **signed** claims — so it cannot be lengthened in\n * transit by anyone wanting a fleet to check in less often.\n *\n * `null` until the first heartbeat of this process.\n */\n get heartbeatIntervalSeconds(): number | null {\n return this.heartbeatInterval;\n }\n\n /**\n * Subscribe to a licence transition. Returns a function that unsubscribes.\n *\n * ```ts\n * const off = talos.on(\"gracePeriodStarted\", (s) => showOfflineBanner(s.graceUntil));\n * ```\n *\n * Fires from every path that adopts a verdict — `activate`, `validate`,\n * `heartbeat`, and the offline fallback — not only from the background loop,\n * so a host that runs its own timer gets the same transitions. The listener\n * runs synchronously inside that call; a listener that throws is ignored (see\n * {@link emit}).\n *\n * `updateAvailable` is the one event that is not a licence transition: it\n * carries an {@link UpdateInfo}, and subscribing to it is what makes the\n * heartbeat ask the server whether a release is waiting.\n */\n on<E extends TalosEventName>(\n event: E,\n listener: (payload: TalosEventMap[E]) => void,\n ): () => void {\n let set = this.listeners.get(event);\n if (!set) {\n set = new Set();\n this.listeners.set(event, set);\n }\n // The registry is one map for five payload types; `never` is the only\n // parameter every listener is assignable to, and the cast back at emit is\n // where the type is put on again.\n const fn = listener as (payload: never) => void;\n set.add(fn);\n return () => {\n set.delete(fn);\n };\n }\n\n /**\n * Adopt a verdict as the current one, announcing the transition it makes.\n *\n * The single funnel every path goes through, which is what makes the events\n * trustworthy: a verdict reached anywhere else would be a transition nobody\n * hears about.\n */\n private adopt(state: LicenseState): LicenseState {\n const event = transitionEvent(this.current, state);\n this.current = state;\n if (event) this.emit(event, state);\n return state;\n }\n\n private emit<E extends TalosEventName>(event: E, payload: TalosEventMap[E]): void {\n for (const listener of this.listeners.get(event) ?? []) {\n try {\n (listener as (p: TalosEventMap[E]) => void)(payload);\n } catch {\n // The verdict is already decided, and this is being raised from inside\n // `validate`, whose own catch treats a throw as an unreachable server.\n // A host's broken banner code must not turn a live \"revoked\" answer\n // into offline grace.\n }\n }\n }\n\n /**\n * Run the check-ins the server's policy asks for, in the background.\n *\n * Two knobs have existed on the wire since Phase 1 and reached no client:\n * `heartbeat_interval_s` (how often an install reports in — the telemetry\n * rate, and the upper bound on how long a revocation takes to arrive) and\n * `reval_after` (when the entitlement bag and the offline cache should be\n * refreshed). Both are signed. This loop is what makes them a lever the\n * developer can actually pull: change the policy, and deployed apps follow.\n *\n * One timer serves both. Each tick re-validates if the signed `reval_after`\n * has passed, and otherwise sends a heartbeat — which is also what renews the\n * cached verdict that {@link validate} falls back on, so an app that runs\n * this stays inside its grace window without the host writing any scheduling\n * of its own.\n *\n * Opt-in, and idempotent: calling it again replaces the running loop.\n */\n startCheckIns(opts: CheckInOptions = {}): void {\n this.stopCheckIns();\n const generation = ++this.checkInGeneration;\n const tick = async (): Promise<void> => {\n try {\n const due = this.current?.revalidateAfter;\n const state =\n due === null || due === undefined || nowSeconds() >= due\n ? await this.validate()\n : await this.heartbeat();\n if (generation !== this.checkInGeneration) return;\n opts.onState?.(state);\n } catch (e) {\n if (generation !== this.checkInGeneration) return;\n opts.onError?.(e);\n }\n // Re-arm even after a failure: a network outage must not silently end\n // the loop, or the app would never notice the connection coming back.\n if (generation === this.checkInGeneration) schedule();\n };\n const schedule = (): void => {\n const secs = Math.max(60, this.heartbeatInterval ?? DEFAULT_HEARTBEAT_SECONDS);\n this.checkInTimer = setTimeout(() => void tick(), secs * 1000);\n this.checkInTimer.unref?.();\n };\n schedule();\n }\n\n /** Stop the background check-ins started by {@link startCheckIns}. */\n stopCheckIns(): void {\n this.checkInGeneration += 1;\n if (this.checkInTimer) {\n clearTimeout(this.checkInTimer);\n this.checkInTimer = null;\n }\n }\n\n /** Drop the machine binding, in memory and on disk. */\n private forgetBinding(): void {\n this.licensingGeneration += 1;\n this.licenseKeyHash = null;\n this.activationSecret = null;\n this.machineId = null;\n this.current = null;\n this.binding = null;\n this.store?.clear();\n }\n\n /** Merge fields into the persisted binding and write it back. */\n private persist(patch: Partial<StoredBinding>): void {\n if (!this.store || !this.binding) return;\n this.binding = { ...this.binding, ...patch };\n this.store.save(this.binding);\n }\n\n /**\n * Advance the server-time high-water mark from a signed claim.\n *\n * Only ever forward: this is the mark that keeps offline grace bounded when\n * the user owns the clock. `iat` is used when a token carries no explicit\n * `server_time` (activation does not) — both are the server's clock, signed.\n */\n private advanceClock(claims: Record<string, unknown>): void {\n const seen = num(claims[\"server_time\"]) ?? num(claims[\"iat\"]);\n if (seen === null) return;\n const current = this.binding?.timeHighWater ?? 0;\n if (seen > current) {\n if (this.binding) this.binding.timeHighWater = seen;\n }\n }\n\n async validate(): Promise<LicenseState> {\n // An air-gapped install has no server to ask and no secret to ask with. Its\n // file *is* the verdict — re-checked here rather than cached, because the\n // two things that can have changed since it was loaded are precisely what\n // that check is about: the clock and the calendar.\n if (!this.activationSecret && this.binding?.offlineToken) {\n return this.adoptOfflineToken(this.binding.offlineToken);\n }\n this.requireActivated();\n const generation = this.licensingGeneration;\n try {\n await this.ensureKeyset();\n const switchedBeforeRequest = this.offlineSince(generation);\n if (switchedBeforeRequest) return switchedBeforeRequest;\n const nonce = newNonce();\n const body = await this.post(\"/v1/client/validate\", {\n product_token: this.options.productToken,\n fingerprint: { composite: this.fingerprint, components: this.fp.components },\n activation_secret: this.activationSecret,\n nonce,\n // Sent here as well as on activation and heartbeat, because check-ins\n // are opt-in and this call is not: an application that validates on\n // launch and never calls `startCheckIns` would otherwise show, forever,\n // whatever version it activated on.\n app_version: this.options.appVersion,\n sdk_version: SDK_ID,\n });\n const switched = this.offlineSince(generation);\n if (switched) return switched;\n const token = body[\"validation_token\"] as string;\n const claims = verifyToken(\n this.licenseVerifyKeys(),\n \"validation\",\n this.options.productToken,\n nonce,\n token,\n );\n const decision =\n typeof claims[\"decision\"] === \"string\" ? (claims[\"decision\"] as string) : \"invalid\";\n this.advanceClock(claims);\n this.persist({ validation: token, timeHighWater: this.binding?.timeHighWater });\n return this.adopt(stateFromClaims(claims, decision, false));\n } catch (e) {\n const switched = this.offlineSince(generation);\n if (switched) return switched;\n // An HTTP status is an *answer* — \"revoked\", \"no such machine\", \"tenant\n // suspended\". Honouring a cached verdict over one would turn every\n // negative answer into 72 hours of grace, which is the opposite of what\n // grace is for. Only the absence of an answer falls back: a dead network,\n // or a response that fails verification (a garbled or forged reply tells\n // us nothing, so per the trust model it is treated as unreachable, never\n // as valid).\n if (e instanceof TalosApiError) throw e;\n const offline = this.offlineDecision();\n if (!offline) throw e;\n return this.adopt(offline);\n }\n }\n\n /**\n * The verdict from the local cache, or null when there is nothing usable.\n *\n * This is the whole offline story, and every check here is load-bearing:\n *\n * - the token is re-verified under the pinned key, so a cache edited on disk\n * is worthless;\n * - `fph` must be this machine, so copying a state file to a second computer\n * and pulling the network cable does not licence it;\n * - `grace_until` — the policy's `offline_grace_hours`, signed — bounds how\n * long a verdict may outlive its issuance;\n * - the licence's own expiry is re-checked, because a cached \"valid\" must not\n * outlive the subscription it was issued under;\n * - and the clock is checked against a mark that only moves forward, since\n * every deadline above is evaluated against a clock the user controls.\n */\n private offlineDecision(): LicenseState | null {\n const token = this.binding?.validation;\n if (!token) return null;\n const now = nowSeconds();\n\n const highWater = this.binding?.timeHighWater ?? 0;\n if (highWater > 0 && now + CLOCK_SKEW_SECONDS < highWater) {\n // Distinct from \"expired\": nothing has run out, the machine is lying\n // about the time. Say so, rather than reporting a licence problem the\n // user cannot act on.\n return new LicenseState(\"clock_tampered\", {}, null, true, null, null);\n }\n\n let claims: Record<string, unknown>;\n try {\n claims = verifyCachedToken(\n this.licenseVerifyKeys(),\n \"validation\",\n this.options.productToken,\n token,\n );\n } catch {\n return null;\n }\n if (claims[\"fph\"] !== this.fingerprint) return null;\n\n const graceUntil = num(claims[\"grace_until\"]);\n const cached = stateFromClaims(\n claims,\n typeof claims[\"decision\"] === \"string\" ? (claims[\"decision\"] as string) : \"invalid\",\n true,\n );\n if (graceUntil === null || now > graceUntil) {\n return new LicenseState(\n \"grace_expired\",\n cached.features,\n cached.licenseExpiresAt,\n true,\n cached.revalidateAfter,\n graceUntil,\n );\n }\n if (cached.licenseExpiresAt !== null && now > cached.licenseExpiresAt) {\n return new LicenseState(\n \"expired\",\n cached.features,\n cached.licenseExpiresAt,\n true,\n cached.revalidateAfter,\n graceUntil,\n );\n }\n return cached;\n }\n\n async deactivate(): Promise<void> {\n this.requireActivated();\n const generation = this.licensingGeneration;\n await this.post(\"/v1/client/deactivate\", {\n product_token: this.options.productToken,\n fingerprint: { composite: this.fingerprint, components: this.fp.components },\n activation_secret: this.activationSecret,\n });\n if (generation === this.licensingGeneration) this.forgetBinding();\n }\n\n /**\n * Send a heartbeat: refreshes this machine's last-seen state, feeds the\n * developer's monitoring (version adoption / active installs), and returns the\n * current signed decision — so a revocation reaches the app within one\n * heartbeat interval. Call it periodically while the app runs.\n *\n * The response is a signed `typ=heartbeat` token, so a MITM cannot forge the\n * status or lengthen the interval.\n */\n /**\n * The machine name to report, or `undefined` to report none.\n *\n * An explicit `\"\"` means \"do not send\": omitting the field leaves whatever\n * the server already holds, because the server coalesces, so sending an empty\n * string would not clear it either — suppression has to happen here.\n */\n private reportedHostname(): string | undefined {\n const h = this.options.hostname ?? os.hostname();\n return h === \"\" ? undefined : h;\n }\n\n async heartbeat(): Promise<LicenseState> {\n this.requireActivated();\n const generation = this.licensingGeneration;\n try {\n await this.ensureKeyset();\n const switchedBeforeRequest = this.offlineSince(generation);\n if (switchedBeforeRequest) return switchedBeforeRequest;\n const nonce = newNonce();\n const body = await this.post(\"/v1/client/heartbeat\", {\n product_token: this.options.productToken,\n fingerprint: { composite: this.fingerprint, components: this.fp.components },\n activation_secret: this.activationSecret,\n nonce,\n app_version: this.options.appVersion,\n os: `${os.type()} ${os.release()}`,\n arch: os.arch(),\n sdk_version: SDK_ID,\n // Naming the channel is what asks the server for the update hint, and it\n // costs two queries there — so it is asked for only when somebody is\n // listening for the answer.\n update_channel: this.listeners.get(\"updateAvailable\")?.size\n ? this.updateChannel()\n : undefined,\n });\n const switched = this.offlineSince(generation);\n if (switched) return switched;\n const claims = verifyToken(\n this.licenseVerifyKeys(),\n \"heartbeat\",\n this.options.productToken,\n nonce,\n body[\"heartbeat_token\"] as string,\n );\n const decision =\n typeof claims[\"decision\"] === \"string\" ? (claims[\"decision\"] as string) : \"invalid\";\n // The cadence comes from the SIGNED claims, never the response envelope:\n // the whole point of signing it is that nobody in the middle can tell a\n // fleet to check in less often.\n const interval = num(claims[\"heartbeat_interval_s\"]);\n if (interval !== null && interval > 0) this.heartbeatInterval = interval;\n // Every signed server clock we see pushes the high-water mark forward, not\n // just validation's — a heartbeat is the most frequent call there is.\n this.advanceClock(claims);\n this.persist({ timeHighWater: this.binding?.timeHighWater });\n // A heartbeat carries the decision but not the entitlement bag; keep the\n // features from the last activate/validate.\n const state = this.adopt(\n new LicenseState(\n decision,\n this.current?.features ?? {},\n this.current?.licenseExpiresAt ?? null,\n false,\n this.current?.revalidateAfter ?? null,\n this.current?.graceUntil ?? null,\n ),\n );\n await this.announceUpdate(claims);\n const switchedAfterUpdate = this.offlineSince(generation);\n if (switchedAfterUpdate) return switchedAfterUpdate;\n return state;\n } catch (e) {\n const switched = this.offlineSince(generation);\n if (switched) return switched;\n throw e;\n }\n }\n\n /** The channel this install follows; `stable` unless set. */\n private updateChannel(): string {\n return this.options.updateChannel || \"stable\";\n }\n\n /**\n * Act on the heartbeat's `upd` hint: confirm it with a real update check and\n * emit `updateAvailable` for a release not announced yet.\n *\n * The hint is read from the **signed** claims, not the response envelope, and\n * it is only ever a prompt to go and look: the check applies this install's\n * anti-downgrade floor and verifies a manifest, neither of which the hint\n * does. A hint that turns out to be wrong costs one request and emits\n * nothing.\n *\n * A failing check is swallowed. The heartbeat it rode in on has already\n * succeeded, and a licensing call must not start failing because the update\n * service is having a bad day.\n */\n private async announceUpdate(claims: Record<string, unknown>): Promise<void> {\n if (!this.listeners.get(\"updateAvailable\")?.size) return;\n if (claims[\"upd\"] !== true) return;\n let info: UpdateInfo | null;\n try {\n info = await this.checkForUpdate();\n } catch {\n return;\n }\n if (!info || info.releaseSeq <= this.announcedReleaseSeq) return;\n this.announcedReleaseSeq = info.releaseSeq;\n this.emit(\"updateAvailable\", info);\n }\n\n /**\n * Acquire a floating seat (concurrent-use license). The server arbitrates the\n * cap atomically, so this either returns a seat or throws\n * {@link TalosApiError} with `seat_limit_reached` (HTTP 409).\n *\n * By default the seat is auto-renewed in the background at the server-chosen\n * interval; a lost renewal invokes `onLeaseLost`. Always\n * {@link releaseLease} on clean exit — a crash simply lets the seat expire.\n */\n async acquireLease(opts: AcquireLeaseOptions = {}): Promise<Lease> {\n this.requireActivated();\n await this.ensureKeyset();\n const instanceId = opts.instanceId ?? this.instanceId;\n // Snapshot the generation: if releaseLease() runs while this request is in\n // flight, we must not adopt the seat we just took (and should hand it back).\n const generation = this.leaseGeneration;\n const nonce = newNonce();\n const body = await this.post(\"/v1/client/leases\", {\n product_token: this.options.productToken,\n fingerprint: { composite: this.fingerprint, components: this.fp.components },\n activation_secret: this.activationSecret,\n instance_id: instanceId,\n nonce,\n });\n\n // The lease token is signed: a MITM cannot fabricate a seat.\n const claims = verifyToken(\n this.licenseVerifyKeys(),\n \"lease\",\n this.options.productToken,\n nonce,\n body[\"lease_token\"] as string,\n );\n\n // The signed grant must be for the instance and machine we asked about —\n // otherwise a server could hand every process one shared seat's token.\n if (claims[\"inst\"] !== instanceId || claims[\"fph\"] !== this.fingerprint) {\n throw new TalosSignatureError(\"lease token does not match this instance\");\n }\n\n // Seat facts come from the SIGNED claims, never the unsigned envelope, so a\n // MITM cannot misreport occupancy. `renew_after_s` is only a scheduling\n // hint; the budget is derived from the token's own clock (`exp` vs\n // `server_time`) so a wrong local clock cannot collapse the renew loop.\n const expiresAt = typeof claims[\"exp\"] === \"number\" ? (claims[\"exp\"] as number) : 0;\n const serverNow =\n typeof claims[\"server_time\"] === \"number\"\n ? (claims[\"server_time\"] as number)\n : Math.floor(Date.now() / 1000);\n const budget = Math.max(1, expiresAt - serverNow);\n const hinted = Number(body[\"renew_after_s\"] ?? 0);\n const lease: Lease = {\n id: String(claims[\"lease\"] ?? \"\"),\n instanceId,\n expiresAt,\n renewAfterSeconds:\n hinted > 0 && hinted <= budget ? hinted : Math.max(1, Math.floor(budget / 3)),\n seatsInUse: Number(claims[\"seats_in_use\"] ?? 0),\n maxSeats: Number(claims[\"max_seats\"] ?? 0),\n };\n\n if (generation !== this.leaseGeneration) {\n // Released while this was in flight: hand the seat straight back instead\n // of holding it until the TTL expires, and never re-adopt it locally.\n void this.releaseInstance(instanceId).catch(() => {});\n return lease;\n }\n this.lease = lease;\n\n if (opts.autoRenew !== false) this.scheduleRenew(opts);\n return lease;\n }\n\n /** Best-effort DELETE of one instance's seat (used for stale-renewal cleanup). */\n private async releaseInstance(instanceId: string): Promise<void> {\n if (!this.activationSecret) return;\n await this.request(\"DELETE\", \"/v1/client/leases\", {\n product_token: this.options.productToken,\n fingerprint: { composite: this.fingerprint, components: this.fp.components },\n activation_secret: this.activationSecret,\n instance_id: instanceId,\n });\n }\n\n /** The currently held floating seat, if any. */\n get currentLease(): Lease | null {\n return this.lease;\n }\n\n /** Release the floating seat (clean exit) and stop auto-renewal. */\n async releaseLease(): Promise<void> {\n // Bump the generation FIRST: an already in-flight renewal will see that it\n // is stale and neither re-adopt the seat nor re-arm the timer. Without this\n // a renewal racing the release resurrects the lease for the process\n // lifetime — the seat would stay held long after the app \"released\" it.\n this.leaseGeneration += 1;\n this.stopRenew();\n const lease = this.lease;\n this.lease = null;\n if (!lease) return;\n await this.releaseInstance(lease.instanceId);\n }\n\n private scheduleRenew(opts: AcquireLeaseOptions): void {\n this.stopRenew();\n const generation = this.leaseGeneration;\n const instanceId = this.lease?.instanceId;\n const delay = Math.max(1, this.lease?.renewAfterSeconds ?? 200) * 1000;\n this.renewTimer = setTimeout(() => {\n void (async () => {\n try {\n await this.acquireLease({ ...opts, instanceId, autoRenew: false });\n // Released (or re-acquired) while we were awaiting → do not re-adopt.\n if (generation !== this.leaseGeneration) return;\n this.scheduleRenew(opts);\n } catch (e) {\n if (generation !== this.leaseGeneration) return;\n this.lease = null;\n opts.onLeaseLost?.(e instanceof Error ? e.message : \"lease renewal failed\");\n }\n })();\n }, delay);\n // Never hold the event loop open for a background renewal.\n this.renewTimer.unref?.();\n }\n\n private stopRenew(): void {\n if (this.renewTimer) {\n clearTimeout(this.renewTimer);\n this.renewTimer = null;\n }\n }\n\n /**\n * Ask whether a newer release is available. Returns a verified {@link UpdateInfo}\n * (all metadata comes from the manifest signed by the pinned release key), or\n * `null` when up to date.\n *\n * Enforces anti-downgrade: a manifest whose `release_seq` is at or below the\n * floor is rejected as no update. The floor is the persisted high-water mark\n * — the highest `release_seq` this install has been seen running — unless\n * {@link CheckForUpdateOptions.currentReleaseSeq} overrides it.\n */\n async checkForUpdate(opts: CheckForUpdateOptions = {}): Promise<UpdateInfo | null> {\n this.requireActivated();\n await this.ensureKeyset();\n const body = await this.post(\"/v1/client/updates/check\", {\n product_token: this.options.productToken,\n fingerprint: { composite: this.fingerprint, components: this.fp.components },\n activation_secret: this.activationSecret,\n current_version: opts.currentVersion ?? this.options.appVersion,\n current_release_seq: this.releaseFloor(opts.currentReleaseSeq),\n channel: opts.channel ?? this.updateChannel(),\n });\n if (body[\"update_available\"] !== true) return null;\n\n const manifestToken = body[\"manifest\"];\n if (typeof manifestToken !== \"string\") {\n throw new TalosSignatureError(\"update available but no manifest\");\n }\n // Trust only the signed manifest for version/seq/artifact metadata.\n const m = verifyManifest(this.releaseVerifyKeys(), this.options.productToken, manifestToken);\n const releaseSeq = typeof m[\"release_seq\"] === \"number\" ? (m[\"release_seq\"] as number) : 0;\n const floor = this.releaseFloor(opts.currentReleaseSeq);\n if (floor !== undefined && releaseSeq <= floor) {\n // Anti-downgrade, checked here as well as on the server: the server's\n // answer is the one an attacker replaces, and this one is checked against\n // a mark on this machine and a manifest signed by the pinned key.\n return null;\n }\n\n // Untrusted transport map: platform/arch/kind → download path.\n const downloads = new Map<string, string>();\n for (const d of asArray(body[\"artifacts\"])) {\n const o = d as Record<string, unknown>;\n if (typeof o[\"download_path\"] === \"string\") {\n downloads.set(selKey(o[\"platform\"], o[\"arch\"], o[\"kind\"]), o[\"download_path\"]);\n }\n }\n\n const artifacts: UpdateArtifact[] = asArray(m[\"artifacts\"]).map((a) => {\n const o = a as Record<string, unknown>;\n const platform = String(o[\"platform\"] ?? \"\");\n const arch = String(o[\"arch\"] ?? \"\");\n const kind = String(o[\"kind\"] ?? \"\");\n return {\n platform,\n arch,\n kind,\n filename: String(o[\"filename\"] ?? \"\"),\n size: typeof o[\"size\"] === \"number\" ? (o[\"size\"] as number) : 0,\n sha256: String(o[\"sha256\"] ?? \"\"),\n downloadPath: downloads.get(selKey(platform, arch, kind)),\n };\n });\n\n // Patches, keyed by what distinguishes them from each other: the version\n // they apply to and the file they rebuild. A release that supports upgrading\n // from three versions and ships two files carries six.\n const deltaPaths = new Map<string, string>();\n for (const d of asArray(body[\"deltas\"])) {\n const o = d as Record<string, unknown>;\n if (typeof o[\"download_path\"] === \"string\") {\n deltaPaths.set(\n deltaKey(o[\"platform\"], o[\"arch\"], o[\"from\"], o[\"target_kind\"]),\n o[\"download_path\"],\n );\n }\n }\n\n const deltas: UpdateDelta[] = asArray(m[\"deltas\"]).map((d) => {\n const o = d as Record<string, unknown>;\n const t = (o[\"target\"] ?? {}) as Record<string, unknown>;\n const platform = String(o[\"platform\"] ?? \"\");\n const arch = String(o[\"arch\"] ?? \"\");\n const from = String(o[\"from\"] ?? \"\");\n const targetKind = String(t[\"kind\"] ?? \"\");\n return {\n platform,\n arch,\n from,\n fromSeq: typeof o[\"from_seq\"] === \"number\" ? (o[\"from_seq\"] as number) : 0,\n filename: String(o[\"filename\"] ?? \"\"),\n size: typeof o[\"size\"] === \"number\" ? (o[\"size\"] as number) : 0,\n sha256: String(o[\"sha256\"] ?? \"\"),\n target: {\n kind: targetKind,\n filename: String(t[\"filename\"] ?? \"\"),\n size: typeof t[\"size\"] === \"number\" ? (t[\"size\"] as number) : 0,\n sha256: String(t[\"sha256\"] ?? \"\"),\n },\n downloadPath: deltaPaths.get(deltaKey(platform, arch, from, targetKind)),\n };\n });\n\n return {\n version: String(m[\"version\"] ?? \"\"),\n releaseSeq,\n mandatory: m[\"mandatory\"] === true,\n notesUrl: typeof m[\"notes_url\"] === \"string\" ? (m[\"notes_url\"] as string) : null,\n artifacts,\n deltas,\n };\n }\n\n /**\n * The patch that upgrades this machine, or `null` if there is none.\n *\n * Matches on the *exact* published version string, not on version ordering: a\n * patch applies to the bytes it was built against, and \"close enough\" produces\n * a corrupt file rather than an error. So a machine whose installed version is\n * not one the release ships a patch from simply downloads the full build.\n */\n selectDelta(info: UpdateInfo, opts: DeltaOptions = {}): UpdateDelta | null {\n const from = opts.from ?? this.options.appVersion;\n if (!from) return null;\n const { platform, arch } = hostTarget();\n const matches = info.deltas.filter(\n (d) => d.platform === platform && d.arch === arch && d.from === from,\n );\n if (opts.targetKind) return matches.find((d) => d.target.kind === opts.targetKind) ?? null;\n // Ambiguity is refused rather than guessed. Picking one of two patches for\n // an installer and a portable archive would download megabytes and rebuild\n // the file the host was not updating.\n return matches.length === 1 ? matches[0]! : null;\n }\n\n /**\n * Download a patch and verify its bytes against the manifest's SHA-256, then\n * write it to `destPath`. Returns the patch, whose `target.sha256` is what the\n * rebuilt file must hash to.\n *\n * Does NOT apply it. Applying is the host's job — and so is checking the\n * result, which {@link verifyAppliedUpdate} does. A patch verified on the way\n * in and a result nobody checks is a signed chain that stops one step short of\n * the binary that runs.\n */\n async downloadDelta(\n info: UpdateInfo,\n destPath: string,\n opts: DeltaOptions & { delta?: UpdateDelta; onProgress?: DownloadProgressCallback } = {},\n ): Promise<UpdateDelta> {\n const chosen = opts.delta ?? this.selectDelta(info, opts);\n if (!chosen) throw new TalosError(\"no patch applies to this install\");\n await this.fetchVerified(\n chosen.downloadPath,\n chosen.size,\n chosen.sha256,\n destPath,\n opts.onProgress,\n );\n this.rememberDownloaded(info);\n return chosen;\n }\n\n /**\n * Download one artifact of an update and verify its bytes against the manifest\n * SHA-256, then write it to `destPath`. Returns the chosen artifact. Does NOT\n * apply or execute anything — verification and installation are the host app's\n * responsibility.\n *\n * `onProgress` is called as the bytes arrive, which is the only way a host can\n * show a bar: an installer takes minutes on a domestic connection, and a\n * promise that says nothing until it settles is indistinguishable from one that\n * has hung. Totals come from the signed manifest — see {@link DownloadProgress}.\n */\n async downloadUpdate(\n info: UpdateInfo,\n destPath: string,\n artifact?: UpdateArtifact,\n onProgress?: DownloadProgressCallback,\n ): Promise<UpdateArtifact> {\n this.requireActivated();\n const chosen = artifact ?? pickArtifact(info.artifacts);\n if (!chosen) throw new TalosError(\"no artifact matches this platform\");\n await this.fetchVerified(chosen.downloadPath, chosen.size, chosen.sha256, destPath, onProgress);\n this.rememberDownloaded(info);\n return chosen;\n }\n\n /**\n * Note which release's bytes are now on disk, so the next start can tell\n * whether they were installed.\n *\n * Written after the hash check, never before: a download that failed\n * verification is not a release this install is about to be running, and\n * recording it would let a corrupt or substituted file raise the\n * anti-downgrade floor past a good build.\n */\n private rememberDownloaded(info: UpdateInfo): void {\n if (info.releaseSeq <= (this.binding?.releaseSeqHighWater ?? 0)) return;\n this.persist({ pendingRelease: { version: info.version, releaseSeq: info.releaseSeq } });\n }\n\n /**\n * Fetch one file over the entitled download route and put it at `destPath`\n * only once its bytes match the size and hash the signed manifest names.\n * Shared by full builds and patches, which differ in what they are for and not\n * at all in how their bytes are checked.\n *\n * The bytes go straight to a `.talos-part` file beside the destination, hashed\n * as they arrive, and are renamed into place only once the hash matches. Two\n * things follow. A multi-gigabyte installer is never held in memory — the whole\n * body used to be buffered before it was checked, so the largest artifact a\n * product could ship was bounded by the smallest machine it ran on. And\n * `destPath` never holds unverified bytes, not even briefly: a download that\n * fails leaves whatever was there before rather than a truncated file a host\n * might run.\n */\n private async fetchVerified(\n downloadPath: string | undefined,\n size: number,\n sha256: string,\n destPath: string,\n onProgress?: DownloadProgressCallback,\n ): Promise<void> {\n if (!downloadPath) throw new TalosError(\"artifact has no download path\");\n const url = this.options.serverUrl.replace(/\\/+$/, \"\") + downloadPath;\n const res = await this.doFetch(url, {\n method: \"GET\",\n headers: {\n \"x-talos-product-token\": this.options.productToken,\n \"x-talos-activation-secret\": this.activationSecret!,\n \"x-talos-fingerprint\": this.fingerprint,\n },\n });\n if (res.status < 200 || res.status >= 300) throw new TalosApiError(res.status, undefined);\n\n // The download channel is untrusted (could stream an unbounded body to\n // exhaust memory before the hash check). Cap the read at the manifest's\n // trusted `size`, then verify the SHA-256 from the signed manifest.\n const contentLength = res.headers?.get(\"content-length\");\n if (contentLength && Number(contentLength) > size) {\n throw new TalosSignatureError(\"artifact exceeds manifest size\");\n }\n\n const partPath = destPath + \".talos-part\";\n const handle = await open(partPath, \"w\");\n const hash = createHash(\"sha256\");\n let received = 0;\n let closed = false;\n const take = async (chunk: Uint8Array): Promise<void> => {\n received += chunk.byteLength;\n if (received > size) throw new TalosSignatureError(\"artifact exceeds manifest size\");\n hash.update(chunk);\n await handle.write(chunk);\n onProgress?.({ bytesReceived: received, totalBytes: size });\n };\n try {\n const body = res.body;\n if (body) {\n const reader = body.getReader();\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done || !value) break;\n await take(value);\n }\n } catch (e) {\n await reader.cancel().catch(() => {});\n throw e;\n }\n } else {\n // Nothing to meter, so this one arrives whole. The real `fetch` always\n // has a stream; this is the path an injected implementation takes.\n await take(new Uint8Array(await res.arrayBuffer()));\n }\n\n if (hash.digest(\"hex\") !== sha256) {\n throw new TalosSignatureError(\"artifact hash mismatch (tampered or wrong bytes)\");\n }\n // Closed before the rename: Windows will not move a file that is open.\n await handle.close();\n closed = true;\n await rename(partPath, destPath);\n } catch (e) {\n if (!closed) await handle.close().catch(() => {});\n // A part file left behind is one the next download has to reason about,\n // and one nobody who finds it in an install directory can.\n await rm(partPath, { force: true }).catch(() => {});\n throw e;\n }\n }\n\n private requireActivated(): void {\n if (!this.activationSecret) {\n throw new TalosError(\"not activated: call activate() first\");\n }\n }\n\n /** Keep a newly imported offline licence authoritative over older requests. */\n private offlineSince(generation: number): LicenseState | null {\n if (generation === this.licensingGeneration || this.activationSecret || !this.binding?.offlineToken) {\n return null;\n }\n return this.adoptOfflineToken(this.binding.offlineToken);\n }\n\n private post(path: string, body: unknown): Promise<Record<string, unknown>> {\n return this.request(\"POST\", path, body);\n }\n\n /**\n * The abort signal for one licensing call, or `undefined` when disabled.\n *\n * A fresh one per request rather than one shared: `AbortSignal.timeout`\n * starts counting when it is created, so a signal made once in the\n * constructor would already have fired by the second call.\n */\n private timeoutSignal(): AbortSignal | undefined {\n const ms = this.options.httpTimeoutMs ?? DEFAULT_HTTP_TIMEOUT_MS;\n if (!ms || ms <= 0) return undefined;\n // Present since Node 17.3. Guarded rather than assumed because `fetch` may\n // be injected in a runtime that has one and not the other, and a missing\n // timeout is better than a crash on the launch path.\n return typeof AbortSignal.timeout === \"function\" ? AbortSignal.timeout(ms) : undefined;\n }\n\n private async request(\n method: string,\n path: string,\n body: unknown,\n ): Promise<Record<string, unknown>> {\n let res: FetchResponseLike;\n try {\n res = await this.doFetch(this.options.serverUrl.replace(/\\/+$/, \"\") + path, {\n method,\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(body),\n signal: this.timeoutSignal(),\n });\n } catch (e) {\n // An abort is a network failure, not a licensing verdict, and it has to\n // read as one: `validate` turns a network failure into the grace-period\n // path, and an unrecognised exception type there would take the\n // application down instead of letting it keep running offline.\n if (e instanceof Error && (e.name === \"TimeoutError\" || e.name === \"AbortError\")) {\n throw new TalosError(\n `request timed out after ${this.options.httpTimeoutMs ?? DEFAULT_HTTP_TIMEOUT_MS}ms`,\n );\n }\n throw e;\n }\n const text = await res.text();\n if (res.status < 200 || res.status >= 300) {\n let errorCode: string | undefined;\n try {\n errorCode = (JSON.parse(text) as { error?: string }).error;\n } catch {\n errorCode = undefined;\n }\n throw new TalosApiError(res.status, errorCode);\n }\n return text ? (JSON.parse(text) as Record<string, unknown>) : {};\n }\n}\n\nfunction stateFromClaims(\n claims: Record<string, unknown>,\n decision: string,\n isOffline: boolean,\n): LicenseState {\n const lic = (claims[\"lic\"] ?? {}) as Record<string, unknown>;\n const features = (lic[\"features\"] ?? {}) as Record<string, unknown>;\n const exp = num(lic[\"lic_exp\"]);\n // Validation carries absolute deadlines; activation carries the same policy\n // as hours beside its `iat`. Prefer the absolute form and derive the other,\n // so both tokens answer \"when should I come back?\" the same way.\n const iat = num(claims[\"iat\"]) ?? nowSeconds();\n const revalAfter = num(claims[\"reval_after\"]) ?? hoursAfter(iat, lic[\"reval_h\"]);\n const graceUntil = num(claims[\"grace_until\"]) ?? hoursAfter(iat, lic[\"grace_h\"]);\n return new LicenseState(decision, features, exp, isOffline, revalAfter, graceUntil);\n}\n\nfunction hoursAfter(base: number, hours: unknown): number | null {\n const h = num(hours);\n return h === null ? null : base + h * 3600;\n}\n\nfunction num(v: unknown): number | null {\n return typeof v === \"number\" && Number.isFinite(v) ? v : null;\n}\n\nfunction nowSeconds(): number {\n return Math.floor(Date.now() / 1000);\n}\n\n/**\n * Which transition, if any, moving from `prev` to `next` amounts to.\n *\n * At most one: the four are mutually exclusive by decision, and a repeat of the\n * verdict already held is not a transition at all — an application offline for a\n * week gets one `gracePeriodStarted`, not one per check-in. A *different* bad\n * verdict does fire again (`expired` after `suspended` is a different thing to\n * tell the user about), which is why this compares the decision and not just\n * validity.\n */\nfunction transitionEvent(prev: LicenseState | null, next: LicenseState): TalosEventName | null {\n if (next.decision === \"clock_tampered\") {\n return prev?.decision === \"clock_tampered\" ? null : \"clockTamperDetected\";\n }\n if (next.decision === \"grace_expired\") {\n return prev?.decision === \"grace_expired\" ? null : \"gracePeriodExpired\";\n }\n if (next.isValid) {\n // Grace begins when a cached verdict is what is keeping the application\n // running, not when the network drops: a failed call that falls back on\n // nothing throws, and never reaches here.\n if (!next.isOffline) return null;\n return prev?.isOffline && prev.isValid ? null : \"gracePeriodStarted\";\n }\n return prev?.decision === next.decision ? null : \"licenseInvalid\";\n}\n\nfunction newNonce(): string {\n return b64urlEncode(randomBytes(16));\n}\n\nfunction asArray(v: unknown): unknown[] {\n return Array.isArray(v) ? v : [];\n}\n\nfunction selKey(platform: unknown, arch: unknown, kind: unknown): string {\n return `${String(platform)}/${String(arch)}/${String(kind)}`;\n}\n\nfunction deltaKey(platform: unknown, arch: unknown, from: unknown, targetKind: unknown): string {\n return `${String(platform)}/${String(arch)}/${String(from)}/${String(targetKind)}`;\n}\n\n/** The platform and arch names this machine matches artifacts by. */\nfunction hostTarget(): { platform: string; arch: string } {\n const map: Record<string, string> = { win32: \"windows\", darwin: \"macos\", linux: \"linux\" };\n return { platform: map[os.platform()] ?? os.platform(), arch: os.arch() };\n}\n\n/** Best artifact for the current platform (installer preferred), or undefined. */\nfunction pickArtifact(artifacts: UpdateArtifact[]): UpdateArtifact | undefined {\n const { platform, arch } = hostTarget();\n const matches = artifacts.filter((a) => a.platform === platform && a.arch === arch);\n return matches.find((a) => a.kind === \"installer\") ?? matches[0];\n}\n\n/**\n * Check that applying a patch produced the file the signed manifest said it\n * would. Throws {@link TalosSignatureError} if not.\n *\n * This is the last link in the chain and the easiest one to leave out. The patch\n * itself was verified on the way in, but a patcher can fail halfway, apply to\n * the wrong base file, or be handed bytes from somewhere else entirely — and\n * every one of those produces a file that nothing has vouched for. Reads the\n * file in chunks, so a multi-gigabyte install is not brought into memory.\n *\n * Needs no licence and no activation: run it from the updater process, after the\n * application it belongs to has exited.\n */\nexport async function verifyAppliedUpdate(filePath: string, delta: UpdateDelta): Promise<void> {\n const hash = createHash(\"sha256\");\n const stream = createReadStream(filePath);\n await new Promise<void>((resolve, reject) => {\n stream.on(\"data\", (chunk) => hash.update(chunk));\n stream.on(\"error\", reject);\n stream.on(\"end\", () => resolve());\n });\n const actual = hash.digest(\"hex\");\n if (actual !== delta.target.sha256) {\n throw new TalosSignatureError(\n `applied update does not match the signed manifest: expected ${delta.target.sha256}, got ${actual}`,\n );\n }\n}\n","/** Base class for all Talos SDK errors. */\nexport class TalosError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"TalosError\";\n }\n}\n\n/**\n * A server response failed cryptographic verification against the pinned\n * product key (bad signature, wrong nonce/audience/type, or expired). Treated\n * like a network failure — never as \"valid\".\n */\nexport class TalosSignatureError extends TalosError {\n constructor(message: string) {\n super(message);\n this.name = \"TalosSignatureError\";\n }\n}\n\n/** A non-success HTTP response from the Talos runtime API. */\nexport class TalosApiError extends TalosError {\n readonly statusCode: number;\n readonly errorCode: string | undefined;\n\n constructor(statusCode: number, errorCode: string | undefined) {\n super(`talos api error ${statusCode}${errorCode ? `: ${errorCode}` : \"\"}`);\n this.name = \"TalosApiError\";\n this.statusCode = statusCode;\n this.errorCode = errorCode;\n }\n}\n","import { createHash, createHmac } from \"node:crypto\";\nimport { readFileSync, statSync } from \"node:fs\";\nimport os, { type NetworkInterfaceInfo } from \"node:os\";\n\n/** A machine fingerprint: the composite plus the per-component hashes. */\nexport interface FingerprintData {\n /** What the server matches on. Derived from {@link components}. */\n composite: string;\n /** `{component_id: hash}` — lets the server tolerate hardware drift. */\n components: Record<string, string>;\n}\n\n/**\n * Collect a machine fingerprint.\n *\n * Each component is HMAC'd with a product-scoped key before it leaves the\n * machine, so the server stores hashes and never the underlying identifiers,\n * and the same hardware yields different hashes for different products.\n *\n * **Coverage is thin here, and honestly so.** Node has no hardware access\n * without native modules, and this SDK ships with zero dependencies and never\n * spawns a subprocess. On Linux it can read `/etc/machine-id`, which is enough\n * for the server to recognise a machine across a hardware change. On Windows and\n * macOS it cannot reach anything comparable, so those hosts stay on exact\n * matching: change a NIC and the user re-activates. If your app can do better —\n * Electron with a native module, or a value you already have — pass\n * `fingerprintComponents` in the options and the server will use it. See\n * `docs/FINGERPRINT.md` for the recognised ids and their weights.\n *\n * None of this resists a determined attacker: these values are produced on the\n * client and can be forged. They exist to recognise honest hardware churn.\n */\nexport function collectFingerprint(\n productToken: string,\n extra?: Record<string, string>,\n): FingerprintData {\n const key = createHash(\"sha256\").update(\"talos-fp|\" + productToken).digest();\n\n const raw: Record<string, string | undefined> = {\n machine_guid: machineGuid(),\n smbios_uuid: readFile(\"/sys/class/dmi/id/product_uuid\"),\n disk_serial: readFile(\"/sys/class/dmi/id/board_serial\"),\n mac_set: macSet(),\n cpu: cpu(),\n mem_bucket: memBucket(),\n os_install: osInstall(),\n hostname: safe(() => os.hostname()),\n ...extra,\n };\n\n const components: Record<string, string> = {};\n for (const id of Object.keys(raw).sort()) {\n const value = raw[id];\n if (value === undefined || value === null || value.trim() === \"\") continue;\n // 16 bytes distinguishes components and keeps the request small; this is a\n // matching token, not a secret.\n components[id] = createHmac(\"sha256\", key)\n .update(id + \"=\" + value.trim())\n .digest(\"hex\")\n .slice(0, 32);\n }\n\n // The composite is a digest of the hashed components, so a host reporting the\n // same parts always yields the same composite and the server's exact-match\n // fast path keeps working.\n const canonical = Object.entries(components)\n .map(([k, v]) => `${k}=${v}`)\n .join(\"\\n\");\n return {\n composite: \"fp2:\" + createHash(\"sha256\").update(canonical).digest(\"hex\"),\n components,\n };\n}\n\n/** Per-install OS identity; survives hardware changes. Linux only here. */\nfunction machineGuid(): string | undefined {\n return readFile(\"/etc/machine-id\") ?? readFile(\"/var/lib/dbus/machine-id\");\n}\n\n/**\n * Sorted physical MACs. Internal interfaces are skipped: loopback and the\n * virtual adapters a VPN raises and drops would otherwise report drift on every\n * connection.\n */\nfunction macSet(): string | undefined {\n return safe(() => {\n const macs = Object.values(os.networkInterfaces())\n .flat()\n .filter((i): i is NetworkInterfaceInfo => Boolean(i) && !i!.internal)\n .map((i) => i.mac)\n .filter((m) => m && m !== \"00:00:00:00:00:00\")\n .filter((m, i, all) => all.indexOf(m) === i)\n .sort();\n return macs.length === 0 ? undefined : macs.join(\",\");\n });\n}\n\nfunction cpu(): string | undefined {\n return safe(() => {\n const cpus = os.cpus();\n if (!cpus || cpus.length === 0) return undefined;\n return `${cpus[0].model}|${cpus.length}`;\n });\n}\n\n/**\n * Installed RAM rounded down to a power of two GiB. Coarse on purpose: exact\n * byte counts shift when firmware reserves memory, which would look like drift\n * on a machine nobody touched.\n */\nfunction memBucket(): string | undefined {\n return safe(() => {\n const gib = Math.floor(os.totalmem() / 1024 ** 3);\n if (gib < 1) return undefined;\n let bucket = 1;\n while (bucket * 2 <= gib) bucket *= 2;\n return String(bucket);\n });\n}\n\n/** Install timestamp, approximated by the OS directory's creation time. */\nfunction osInstall(): string | undefined {\n const path = os.platform() === \"win32\" ? process.env.SystemRoot ?? \"C:\\\\Windows\" : \"/etc\";\n return safe(() => {\n const birth = statSync(path).birthtimeMs;\n // Filesystems that do not record a birth time report 0 (or the epoch);\n // a constant would be worse than nothing, since every host would agree.\n return birth > 0 ? String(Math.floor(birth / 1000)) : undefined;\n });\n}\n\nfunction readFile(path: string): string | undefined {\n return safe(() => {\n const text = readFileSync(path, \"utf8\").trim();\n return text.length === 0 ? undefined : text;\n });\n}\n\n/**\n * Run a probe, treating any failure as \"component unavailable\". A missing\n * component costs a little matching weight; an exception escaping into a\n * licensing call would cost the user their session.\n */\nfunction safe<T>(f: () => T | undefined): T | undefined {\n try {\n return f();\n } catch {\n return undefined;\n }\n}\n","import { createCipheriv, createDecipheriv, createHash, hkdfSync } from \"node:crypto\";\nimport { randomBytes } from \"node:crypto\";\nimport { chmodSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport os from \"node:os\";\n\nimport type { FingerprintData } from \"./fingerprint.js\";\n\n/**\n * The machine binding the server hands back at activation, held across process\n * restarts.\n *\n * Without this the binding died with the process and every application start\n * had to call `activate` again — which burns a round trip the protocol was\n * designed to avoid, rotates the activation secret out from under any other\n * running copy, and (because `activate` is the one client route refused for a\n * suspended tenant) turns the platform owner's deliberately graduated\n * suspension into a hard kill switch for the developer's own paying users.\n */\nexport interface StoredBinding {\n /**\n * SHA-256 of the licence key as it was typed, hex — **not** the key.\n *\n * The key itself used to live here, so that the next call could send it. It\n * no longer has to: the server resolves a licence from the activation secret,\n * which is the credential these calls authenticate with anyway. What was left\n * was a plaintext licence key in a file on every customer's machine, which is\n * a credential-shaped file and exactly what an infostealer collects.\n *\n * A hash is kept because one thing still needs it: `activate()` called twice\n * with the same key must validate rather than spend a second seat, and that\n * comparison is all the stored value was ever used for. The hash cannot be\n * replayed to the server — the server matches an HMAC under its own pepper —\n * so this is a value with exactly one use and no resale.\n *\n * Hashed as typed, without normalising, because the comparison it replaces\n * was a string equality on the same input: normalising here would change when\n * a re-activation is avoided, which is not what this change is about.\n */\n licenseKeyHash: string;\n activationSecret: string;\n machineId: string;\n /**\n * The fingerprint composite at activation time. Informational: it is *not*\n * required to match on restore, because a machine whose hardware drifted is\n * exactly the case the server's fuzzy matcher exists to resolve. Discarding\n * the binding here would re-activate the customer whose disk was replaced —\n * the support ticket the drift budget was built to prevent.\n */\n fingerprint: string;\n /**\n * The last anchor-signed keyset token.\n *\n * Cached because an anchor-only client — the pinning that survives key\n * rotation — otherwise has no verification key at all until it can reach the\n * server, which is exactly the situation the offline path exists for. The\n * anchor is compiled into the app, the keyset is signed by it, and the\n * validation token is signed by a key it lists: the whole chain verifies\n * offline against a root the network cannot influence.\n */\n keyset?: string;\n /** The last verified `typ=validation` token — the offline verdict. */\n validation?: string;\n /**\n * The highest server clock ever seen, in unix seconds, taken from a signed\n * claim.\n *\n * This is what makes offline grace bounded. Grace is evaluated against the\n * local clock, which the user controls; with no mark that only moves forward,\n * winding the clock back would renew a 72-hour window indefinitely. A\n * monotonic timer is not a substitute — it resets on reboot and on a VM\n * snapshot restore, which is the case that matters.\n */\n timeHighWater?: number;\n /**\n * The highest `release_seq` this install is known to be running, and the\n * floor `checkForUpdate` refuses to go below.\n *\n * Anti-downgrade only works against a mark that outlives the process, and\n * until this existed there was none: the floor came entirely from\n * `currentReleaseSeq`, which the host had to compute and pass, and which no\n * example passed. `docs/SECURITY.md` described a high-water mark that did not\n * exist, so \"yanking a release does not recall it from installs that already\n * applied it\" rested on the host having implemented the mark itself.\n */\n releaseSeqHighWater?: number;\n /**\n * A release whose bytes were downloaded and verified but which is not\n * running yet: the version string from its signed manifest, and its\n * `release_seq`.\n *\n * The SDK downloads; the host installs, in its own way, on its own schedule,\n * usually after a restart. So \"installed\" is not an event the SDK can see —\n * but the *result* is, because the host tells it which version it is on every\n * time a client is constructed. When `appVersion` matches what was downloaded,\n * the download became an install and the mark advances.\n *\n * The alternative was a `markInstalled()` for the host to call, and the whole\n * defect here is a mark that depended on the host doing something nobody\n * documented and no example did.\n */\n pendingRelease?: { version: string; releaseSeq: number };\n /**\n * This machine's own Ed25519 private key (PKCS#8 DER, base64), generated at\n * its first offline request.\n *\n * The air-gapped counterpart of `activationSecret`, and it exists for the same\n * reason that one does: an install has to be able to prove it is the install a\n * decision was made for. It never leaves this file — only the public half\n * travels, inside `talos-request.json` — and losing the file means repeating\n * the ceremony with the vendor, because the identity has exactly one copy by\n * construction.\n */\n machineKey?: string;\n /** The raw public half, standard base64. Checked against the file's `mpk`. */\n machinePublicKey?: string;\n /** The last verified `typ=offline` licence file. */\n offlineToken?: string;\n /**\n * The highest local clock ever seen while running on an offline file, in unix\n * seconds.\n *\n * Deliberately separate from {@link timeHighWater}, which holds *server*\n * times taken from signed claims. An air-gapped install sees exactly one\n * server time in its life — the `iat` of its file — so the server mark stops\n * moving, and nothing would notice a clock wound back to the day after\n * issuance to keep a year-long file alive forever.\n *\n * Mixing the two would be worse than useless: a machine whose clock ran fast\n * would inflate the server mark, and correcting the clock afterwards would\n * then read as tampering on an install that had done nothing wrong.\n *\n * What this bounds is **rollback, not freeze**. A user who stops their clock\n * keeps a file past its expiry; a user who winds it back is told\n * `clock_tampered`. That is the honest limit of enforcing a deadline against\n * a clock its owner controls, and it is the price of offline.\n */\n offlineSeen?: number;\n}\n\nconst MAGIC = Buffer.from(\"TLS1\");\nconst IV_LEN = 12;\nconst TAG_LEN = 16;\nconst SALT = Buffer.from(\"talos-state-v1\");\n\n/**\n * Where the binding lives, per product, following each OS's convention.\n *\n * The directory is named by a hash of the product token rather than the token\n * itself: it keeps the path filesystem-safe without assuming anything about the\n * token's alphabet, and two products never collide.\n */\nexport function defaultStatePath(productToken: string): string {\n return statePathFor(productToken, os.platform(), os.homedir(), process.env);\n}\n\n/**\n * The path, from a host described rather than asked — so the layout on all three\n * platforms is testable from any one of them, and the C# SDK has something to\n * agree with. (Its `DefaultPathFor` is the same three branches; the same product\n * on the same machine must not have two bindings in two places.)\n *\n * Not exported from the package: this exists for the tests on both sides.\n */\nexport function statePathFor(\n productToken: string,\n platform: string,\n home: string,\n env: Record<string, string | undefined>,\n): string {\n const slot = createHash(\"sha256\").update(productToken).digest(\"hex\").slice(0, 16);\n switch (platform) {\n case \"win32\":\n return join(env[\"LOCALAPPDATA\"] || join(home, \"AppData\", \"Local\"), \"Talos\", slot, \"state.bin\");\n case \"darwin\":\n return join(home, \"Library\", \"Application Support\", \"Talos\", slot, \"state.bin\");\n default:\n // Lower-case, unlike the other two: this is the directory a Linux user's\n // other applications keep company with.\n return join(env[\"XDG_DATA_HOME\"] || join(home, \".local\", \"share\"), \"talos\", slot, \"state.bin\");\n }\n}\n\n/**\n * Key material for the state file, chosen for **stability** rather than\n * strength: a hardware change must not lock the user out of their own stored\n * licence key. `machine_guid` and `smbios_uuid` survive component swaps;\n * `os_install` survives everything short of a reinstall, which invalidates the\n * other two anyway.\n *\n * These values are already HMAC'd per product by the fingerprint collector, so\n * nothing here reveals an identifier the server does not already hold.\n *\n * Where a host offers none of them the file is obfuscated rather than bound to\n * the machine, and that is the honest description. It matters less than it\n * sounds: the server resolves a machine by fingerprint and demands the\n * activation secret, so a state file copied to a different computer does not\n * validate there. This layer is tamper-evidence and resistance to casual\n * copying — never secrecy from someone who owns the machine.\n */\nexport function stateKeyMaterial(fp: FingerprintData): string {\n return (\n fp.components[\"machine_guid\"] ??\n fp.components[\"smbios_uuid\"] ??\n fp.components[\"os_install\"] ??\n \"talos-unbound\"\n );\n}\n\n/**\n * SHA-256 of a licence key, hex.\n *\n * Used only to recognise \"the same key as last time\" without keeping the key.\n * Not a credential and not accepted by anything: the server matches an HMAC\n * computed under a pepper this process has never seen.\n */\nexport function hashLicenseKey(key: string): string {\n return createHash(\"sha256\").update(key, \"utf8\").digest(\"hex\");\n}\n\n/** Reads and writes one product's machine binding. */\nexport class StateStore {\n private readonly key: Buffer;\n\n constructor(\n readonly path: string,\n private readonly productToken: string,\n keyMaterial: string,\n ) {\n this.key = Buffer.from(\n hkdfSync(\"sha256\", Buffer.from(keyMaterial), SALT, Buffer.from(productToken), 32),\n );\n }\n\n /**\n * Load the binding, or null if there is none.\n *\n * Never throws. A state file that is missing, truncated, from another\n * product, written by a future version, or simply corrupt must degrade to\n * \"not activated yet\" — the app then activates again. Throwing here would\n * turn a damaged cache into a launch failure.\n */\n load(): StoredBinding | null {\n let raw: Buffer;\n try {\n raw = readFileSync(this.path);\n } catch {\n return null;\n }\n try {\n if (raw.length < MAGIC.length + IV_LEN + TAG_LEN) return null;\n if (!raw.subarray(0, MAGIC.length).equals(MAGIC)) return null;\n const iv = raw.subarray(MAGIC.length, MAGIC.length + IV_LEN);\n const tag = raw.subarray(MAGIC.length + IV_LEN, MAGIC.length + IV_LEN + TAG_LEN);\n const body = raw.subarray(MAGIC.length + IV_LEN + TAG_LEN);\n const d = createDecipheriv(\"aes-256-gcm\", this.key, iv);\n // The product token is authenticated, not just stored: a file belonging\n // to another product cannot be silently adopted.\n d.setAAD(Buffer.from(this.productToken));\n d.setAuthTag(tag);\n const plain = Buffer.concat([d.update(body), d.final()]).toString(\"utf8\");\n const o = JSON.parse(plain) as Record<string, unknown>;\n if (o[\"v\"] !== 1) return null;\n // An older file carries `licenseKey` in the clear. Upgrade it in place\n // rather than discarding the binding: the customer keeps their seat, and\n // the plaintext key stops existing on disk at the next save — which is\n // the point of the change, and it must not cost anybody a re-activation\n // to get.\n const legacyKey = typeof o[\"licenseKey\"] === \"string\" ? o[\"licenseKey\"] : \"\";\n const b: StoredBinding = {\n licenseKeyHash:\n typeof o[\"licenseKeyHash\"] === \"string\" && o[\"licenseKeyHash\"]\n ? o[\"licenseKeyHash\"]\n : legacyKey\n ? hashLicenseKey(legacyKey)\n : \"\",\n activationSecret: String(o[\"activationSecret\"] ?? \"\"),\n machineId: String(o[\"machineId\"] ?? \"\"),\n fingerprint: String(o[\"fingerprint\"] ?? \"\"),\n };\n if (typeof o[\"keyset\"] === \"string\") b.keyset = o[\"keyset\"];\n if (typeof o[\"validation\"] === \"string\") b.validation = o[\"validation\"];\n if (typeof o[\"timeHighWater\"] === \"number\") b.timeHighWater = o[\"timeHighWater\"];\n if (typeof o[\"releaseSeqHighWater\"] === \"number\") {\n b.releaseSeqHighWater = o[\"releaseSeqHighWater\"];\n }\n // Read field by field rather than trusted wholesale: this file is on a\n // disk the user owns, so every value out of it is input. A `pendingRelease`\n // with a string where a number belongs would make the anti-downgrade\n // comparison silently lexicographic.\n const pending = o[\"pendingRelease\"] as Record<string, unknown> | undefined;\n if (\n pending &&\n typeof pending[\"version\"] === \"string\" &&\n typeof pending[\"releaseSeq\"] === \"number\"\n ) {\n b.pendingRelease = { version: pending[\"version\"], releaseSeq: pending[\"releaseSeq\"] };\n }\n if (typeof o[\"machineKey\"] === \"string\") b.machineKey = o[\"machineKey\"];\n if (typeof o[\"machinePublicKey\"] === \"string\") {\n b.machinePublicKey = o[\"machinePublicKey\"];\n }\n if (typeof o[\"offlineToken\"] === \"string\") b.offlineToken = o[\"offlineToken\"];\n if (typeof o[\"offlineSeen\"] === \"number\") b.offlineSeen = o[\"offlineSeen\"];\n // What makes a binding worth keeping is that it holds a licence or the\n // identity behind one. The activation secret is that for an online\n // install; an air-gapped install has none — it was never issued one — and\n // requiring it here would throw away the offline file and the machine key\n // on every start, which is the whole state of an air-gapped install.\n return b.activationSecret || b.offlineToken || b.machineKey ? b : null;\n } catch {\n return null;\n }\n }\n\n /**\n * Persist the binding. Best-effort by design: a read-only or full disk costs\n * the user a re-activation next launch, and must not fail the activation\n * that just succeeded.\n */\n save(b: StoredBinding): boolean {\n try {\n const plain = Buffer.from(JSON.stringify({ v: 1, ...b }), \"utf8\");\n const iv = randomBytes(IV_LEN);\n const c = createCipheriv(\"aes-256-gcm\", this.key, iv);\n c.setAAD(Buffer.from(this.productToken));\n const body = Buffer.concat([c.update(plain), c.final()]);\n const out = Buffer.concat([MAGIC, iv, c.getAuthTag(), body]);\n\n mkdirSync(dirname(this.path), { recursive: true, mode: 0o700 });\n // Write-then-rename: a crash mid-write leaves the previous binding\n // intact rather than a half file that reads as \"never activated\".\n const tmp = `${this.path}.tmp`;\n writeFileSync(tmp, out, { mode: 0o600 });\n try {\n chmodSync(tmp, 0o600); // no-op on Windows; explicit on POSIX umasks\n } catch {\n /* ignore */\n }\n renameSync(tmp, this.path);\n return true;\n } catch {\n return false;\n }\n }\n\n /** Forget the binding (deactivation). */\n clear(): void {\n try {\n rmSync(this.path, { force: true });\n } catch {\n /* ignore */\n }\n }\n}\n","/**\n * This SDK's own version, as reported to the server on activate and heartbeat.\n *\n * A constant rather than a read of `package.json`: the package ships dual\n * ESM/CJS through a bundler, so neither `import.meta.url` nor `__dirname`\n * resolves to the package root in both outputs, and a runtime read that works\n * in one build and throws in the other is worse than no read at all.\n *\n * `test/version.test.ts` asserts this equals `package.json`, so it cannot\n * quietly go stale — which is what the previous hard-coded `\"node/0.0.0\"`\n * literal at the call site was doing, and why the column it fills was worthless\n * for answering \"who is still on the old client?\".\n */\nexport const SDK_VERSION = \"0.3.0\";\n\n/** The wire form: language tag plus version, e.g. `node/0.2.0`. */\nexport const SDK_ID = `node/${SDK_VERSION}`;\n","import { TalosApiError, TalosSignatureError } from \"./errors.js\";\nimport type { LicenseState } from \"./client.js\";\nimport { SDK_ID } from \"./version.js\";\n\nconst decisions = new Set([\"valid\", \"expired\", \"revoked\", \"suspended\", \"deactivated\", \"machine_revoked\", \"invalid\", \"grace_expired\", \"clock_tampered\"]);\nconst errors = new Set([\"product_not_found\", \"license_not_found\", \"license_expired\", \"license_revoked\", \"license_suspended\", \"machine_revoked\", \"activation_limit_reached\", \"seat_limit_reached\", \"deactivation_limit_reached\", \"tenant_suspended\", \"tenant_closed\", \"invalid_activation_secret\", \"rate_limited\"]);\n\n/** An allowlisted local snapshot. Never serialize options, errors or bindings wholesale. */\nexport function diagnosticReport(activated: boolean, anchorPinned: boolean, state: LicenseState | null, error?: unknown): string {\n const code = error instanceof TalosApiError\n ? (errors.has(error.errorCode ?? \"\") ? error.errorCode! : \"api_error\")\n : error instanceof TalosSignatureError ? \"signature_verification_failed\"\n : error != null ? \"client_error\" : null;\n const decision = state == null ? null : decisions.has(state.decision) ? state.decision : \"unknown\";\n const advice: Record<string, string> = {\n signature_verification_failed: \"Check the pinned anchor, product configuration and system time. Keep signature verification enabled.\",\n activation_limit_reached: \"Open the license's devices in the portal. Release an unused device or review its activation limit.\",\n seat_limit_reached: \"Close an unused app session or review the concurrent seat limit.\",\n license_expired: \"Renew the license in the portal, then validate again.\",\n expired: \"Renew the license in the portal, then validate again.\",\n license_revoked: \"Revocation is permanent. Ask the vendor for a replacement license.\",\n revoked: \"Revocation is permanent. Ask the vendor for a replacement license.\",\n license_suspended: \"Ask the vendor to review the suspension before activating a new device.\",\n suspended: \"Ask the vendor to review the suspension.\",\n machine_revoked: \"Ask the vendor to review this device in the portal.\",\n grace_expired: \"Reconnect to the configured Talos server and validate. Do not extend the local grace deadline.\",\n clock_tampered: \"Correct the system clock and validate online.\",\n client_error: \"Check connectivity, server configuration and local state access. The exception text is intentionally excluded.\",\n };\n const action = advice[code ?? decision ?? \"\"]\n ?? (code ? \"Review the licensing call and configuration with the vendor. HTTP errors are not signed license decisions.\"\n : state?.isOffline ? \"The last decision was offline. Check connectivity and validate online; this snapshot cannot identify the transport failure.\"\n : !activated ? \"Activate with a license key, then validate the saved activation.\"\n : decision === \"valid\" ? \"The last licensing call returned valid. Check the portal for this device's recorded validation.\"\n : \"Run a licensing check and generate a new report. Ask the vendor to review an invalid decision.\");\n const timestamp = (value: number | null | undefined) => typeof value === \"number\" && Number.isFinite(value) ? value : null;\n return JSON.stringify({\n schema_version: 1,\n sdk: SDK_ID,\n generated_at: Math.floor(Date.now() / 1000),\n activated,\n anchor_pinned: anchorPinned,\n decision,\n is_offline: state?.isOffline ?? null,\n license_expires_at: timestamp(state?.licenseExpiresAt),\n grace_until: timestamp(state?.graceUntil),\n error_code: code,\n http_status: error instanceof TalosApiError && Number.isInteger(error.statusCode) && error.statusCode >= 100 && error.statusCode <= 599 ? error.statusCode : null,\n next_action: action,\n }, null, 2);\n}\n","import { createPrivateKey, generateKeyPairSync, sign as cryptoSign, type KeyObject } from \"node:crypto\";\n\nimport { b64urlEncode } from \"./talos-compact.js\";\n\n/**\n * The air-gapped exchange, and the identity that makes it possible.\n *\n * Online, a machine proves who it is with the `activation_secret` the server\n * hands back from `activate`. A machine that cannot reach a server never gets\n * one — so it makes its own: an Ed25519 pair at the first offline request,\n * public half in `talos-request.json`, private half never leaving this disk. The\n * file the portal issues is bound to that key as well as to the fingerprint, and\n * anything this machine sends back afterwards is signed by it.\n *\n * The document format deliberately is **not** Talos-compact. That format's `typ`\n * set is the closed list of things the *platform* signs, and putting a machine's\n * key into the same namespace as a product's is exactly the confusion the tag\n * set exists to prevent. What is copied is the part that matters: the payload\n * travels base64url-encoded and is verified as received, never re-serialised, so\n * three implementations cannot disagree about JSON key order and turn that into\n * a signature one of them rejects.\n */\n\n/** Domain tag of `talos-request.json`; also part of what is signed. */\nexport const REQUEST_DOMAIN = \"talos-offline-request\";\n\n/**\n * Domain tag of `talos-deactivation.json`, the document that hands a seat back.\n *\n * The second thing a machine key signs, and the reason the domain is inside the\n * signed bytes rather than beside them: without it a captured request would be a\n * valid receipt for the same machine, and returning a seat is not what its owner\n * asked for when they asked for a licence.\n */\nexport const RECEIPT_DOMAIN = \"talos-offline-deactivation\";\n/** `kind` of the file the portal issues back. */\nexport const LICENSE_FILE_KIND = \"talos-offline-license\";\nconst DOC_VERSION = 1;\n\n/** A machine's own keypair, in the two forms this SDK needs. */\nexport interface MachineKey {\n /** PKCS#8 DER, base64 — how the private half is kept in the state file. */\n privateKey: string;\n /** The raw 32-byte public key, standard base64 — how it travels. */\n publicKey: string;\n}\n\n/** A document this machine signed: what gets written to disk and carried. */\nexport interface SignedDoc {\n v: number;\n kind: string;\n /** base64url of the payload JSON. */\n req: string;\n /** base64url Ed25519 signature over `\"<kind>.1.<req>\"`. */\n sig: string;\n}\n\n/**\n * Generate the machine's identity.\n *\n * Once per install, at the first offline request. Losing it means the customer\n * repeats the ceremony — which is the honest consequence of an identity that is\n * only ever held in one place, and the reason it lives in the same encrypted\n * state file as everything else this SDK persists.\n */\nexport function generateMachineKey(): MachineKey {\n const { privateKey, publicKey } = generateKeyPairSync(\"ed25519\");\n return {\n privateKey: privateKey.export({ format: \"der\", type: \"pkcs8\" }).toString(\"base64\"),\n // The raw key is the last 32 bytes of the SPKI DER; every other Ed25519\n // implementation in this project speaks raw-32, including the server's.\n publicKey: publicKey.export({ format: \"der\", type: \"spki\" }).subarray(-32).toString(\"base64\"),\n };\n}\n\nfunction privateKeyObject(pkcs8Base64: string): KeyObject {\n return createPrivateKey({\n key: Buffer.from(pkcs8Base64, \"base64\"),\n format: \"der\",\n type: \"pkcs8\",\n });\n}\n\n/** The exact ASCII bytes a machine-signed document is signed over. */\nexport function signedMessage(domain: string, payloadB64: string): string {\n return `${domain}.${DOC_VERSION}.${payloadB64}`;\n}\n\n/** Sign a payload object as a machine document. */\nexport function signMachineDoc(\n key: MachineKey,\n domain: string,\n payload: Record<string, unknown>,\n): SignedDoc {\n const req = b64urlEncode(Buffer.from(JSON.stringify(payload), \"utf8\"));\n const sig = cryptoSign(null, Buffer.from(signedMessage(domain, req), \"ascii\"), privateKeyObject(key.privateKey));\n return { v: DOC_VERSION, kind: domain, req, sig: b64urlEncode(sig) };\n}\n","import { createPublicKey, verify as cryptoVerify, type KeyObject } from \"node:crypto\";\n\nimport { TalosSignatureError } from \"./errors.js\";\n\n// DER SubjectPublicKeyInfo prefix for a raw Ed25519 public key.\nconst SPKI_PREFIX = Buffer.from(\"302a300506032b6570032100\", \"hex\");\nconst CLOCK_SKEW_SECONDS = 60;\n\n/** Wrap a base64-encoded raw 32-byte Ed25519 public key as a KeyObject. */\nexport function publicKeyFromBase64(b64: string): KeyObject {\n const raw = Buffer.from(b64, \"base64\");\n if (raw.length !== 32) {\n throw new Error(\"public key must decode to 32 bytes\");\n }\n const der = Buffer.concat([SPKI_PREFIX, raw]);\n return createPublicKey({ key: der, format: \"der\", type: \"spki\" });\n}\n\nfunction b64urlDecode(s: string): Buffer {\n return Buffer.from(s.replace(/-/g, \"+\").replace(/_/g, \"/\"), \"base64\");\n}\n\nexport function b64urlEncode(buf: Buffer): string {\n return buf.toString(\"base64\").replace(/=+$/, \"\").replace(/\\+/g, \"-\").replace(/\\//g, \"_\");\n}\n\ntype Keys = KeyObject | KeyObject[];\n\n/**\n * Parse a Talos-compact token, check scheme/version/`typ`, and verify the\n * signature against ANY of `keys` (a keyset may carry several valid keys during\n * rotation). Returns the decoded claims; the caller checks aud/nonce/exp.\n */\nfunction parseAndVerify(keys: Keys, token: string, expectedTyp: string): Record<string, unknown> {\n const list = Array.isArray(keys) ? keys : [keys];\n const parts = token.split(\".\");\n if (parts.length !== 5) throw new TalosSignatureError(\"malformed token\");\n if (parts[0] !== \"talos\") throw new TalosSignatureError(\"unexpected scheme\");\n if (parts[1] !== \"1\") throw new TalosSignatureError(\"unsupported wire version\");\n if (parts[2] !== expectedTyp) throw new TalosSignatureError(\"token type mismatch\");\n if (list.length === 0) throw new TalosSignatureError(\"no verification key\");\n\n const lastDot = token.lastIndexOf(\".\");\n const prefix = Buffer.from(token.slice(0, lastDot), \"ascii\");\n const sig = b64urlDecode(parts[4]!);\n const ok = list.some((k) => cryptoVerify(null, prefix, k, sig));\n if (!ok) throw new TalosSignatureError(\"signature verification failed\");\n\n return JSON.parse(b64urlDecode(parts[3]!).toString(\"utf8\")) as Record<string, unknown>;\n}\n\n/**\n * Verify a Talos-compact token\n * (`talos.<ver>.<typ>.<b64url(payload)>.<b64url(sig)>`, Ed25519 over the exact\n * ASCII prefix). `keys` may be a single pinned key or a keyset (any of which may\n * have signed it). IGNORES any `kid` and enforces `typ`, `aud`, the echoed\n * nonce, and `nbf`/`exp`.\n */\nexport function verifyToken(\n keys: Keys,\n expectedTyp: string,\n expectedAud: string,\n expectedNonce: string,\n token: string,\n): Record<string, unknown> {\n const claims = parseAndVerify(keys, token, expectedTyp);\n if (claims[\"aud\"] !== expectedAud) throw new TalosSignatureError(\"audience mismatch\");\n if (claims[\"non\"] !== expectedNonce) {\n throw new TalosSignatureError(\"nonce mismatch (possible replay)\");\n }\n // Required, not \"checked if present\". Every live token the server mints\n // carries both, so a token without them did not come from a server that\n // knows what it is doing — and treating a missing `exp` as \"no expiry\" turns\n // one server-side omission into a verdict that is valid forever on every\n // install in the field. Cached tokens are a different path (see\n // `verifyCachedToken`), where `exp` deliberately does not apply.\n const now = Math.floor(Date.now() / 1000);\n const nbf = claims[\"nbf\"];\n if (typeof nbf !== \"number\") throw new TalosSignatureError(\"token has no nbf\");\n if (now + CLOCK_SKEW_SECONDS < nbf) {\n throw new TalosSignatureError(\"token not yet valid\");\n }\n const exp = claims[\"exp\"];\n if (typeof exp !== \"number\") throw new TalosSignatureError(\"token has no exp\");\n if (now - CLOCK_SKEW_SECONDS > exp) {\n throw new TalosSignatureError(\"token expired\");\n }\n return claims;\n}\n\n/**\n * Verify a token restored from the local cache, for use while offline.\n *\n * Two checks from {@link verifyToken} deliberately do not apply. The nonce\n * belonged to the request that fetched this token, and `exp` is the short window\n * in which it counts as *fresh* — a cached decision is by definition being used\n * after that. What bounds offline use instead is the `grace_until` claim, which\n * the caller enforces, together with the persisted server-time high-water mark\n * that stops the system clock being wound back to extend it.\n *\n * Everything binding the token to this machine still holds: the signature under\n * a pinned key, `typ`, `aud`, `nbf`, and the caller's fingerprint check.\n */\nexport function verifyCachedToken(\n keys: Keys,\n expectedTyp: string,\n expectedAud: string,\n token: string,\n): Record<string, unknown> {\n const claims = parseAndVerify(keys, token, expectedTyp);\n if (claims[\"aud\"] !== expectedAud) throw new TalosSignatureError(\"audience mismatch\");\n const nbf = claims[\"nbf\"];\n if (typeof nbf === \"number\" && Math.floor(Date.now() / 1000) + CLOCK_SKEW_SECONDS < nbf) {\n throw new TalosSignatureError(\"cached token not yet valid\");\n }\n return claims;\n}\n\n/**\n * Verify a signed update **manifest** (`typ=manifest`) against the pinned\n * release key(s). Unlike per-request tokens a manifest is a long-lived, static\n * artifact, so there is no nonce and no short `exp`; instead it carries a\n * `not_before` gate. Enforces scheme/version/`typ`, the signature, `aud`, and\n * `not_before`.\n */\nexport function verifyManifest(\n releaseKeys: Keys,\n expectedAud: string,\n token: string,\n): Record<string, unknown> {\n const claims = parseAndVerify(releaseKeys, token, \"manifest\");\n if (claims[\"aud\"] !== expectedAud) throw new TalosSignatureError(\"audience mismatch\");\n const notBefore = claims[\"not_before\"];\n if (typeof notBefore === \"number\") {\n const now = Math.floor(Date.now() / 1000);\n if (now + CLOCK_SKEW_SECONDS < notBefore) {\n throw new TalosSignatureError(\"manifest not yet released\");\n }\n }\n return claims;\n}\n\n/**\n * Verify a signed **keyset** (`typ=keyset`) against the pinned anchor key. The\n * keyset lists the product's current, rotatable license/release public keys, so\n * an SDK that pins only the anchor learns the signing keys from it — a rotated\n * key never breaks a deployed app. Enforces scheme/version/`typ`, the anchor\n * signature, and `aud`.\n */\nexport function verifyKeyset(\n anchorKey: KeyObject,\n expectedAud: string,\n token: string,\n): Record<string, unknown> {\n const claims = parseAndVerify(anchorKey, token, \"keyset\");\n if (claims[\"aud\"] !== expectedAud) throw new TalosSignatureError(\"audience mismatch\");\n return claims;\n}\n"],"mappings":"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","names":["createHash","randomBytes","readFileSync","os","createHash","readFileSync","os","CLOCK_SKEW_SECONDS","randomBytes","os","readFileSync","createHash"]}
|