// Action: "Check license status" — re-verifies the stored key (offline + // online) and refreshes `license_last_status`. Useful for diagnosing a // suddenly-rejecting app. import { sdk } from '../sdk' import { Verifier, PublicKey, Client } from '@keysat/licensing-client' import { LICENSING_BASE_URL, LICENSING_BASE_URL_TOR, PRODUCT_SLUG, ISSUER_PUBKEY_PEM, PRODUCT_DISPLAY_NAME, } from '../licensing/config' export const checkLicense = sdk.Action.withoutInput( 'check-license', async ({ effects }) => ({ name: 'Check license status', description: `Re-run signature and server-side checks against the currently ` + `stored license key for ${PRODUCT_DISPLAY_NAME}.`, warning: null, allowedStatuses: 'any', group: 'License', visibility: 'enabled', }), async ({ effects }) => { const store = await sdk.store.getOwn(effects, sdk.StorePath).const() const key = (store as { license_key: string | null }).license_key if (!key) { return { message: `No license key is stored. Run "Activate license" or "Buy license" first.`, } } // Offline let offline try { offline = new Verifier(PublicKey.fromPem(ISSUER_PUBKEY_PEM)).verify(key) } catch (e) { await sdk.store.getOwn(effects, sdk.StorePath).merge({ license_last_status: `offline-invalid: ${errMsg(e)}`, }) return { message: `The stored key no longer verifies cryptographically. This means ` + `the key was edited, truncated, or the issuer public key was ` + `rotated by the seller. Contact the seller.\n\nDetails: ${errMsg(e)}`, } } // Online let onlineLine: string try { const client = await firstReachableClient() const r = await client.validate(key, PRODUCT_SLUG, undefined) onlineLine = r.ok ? 'Server says: OK' : `Server rejected the key: ${r.reason ?? 'unknown'}` await sdk.store.getOwn(effects, sdk.StorePath).merge({ license_last_status: r.ok ? 'valid (online-checked)' : `rejected: ${r.reason ?? 'unknown'}`, }) } catch (_) { onlineLine = 'Server: unreachable (offline — relying on signature only)' } return { message: `Signature: OK\n` + `${onlineLine}\n\n` + `Product: ${offline.productId}\n` + `License id: ${offline.licenseId}\n` + `Issued at: ${offline.payload.issuedAt}`, } }, ) async function firstReachableClient(): Promise { const urls = [LICENSING_BASE_URL, LICENSING_BASE_URL_TOR].filter(Boolean) as string[] for (const base of urls) { const client = new Client(base) try { await client.fetchPubkeyPem() return client } catch (_) {} } throw new Error('no reachable URL') } function errMsg(e: unknown): string { return e instanceof Error ? e.message : String(e) }