/** * License & entitlements (spec: "Monetization Gates", M1). The single * state every gate reads — license parsing/verification and enforcement * points stay decoupled. * * A key IS a signed token (self-verifying — no server round-trip, no * unlock flash, CI works offline): * * om_live_. * payload = { plan, domains?: ["acme.com", "*.acme.com" | "*"], apps?: ["com.acme.app"], exp, keyId } * * Ed25519 via WebCrypto against the public key embedded below; mint with * dev/tools/sign-license.mjs (private key never in the repo). Keys are * PUBLISHABLE, deployment-restricted via a web `domains` claim and/or a * packaged-app `apps` claim — they MAY appear in markup, unlike data * credentials. * * Both claims are deployment SCOPING, not access control: keys are * publishable by design, and commercial-use enforcement is LEGAL rather than * technical (see LICENSE.md) — `keyId` is what makes a deployment * attributable. `appId` reaches this module as a plain argument whose * provenance it cannot verify, so a native host MUST source it from trusted * platform build metadata and never from page script or bridge JSON. * * NOTE: this header is emitted verbatim into the published dist/license.d.ts. * Keep it to the contract a consumer needs. Comparative analysis of how far * each claim can be trusted, and why no origin heuristic guards the app * claim, lives in agent-map-library-architecture.md (internal, not shipped). * * Decisions (spec, RESOLVED 2026-07-14; REVISED 2026-08-05): free tier = * 5 layers / 25k rows per layer / 20 MB fetch; any paid plan hides the * badge; violations are per-layer hard drops with loud structured errors; * no feature is gated at launch. REVISED: caps lift entirely in a DEV * context — loopback http(s), any non-http(s) scheme (file://, tauri://, * …), or no `location` at all (tests/SSR) — and apply only on hosted * http(s). Deliberately NO embedded-runtime sniffing: the commercial / * non-commercial boundary is a fact about intent that code cannot observe, * so packaged-app enforcement is LEGAL, not technical — LICENSE.md states * that the dev exemption is a technical convenience, never a grant, and * commercial distribution (hosted OR packaged) requires a key regardless * of whether gates fired. The badge stays on in every context (it is the * attribution obligation, not a cap). App-identity keys for packaged apps * are the onlymap-native roadmap's `apps` claim. * * Failure semantics: invalid/expired/mismatched keys → free tier + one * console warning — never a broken map. While a key verifies (WebCrypto * is async; sub-millisecond in practice) gates FAIL OPEN so a paid page * never flashes gate errors; the settle re-reconciles. */ import type { LayerIR } from "./ir"; import type { ValidationEntry } from "./validation"; export interface Entitlements { plan: string; maxLayers: number; maxRowsPerLayer: number; maxFetchBytes: number; hideBadge: boolean; keyId: string | null; } export declare const FREE_TIER: Entitlements; /** Trusted packaged-app identity supplied by the native host, never page/bridge input. */ export interface LicenseIdentity { appId: string; } /** The dev context's entitlements: every cap lifted, badge KEPT (attribution is an obligation, not a cap — and it carries the license pointer into shipped artifacts). */ export declare const DEV_TIER: Entitlements; /** * Dev context = not a hosted http(s) page: loopback hosts, any non-http(s) * scheme (file://, a desktop webview's custom scheme), or no `location` at * all (headless tests, SSR). Computed per call (cheap string checks) so the * test seam works; `location` itself never changes within a page. */ export declare function isDevContext(): boolean; /** * @internal Test seam: the unit suite runs under happy-dom on localhost, so * every cap test would silently become vacuous without forcing "hosted". * `null` restores real detection. */ export declare function setHostedContextForTests(hosted: boolean | null): void; export declare function getEntitlements(): Readonly; /** True while a key is being verified — gates fail open so a paid page never flashes violations. */ export declare function licenseSettled(): boolean; /** Notified whenever entitlements settle or change (RuntimeCore re-reconciles; the badge re-evaluates). */ export declare function subscribeLicense(fn: () => void): () => void; /** `*` matches anything; `*.acme.com` matches acme.com and every subdomain; otherwise exact. */ export declare function domainMatches(hostname: string, domains: string[]): boolean; /** App/bundle identifiers are platform identities and therefore exact, case-sensitive matches. */ export declare function appMatches(appId: string, apps: string[]): boolean; /** * Verify a key and swap entitlements. Async because WebCrypto is; gates * fail open until it settles. Any failure lands on the free tier with a * warning — a bad key must never break the map. */ export declare function configureLicense(key: string, identity?: LicenseIdentity): Promise; /** One layer whose data the row cap cut down this pass — drives the viewer-facing notice. */ export interface TruncatedLayer { id: string; /** Rows that DID render (the cap). */ shown: number; /** Rows the layer actually had. */ total: number; } export interface LicenseGateResult { irs: LayerIR[]; /** Empty (a shared frozen constant) on the overwhelmingly common no-violation pass. */ truncated: readonly TruncatedLayer[]; } /** * DECIDED violation mode (revised 2026-07-28): layers past the COUNT cap * (manifest order) still hard-drop — a sixth layer has no meaningful partial * form. An over-ROW-cap layer now renders its first `maxRowsPerLayer` rows * instead of nothing, because the previous hard drop turned one row over the * line into a blank layer, and the cliff landed unpredictably: CityJSON's * `?om-surfaces=1` emits ~33 rows per building with a long tail (one building * in the sample tile is 963 faces), so "how many buildings fit" is not * something an author can reason about in advance. * * The original objection to truncation — that it looks like a data bug and * hides the gate — is answered by making it VISIBLE rather than by rendering * nothing: `truncated` drives a dismissible viewer-facing notice (see * quota-notice.ts), while this `report` stream keeps severity "error" for the * dev/agent channel. The subset is arbitrary (source order, which for most * formats is not spatially meaningful) and the message says so, since a * scattered subset otherwise reads as a rendering fault. * * Runtime-internal layers (trace temps, draw preview, tooltip plumbing) * neither count nor drop. Violations report once per (layer, reason) through * `report` until the violation clears (`warned` is the caller-owned dedup set). */ export declare function applyLicenseGates(irs: LayerIR[], report: (entry: ValidationEntry) => void, warned: Set): LicenseGateResult; export declare function setLicensePublicKeyForTests(rawKey: Uint8Array | null): void; export declare function resetLicenseForTests(): void;