/** * Whose licence this install runs under. * * `WELLFORMED_LICENSE` holds a licence line: a short statement, signed by the * copyright holder, naming the party the licence was granted to. The software * reads it, checks the signature against the keys it was built with, and says * on start whose it is. * * What that setting is *not* is a lock. Wellformed starts either way and no * data is ever out of reach because of it. What a valid licence changes is * that the hosted plans stop applying — which is the honest consequence, since * a plan is a thing you buy from Bram and a licensed install has already * settled that on paper. * * The line asserts something attributable, and that is the whole of its force: * an installation carrying it is claiming, in a form nobody else can forge, to * be running under a named party's licence. Copying an install copies that * name along with it. */ import { LICENCE_PRODUCT, LICENCE_PUBLIC_KEYS } from './key.js'; import { describeLicence, verifyLicence, LicenceError, type Licence, type LicenceFault } from './verify.js'; export type LicenceState = /** Signed, checked, and for this software. */ | { status: 'licensed'; licence: Licence; description: string } /** Nothing set. The ordinary state of the hosted service and of a trial. */ | { status: 'none' } /** * The setting still holds one of the mode words this used to take. * * Worth its own state rather than a parse failure: an install upgrading from * a version where `selfhosted` meant something deserves to be told what * changed, not told its licence is malformed. */ | { status: 'legacy'; word: string } /** Something is there, and it is not a licence this software accepts. */ | { status: 'rejected'; fault: LicenceFault; message: string }; const LEGACY_WORDS = new Set(['hosted', 'selfhosted', 'self-hosted']); export function readLicence(value: string | undefined = process.env.WELLFORMED_LICENSE): LicenceState { const raw = value?.trim() ?? ''; if (raw === '') return { status: 'none' }; if (LEGACY_WORDS.has(raw.toLowerCase())) return { status: 'legacy', word: raw }; try { const licence = verifyLicence(raw, LICENCE_PUBLIC_KEYS, { product: LICENCE_PRODUCT }); return { status: 'licensed', licence, description: describeLicence(licence) }; } catch (error) { if (error instanceof LicenceError) return { status: 'rejected', fault: error.fault, message: error.message }; throw error; } } /** The name to show an operator, or null where there is no licence to name. */ export function licensedTo(state: LicenceState): string | null { return state.status === 'licensed' ? state.description : null; } /** * What to print on start. * * A licensed install gets one line, because that is a fact and not a warning. * Everything else gets said plainly enough that an operator who set the value * and got it wrong finds out now rather than from a support thread later. */ export function licenceNotice(state: LicenceState): string[] { switch (state.status) { case 'licensed': return [`[wellformed] licensed to ${state.description}`]; case 'none': return ['[wellformed] no licence set — the hosted plans apply on this install']; case 'legacy': return [ `[wellformed] WELLFORMED_LICENSE=${state.word} is no longer a setting this understands.`, ' It used to be a mode word. It now holds the licence itself: one signed line,', ' naming the party the licence was granted to. Until one is set, the hosted', ' plans apply. Ask the copyright holder for the licence line.', ]; case 'rejected': return [ `[wellformed] the licence in WELLFORMED_LICENSE was not accepted: ${state.message}`, ' The hosted plans apply until it is. If the line was pasted, check that all of', ' it is on one line, unbroken and unquoted.', ]; } }