From 8a519ee25d0cfab976fbb4326887df334efefd9d Mon Sep 17 00:00:00 2001 From: Keysat Date: Fri, 8 May 2026 13:00:29 -0500 Subject: [PATCH] Fix Settings modal crash: send licenseId as string, not Uint8Array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Keysat client's payload exposes both: • licenseId — raw 16-byte UUID as Uint8Array • licenseUuid — same value, canonical string form server/license.js was sending licenseId (the Uint8Array). After JSON serialization that turned into an object like {"0":1,"1":2,...} on the wire. The frontend's renderLicenseBlock() calls `lic.licenseId.slice(0, 8)` to abbreviate the ID for display — .slice doesn't exist on that object, so the template threw, the app.innerHTML assignment silently aborted, and clicking the gear looked like a no-op. The render error guard added in 0.1.17 caught it on first repro: Render error: lic.licenseId.slice is not a function Fix: switch to payload.licenseUuid (the string form). --- server/license.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/server/license.js b/server/license.js index f81170c..f0ef70c 100644 --- a/server/license.js +++ b/server/license.js @@ -162,7 +162,10 @@ export function checkLicense() { } base = emptyState({ state: "licensed", - licenseId: payload.licenseId || null, + // payload.licenseId is a Uint8Array (raw 16-byte UUID); the canonical + // string form is licenseUuid. The frontend treats licenseId as a + // string (calls .slice on it), so always send the string here. + licenseId: payload.licenseUuid || null, entitlements: new Set(payload.entitlements || []), expiresAt: payload.expiresAt ? new Date(payload.expiresAt * 1000) : null, isTrial: !!(payload.flags & 1),