// Does this licence entitle this product? Asked before anything is built or // launched, because the failure it prevents is otherwise invisible. // // A V2 licence names the products it covers, and `product add` refuses one it // does not name. Discovered late, that surfaces as // `product did not become ready within 60000ms` — the same 60 seconds of // silence as a product that could not start, or one on an address the daemon // could not dial. Reuters' smoke tier sat behind exactly that ambiguity. // // Turnkeys make this routine rather than rare: each ships to one customer under // a licence naming their product, so every new turnkey needs its own, and the // moment one is missing should be a sentence rather than a timeout. import { readFileSync } from "node:fs"; interface LicensedProduct { product?: unknown; } /** The product names a licence covers, or null when the shape is unreadable — * deliberately distinct from "covers nothing", which is a real answer. */ export function licensedProducts(licenceJson: string): string[] | null { let payload: string; try { const outer = JSON.parse(licenceJson) as { payload?: unknown }; if (typeof outer.payload !== "string") return null; payload = Buffer.from(outer.payload, "base64").toString("utf8"); } catch { return null; } try { const inner = JSON.parse(payload) as { products?: unknown }; if (!Array.isArray(inner.products)) return null; return inner.products.map((p) => (p as LicensedProduct)?.product).filter((p): p is string => typeof p === "string"); } catch { return null; } } /** The message to fail with, or null to proceed. Null covers two very different * cases on purpose: the licence entitles the product, or the licence is in a * shape this cannot read. Refusing on the second would assert something never * determined, and would break the fleet on the first licence format change. */ export function entitlementProblem(licenceJson: string, product: string): string | null { if (licenceJson.trim() === "") { return `licence is empty — the NORSK_LICENSE_V2 secret is unset or unreadable, so ${product} cannot be launched`; } const products = licensedProducts(licenceJson); if (products === null) return null; if (products.includes(product)) return null; const covers = products.length ? products.join(", ") : "nothing"; return `licence does not entitle ${product} (covers: ${covers})`; } if (import.meta.main) { const [file, product] = process.argv.slice(2); if (!file || !product) { console.error("usage: check-entitlement.ts "); process.exit(2); } let contents: string; try { contents = readFileSync(file, "utf8"); } catch (e) { console.error(`licence-check: cannot read ${file}: ${String(e)}`); process.exit(2); } const problem = entitlementProblem(contents, product); if (problem) { console.error(`licence-check: ${problem}`); process.exit(1); } const products = licensedProducts(contents); console.log( products === null ? `licence-check: licence shape not recognised; entitlement for ${product} not asserted` : `licence-check: ${product} is entitled (licence covers: ${products.join(", ")})`, ); }