@revealui/core 0.13.1 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/client/admin/components/AdminDashboard.d.ts +8 -1
- package/dist/client/admin/components/AdminDashboard.d.ts.map +1 -1
- package/dist/client/admin/components/AdminDashboard.js +11 -10
- package/dist/client/admin/components/CollectionList.d.ts.map +1 -1
- package/dist/client/admin/components/CollectionList.js +4 -6
- package/dist/client/admin/components/DocumentForm.d.ts.map +1 -1
- package/dist/client/admin/components/DocumentForm.js +26 -26
- package/dist/client/admin/components/GlobalForm.d.ts.map +1 -1
- package/dist/client/admin/components/GlobalForm.js +10 -9
- package/dist/client/admin/page.d.ts.map +1 -1
- package/dist/client/admin/page.js +2 -1
- package/dist/client/richtext/components/ImageNodeComponent.d.ts.map +1 -1
- package/dist/client/richtext/components/ImageNodeComponent.js +3 -22
- package/dist/client/richtext/components/ImageUploadButton.d.ts.map +1 -1
- package/dist/client/richtext/components/ImageUploadButton.js +2 -4
- package/dist/client/richtext/plugins/FloatingToolbarPlugin.d.ts.map +1 -1
- package/dist/client/richtext/plugins/FloatingToolbarPlugin.js +3 -2
- package/dist/client/richtext/plugins/ToolbarPlugin.d.ts.map +1 -1
- package/dist/client/richtext/plugins/ToolbarPlugin.js +3 -2
- package/dist/client/ui/index.d.ts.map +1 -1
- package/dist/client/ui/index.js +10 -6
- package/dist/cogs-breaker.d.ts +27 -0
- package/dist/cogs-breaker.d.ts.map +1 -0
- package/dist/cogs-breaker.js +29 -0
- package/dist/error-handling/error-boundary.d.ts.map +1 -1
- package/dist/error-handling/error-boundary.js +2 -9
- package/dist/error-handling/fallback-components.d.ts.map +1 -1
- package/dist/error-handling/fallback-components.js +9 -86
- package/dist/instance/logger.d.ts +2 -1
- package/dist/instance/logger.d.ts.map +1 -1
- package/dist/license/mint-client.d.ts +15 -4
- package/dist/license/mint-client.d.ts.map +1 -1
- package/dist/license/mint-client.js +38 -9
- package/dist/license.d.ts +39 -14
- package/dist/license.d.ts.map +1 -1
- package/dist/license.js +111 -34
- package/dist/margin-governor.d.ts +82 -0
- package/dist/margin-governor.d.ts.map +1 -0
- package/dist/margin-governor.js +143 -0
- package/dist/revforge-license-boot.d.ts +0 -10
- package/dist/revforge-license-boot.d.ts.map +1 -1
- package/dist/revforge-license-boot.js +41 -20
- package/dist/types/runtime.d.ts +3 -6
- package/dist/types/runtime.d.ts.map +1 -1
- package/dist/utils/block-conversion.d.ts.map +1 -1
- package/dist/utils/block-conversion.js +2 -1
- package/package.json +64 -3
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* License mint client (GAP-260 P4-3).
|
|
2
|
+
* License mint client (GAP-260 P4-3 + residual hosted gate).
|
|
3
3
|
*
|
|
4
4
|
* Single entry for online mint surfaces (Stripe webhooks, admin generate).
|
|
5
5
|
* Routes to either:
|
|
6
6
|
* - remote `apps/license-signer` POST /internal/mint when
|
|
7
|
-
* REVEALUI_LICENSE_SIGN_VIA_SIGNER is truthy, or
|
|
7
|
+
* REVEALUI_LICENSE_SIGN_VIA_SIGNER is truthy, or when
|
|
8
|
+
* REVEALUI_DEPLOYMENT_MODE=hosted (explicit; never local private-key mint), or
|
|
8
9
|
* - local {@link generateLicenseKey} with REVEALUI_LICENSE_PRIVATE_KEY
|
|
9
|
-
*
|
|
10
|
+
* only when MODE is not hosted and SIGN_VIA is off (tests / dogfood).
|
|
10
11
|
*
|
|
11
12
|
* Offline stamper (revforge) keeps calling generateLicenseKey directly.
|
|
12
13
|
*
|
|
@@ -14,6 +15,7 @@
|
|
|
14
15
|
* REVEALUI_LICENSE_SIGN_VIA_SIGNER=1|true|yes|on
|
|
15
16
|
* REVEALUI_LICENSE_SIGNER_URL base URL (e.g. http://127.0.0.1:8791)
|
|
16
17
|
* REVEALUI_SIGNER_INVOKE_SECRET HMAC secret (no REVEALUI_SECRET fallback)
|
|
18
|
+
* REVEALUI_DEPLOYMENT_MODE=hosted forces remote-only (SIGN_VIA required)
|
|
17
19
|
*
|
|
18
20
|
* Env (local path):
|
|
19
21
|
* REVEALUI_LICENSE_PRIVATE_KEY PKCS#8 Ed25519 PEM (required)
|
|
@@ -46,22 +48,43 @@ export function isSignViaSigner(env = process.env) {
|
|
|
46
48
|
const raw = (env.REVEALUI_LICENSE_SIGN_VIA_SIGNER ?? '').trim().toLowerCase();
|
|
47
49
|
return raw === '1' || raw === 'true' || raw === 'yes' || raw === 'on';
|
|
48
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Explicit hosted SaaS posture only. Does not use private-key inference
|
|
53
|
+
* (that belongs to detectDeploymentMode). Hosted online mint must never
|
|
54
|
+
* fall through to mintLocal.
|
|
55
|
+
*/
|
|
56
|
+
export function isExplicitHostedMintMode(env = process.env) {
|
|
57
|
+
return (env.REVEALUI_DEPLOYMENT_MODE ?? '').trim().toLowerCase() === 'hosted';
|
|
58
|
+
}
|
|
59
|
+
function hasRemoteMintConfig(env) {
|
|
60
|
+
const url = env.REVEALUI_LICENSE_SIGNER_URL?.trim() ?? '';
|
|
61
|
+
const secret = env.REVEALUI_SIGNER_INVOKE_SECRET?.trim() ?? '';
|
|
62
|
+
return url.length > 0 && secret.length > 0;
|
|
63
|
+
}
|
|
49
64
|
/**
|
|
50
65
|
* Whether this process can mint a license key (local private key OR remote
|
|
51
66
|
* signer fully configured). Used by call sites that used to gate only on
|
|
52
67
|
* REVEALUI_LICENSE_PRIVATE_KEY presence.
|
|
68
|
+
*
|
|
69
|
+
* Explicit MODE=hosted never treats a local private key as sufficient.
|
|
53
70
|
*/
|
|
54
71
|
export function canMintLicense(env = process.env) {
|
|
72
|
+
if (isExplicitHostedMintMode(env)) {
|
|
73
|
+
return isSignViaSigner(env) && hasRemoteMintConfig(env);
|
|
74
|
+
}
|
|
55
75
|
if (isSignViaSigner(env)) {
|
|
56
|
-
|
|
57
|
-
const secret = env.REVEALUI_SIGNER_INVOKE_SECRET?.trim() ?? '';
|
|
58
|
-
return url.length > 0 && secret.length > 0;
|
|
76
|
+
return hasRemoteMintConfig(env);
|
|
59
77
|
}
|
|
60
78
|
return Boolean(env.REVEALUI_LICENSE_PRIVATE_KEY?.trim());
|
|
61
79
|
}
|
|
62
80
|
/** Human-readable reason mint is unavailable (for CRITICAL logs). */
|
|
63
81
|
export function mintConfigMissingMessage(env = process.env) {
|
|
64
|
-
if (isSignViaSigner(env)) {
|
|
82
|
+
if (isExplicitHostedMintMode(env) && !isSignViaSigner(env)) {
|
|
83
|
+
return ('REVEALUI_DEPLOYMENT_MODE=hosted requires REVEALUI_LICENSE_SIGN_VIA_SIGNER ' +
|
|
84
|
+
'with REVEALUI_LICENSE_SIGNER_URL and REVEALUI_SIGNER_INVOKE_SECRET ' +
|
|
85
|
+
'(local private-key mint is disabled on hosted)');
|
|
86
|
+
}
|
|
87
|
+
if (isSignViaSigner(env) || isExplicitHostedMintMode(env)) {
|
|
65
88
|
return ('REVEALUI_LICENSE_SIGN_VIA_SIGNER is set but REVEALUI_LICENSE_SIGNER_URL ' +
|
|
66
89
|
'and/or REVEALUI_SIGNER_INVOKE_SECRET are missing');
|
|
67
90
|
}
|
|
@@ -180,13 +203,19 @@ async function mintLocal(input, env) {
|
|
|
180
203
|
return generateLicenseKey(payload, privateKey, expiresInSeconds, publicKey);
|
|
181
204
|
}
|
|
182
205
|
/**
|
|
183
|
-
* Mint a signed license JWT — remote signer when
|
|
206
|
+
* Mint a signed license JWT — remote signer when SIGN_VIA is on or MODE is
|
|
207
|
+
* explicitly hosted; else local private key (tests / dogfood only).
|
|
184
208
|
*/
|
|
185
209
|
export async function mintLicenseKey(input, options = {}) {
|
|
186
210
|
const env = options.env ?? process.env;
|
|
187
211
|
const fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
188
212
|
const normalized = withPerpetualSiteCaps(input);
|
|
189
|
-
|
|
213
|
+
// Hosted online mint never uses the local private-key path, even when the
|
|
214
|
+
// key is still present in env during migration.
|
|
215
|
+
if (isExplicitHostedMintMode(env) && !isSignViaSigner(env)) {
|
|
216
|
+
throw new LicenseMintConfigError(mintConfigMissingMessage(env));
|
|
217
|
+
}
|
|
218
|
+
if (isSignViaSigner(env) || isExplicitHostedMintMode(env)) {
|
|
190
219
|
return mintViaSigner(normalized, env, fetchImpl);
|
|
191
220
|
}
|
|
192
221
|
return mintLocal(normalized, env);
|
package/dist/license.d.ts
CHANGED
|
@@ -48,15 +48,27 @@ export interface GracePeriodConfig {
|
|
|
48
48
|
/** Days of cached-license grace when infra is unreachable (default: 7) */
|
|
49
49
|
infraDays: number;
|
|
50
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Hard cap on LICENSE_GRACE_* days. Env values above this become jwtVerify
|
|
53
|
+
* clockTolerance and must not keep an expired subscription JWT valid forever.
|
|
54
|
+
* Same spirit as {@link MAX_LICENSE_CACHE_TTL_MS}.
|
|
55
|
+
*/
|
|
56
|
+
export declare const MAX_LICENSE_GRACE_DAYS = 30;
|
|
57
|
+
/**
|
|
58
|
+
* Parse a LICENSE_GRACE_* env value. Unset / non-numeric / negative → fallback.
|
|
59
|
+
* Values above {@link MAX_LICENSE_GRACE_DAYS} are clamped and warned.
|
|
60
|
+
*/
|
|
61
|
+
export declare function parseLicenseGraceDaysEnv(envValue: string | undefined, fallback: number): number;
|
|
51
62
|
/**
|
|
52
63
|
* Configure grace period durations. Useful for testing.
|
|
64
|
+
* Values are clamped to {@link MAX_LICENSE_GRACE_DAYS}.
|
|
53
65
|
*/
|
|
54
66
|
export declare function configureGracePeriods(overrides: Partial<GracePeriodConfig>): void;
|
|
55
67
|
/** Decoded license payload schema */
|
|
56
68
|
declare const licensePayloadSchema: z.ZodObject<{
|
|
57
69
|
tier: z.ZodEnum<{
|
|
58
|
-
max: "max";
|
|
59
70
|
pro: "pro";
|
|
71
|
+
max: "max";
|
|
60
72
|
enterprise: "enterprise";
|
|
61
73
|
}>;
|
|
62
74
|
customerId: z.ZodString;
|
|
@@ -179,15 +191,16 @@ export declare const REFRESH_ACCEPT_DAYS = 30;
|
|
|
179
191
|
* `POST /api/license/refresh`, accepting a token whose `exp` is past by at most
|
|
180
192
|
* `refreshAcceptDays` (default {@link REFRESH_ACCEPT_DAYS}).
|
|
181
193
|
*
|
|
194
|
+
* `expectedCustomerId` is required (fail closed). A token for customer A must
|
|
195
|
+
* not verify when the caller bound customer B. An empty / missing bind returns
|
|
196
|
+
* null so an unbound refresh cannot exfil another customer's current key.
|
|
197
|
+
*
|
|
182
198
|
* It reuses the SAME ordered multi-key verification (GAP-259 rotation
|
|
183
|
-
* composition) and payload schema as {@link validateLicenseKey}
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
* the token's `customerId` claim and then independently requires an ACTIVE
|
|
187
|
-
* license row for it. This function only proves possession of a recently-valid
|
|
188
|
-
* signed key. It never mints and never grants entitlement on its own.
|
|
199
|
+
* composition) and payload schema as {@link validateLicenseKey}. The 30-day
|
|
200
|
+
* window stays so a recently-expired key can still fetch its own renewal after
|
|
201
|
+
* the bind succeeds. It never mints and never grants entitlement on its own.
|
|
189
202
|
*/
|
|
190
|
-
export declare function validateLicenseKeyForRefresh(licenseKey: string, publicKey: string | readonly string[], refreshAcceptDays?: number): Promise<LicensePayload | null>;
|
|
203
|
+
export declare function validateLicenseKeyForRefresh(licenseKey: string, publicKey: string | readonly string[], expectedCustomerId: string, refreshAcceptDays?: number): Promise<LicensePayload | null>;
|
|
191
204
|
/**
|
|
192
205
|
* Outcome of a signing-keypair self-test. See {@link selfVerifyLicenseKeypair}.
|
|
193
206
|
*/
|
|
@@ -229,13 +242,20 @@ export declare function selfVerifyLicenseKeypair(privateKey: string, publicKeys:
|
|
|
229
242
|
* @returns The resolved license tier
|
|
230
243
|
*/
|
|
231
244
|
export declare function initializeLicense(): Promise<LicenseTier>;
|
|
245
|
+
/**
|
|
246
|
+
* Await an in-flight or due revalidation. Tests and async request gates use
|
|
247
|
+
* this so TTL expiry still proves licensed after the key is re-checked.
|
|
248
|
+
*/
|
|
249
|
+
export declare function refreshLicenseIfStale(): Promise<LicenseTier>;
|
|
232
250
|
/**
|
|
233
251
|
* Returns the current license tier.
|
|
234
|
-
*
|
|
252
|
+
* Uninitialized state is free. A stale cache keeps the last validated tier
|
|
253
|
+
* and triggers revalidation of the configured key.
|
|
235
254
|
*/
|
|
236
255
|
export declare function getCurrentTier(): LicenseTier;
|
|
237
256
|
/**
|
|
238
|
-
* Returns the full license payload, or null if no valid license
|
|
257
|
+
* Returns the full license payload, or null if no valid license is cached.
|
|
258
|
+
* Stale TTL does not clear a still-valid payload.
|
|
239
259
|
*/
|
|
240
260
|
export declare function getLicensePayload(): LicensePayload | null;
|
|
241
261
|
/**
|
|
@@ -249,15 +269,19 @@ export declare function getLicensePayload(): LicensePayload | null;
|
|
|
249
269
|
*
|
|
250
270
|
* Matching rules (no authored regex, per the fleet no-regex rule):
|
|
251
271
|
* - `host` is lower-cased and stripped of any `:port` suffix
|
|
252
|
-
* - `localhost` / `127.0.0.1`
|
|
253
|
-
*
|
|
272
|
+
* - `localhost` / `127.0.0.1` match only when `allowLoopback` is true (trial /
|
|
273
|
+
* unpublished). A published API with a `domains` claim must not accept a
|
|
274
|
+
* spoofed Host: localhost bypass.
|
|
254
275
|
* - otherwise `host` must equal a licensed domain OR be a subdomain of one
|
|
255
276
|
* (`app.example.com` matches `example.com`)
|
|
256
277
|
*
|
|
257
278
|
* @param host raw Host header value or URL hostname (a `:port` suffix is tolerated)
|
|
258
279
|
* @param domains the license payload's `domains` claim
|
|
280
|
+
* @param options.allowLoopback opt-in loopback bypass (default false)
|
|
259
281
|
*/
|
|
260
|
-
export declare function hostMatchesLicensedDomains(host: string, domains: readonly string[]
|
|
282
|
+
export declare function hostMatchesLicensedDomains(host: string, domains: readonly string[], options?: {
|
|
283
|
+
allowLoopback?: boolean;
|
|
284
|
+
}): boolean;
|
|
261
285
|
/**
|
|
262
286
|
* Checks whether the current license is at least the given tier.
|
|
263
287
|
* Also validates that the license has not expired (checks JWT exp claim).
|
|
@@ -289,7 +313,8 @@ export declare function getMaxSites(): number;
|
|
|
289
313
|
export declare function getMaxUsers(): number;
|
|
290
314
|
/**
|
|
291
315
|
* Returns the maximum agent tasks per billing cycle for the current license.
|
|
292
|
-
* Track B metering: free=
|
|
316
|
+
* Track B metering: free=0 (Local AI, no public agent quota), pro=10K,
|
|
317
|
+
* max=50K, enterprise=unlimited.
|
|
293
318
|
*/
|
|
294
319
|
export declare function getMaxAgentTasks(): number;
|
|
295
320
|
/**
|
package/dist/license.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"license.d.ts","sourceRoot":"","sources":["../src/license.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAYxB,8BAA8B;AAC9B,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,YAAY,CAAC;AAEhE;;;;;;;;;;GAUG;AACH,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,OAAO,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;AAE/F,gDAAgD;AAChD,MAAM,WAAW,kBAAkB;IACjC,8CAA8C;IAC9C,OAAO,EAAE,OAAO,CAAC;IACjB,6BAA6B;IAC7B,IAAI,EAAE,WAAW,CAAC;IAClB,qBAAqB;IACrB,IAAI,EAAE,WAAW,CAAC;IAClB,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,6EAA6E;IAC7E,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,6EAA6E;AAC7E,MAAM,WAAW,iBAAiB;IAChC,2EAA2E;IAC3E,gBAAgB,EAAE,MAAM,CAAC;IACzB,6EAA6E;IAC7E,aAAa,EAAE,MAAM,CAAC;IACtB,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;CACnB;
|
|
1
|
+
{"version":3,"file":"license.d.ts","sourceRoot":"","sources":["../src/license.ts"],"names":[],"mappings":"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"}
|
package/dist/license.js
CHANGED
|
@@ -22,26 +22,50 @@ async function getJose() {
|
|
|
22
22
|
/** JWT issuer and audience for license tokens — prevents cross-environment replay */
|
|
23
23
|
const LICENSE_ISSUER = process.env.REVEALUI_LICENSE_ISSUER ?? 'https://revealui.com';
|
|
24
24
|
const LICENSE_AUDIENCE = process.env.REVEALUI_LICENSE_AUDIENCE ?? 'revealui-license';
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Hard cap on LICENSE_GRACE_* days. Env values above this become jwtVerify
|
|
27
|
+
* clockTolerance and must not keep an expired subscription JWT valid forever.
|
|
28
|
+
* Same spirit as {@link MAX_LICENSE_CACHE_TTL_MS}.
|
|
29
|
+
*/
|
|
30
|
+
export const MAX_LICENSE_GRACE_DAYS = 30;
|
|
31
|
+
/**
|
|
32
|
+
* Parse a LICENSE_GRACE_* env value. Unset / non-numeric / negative → fallback.
|
|
33
|
+
* Values above {@link MAX_LICENSE_GRACE_DAYS} are clamped and warned.
|
|
34
|
+
*/
|
|
35
|
+
export function parseLicenseGraceDaysEnv(envValue, fallback) {
|
|
36
|
+
if (!envValue)
|
|
37
|
+
return fallback;
|
|
38
|
+
const parsed = Number.parseInt(envValue, 10);
|
|
39
|
+
if (!Number.isFinite(parsed) || parsed < 0)
|
|
40
|
+
return fallback;
|
|
41
|
+
if (parsed > MAX_LICENSE_GRACE_DAYS) {
|
|
42
|
+
logger.warn(`LICENSE_GRACE_* days=${parsed} exceeds the ${MAX_LICENSE_GRACE_DAYS}-day cap; using ${MAX_LICENSE_GRACE_DAYS}. Larger values extend jwtVerify clockTolerance so expired subscription JWTs stay valid and are not permitted.`);
|
|
43
|
+
return MAX_LICENSE_GRACE_DAYS;
|
|
36
44
|
}
|
|
37
|
-
return
|
|
45
|
+
return parsed;
|
|
38
46
|
}
|
|
47
|
+
function clampGraceDays(days) {
|
|
48
|
+
if (!Number.isFinite(days) || days < 0)
|
|
49
|
+
return 0;
|
|
50
|
+
return Math.min(days, MAX_LICENSE_GRACE_DAYS);
|
|
51
|
+
}
|
|
52
|
+
const DEFAULT_GRACE = {
|
|
53
|
+
subscriptionDays: parseLicenseGraceDaysEnv(process.env.LICENSE_GRACE_SUBSCRIPTION_DAYS, 3),
|
|
54
|
+
perpetualDays: parseLicenseGraceDaysEnv(process.env.LICENSE_GRACE_PERPETUAL_DAYS, 30),
|
|
55
|
+
infraDays: parseLicenseGraceDaysEnv(process.env.LICENSE_GRACE_INFRA_DAYS, 7),
|
|
56
|
+
};
|
|
39
57
|
let graceConfig = { ...DEFAULT_GRACE };
|
|
40
58
|
/**
|
|
41
59
|
* Configure grace period durations. Useful for testing.
|
|
60
|
+
* Values are clamped to {@link MAX_LICENSE_GRACE_DAYS}.
|
|
42
61
|
*/
|
|
43
62
|
export function configureGracePeriods(overrides) {
|
|
44
|
-
|
|
63
|
+
const merged = { ...DEFAULT_GRACE, ...overrides };
|
|
64
|
+
graceConfig = {
|
|
65
|
+
subscriptionDays: clampGraceDays(merged.subscriptionDays),
|
|
66
|
+
perpetualDays: clampGraceDays(merged.perpetualDays),
|
|
67
|
+
infraDays: clampGraceDays(merged.infraDays),
|
|
68
|
+
};
|
|
45
69
|
}
|
|
46
70
|
/** Decoded license payload schema */
|
|
47
71
|
const licensePayloadSchema = z.object({
|
|
@@ -106,6 +130,8 @@ const DEFAULT_CACHE_CONFIG = {
|
|
|
106
130
|
};
|
|
107
131
|
let cacheConfig = { ...DEFAULT_CACHE_CONFIG };
|
|
108
132
|
let cachedAt = 0;
|
|
133
|
+
let refreshInFlight = null;
|
|
134
|
+
let refreshEpoch = 0;
|
|
109
135
|
/**
|
|
110
136
|
* Configure the license cache TTL.
|
|
111
137
|
* Useful for tests (short TTL) or deployments needing faster revocation detection.
|
|
@@ -240,16 +266,20 @@ export const REFRESH_ACCEPT_DAYS = 30;
|
|
|
240
266
|
* `POST /api/license/refresh`, accepting a token whose `exp` is past by at most
|
|
241
267
|
* `refreshAcceptDays` (default {@link REFRESH_ACCEPT_DAYS}).
|
|
242
268
|
*
|
|
269
|
+
* `expectedCustomerId` is required (fail closed). A token for customer A must
|
|
270
|
+
* not verify when the caller bound customer B. An empty / missing bind returns
|
|
271
|
+
* null so an unbound refresh cannot exfil another customer's current key.
|
|
272
|
+
*
|
|
243
273
|
* It reuses the SAME ordered multi-key verification (GAP-259 rotation
|
|
244
|
-
* composition) and payload schema as {@link validateLicenseKey}
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
* the token's `customerId` claim and then independently requires an ACTIVE
|
|
248
|
-
* license row for it. This function only proves possession of a recently-valid
|
|
249
|
-
* signed key. It never mints and never grants entitlement on its own.
|
|
274
|
+
* composition) and payload schema as {@link validateLicenseKey}. The 30-day
|
|
275
|
+
* window stays so a recently-expired key can still fetch its own renewal after
|
|
276
|
+
* the bind succeeds. It never mints and never grants entitlement on its own.
|
|
250
277
|
*/
|
|
251
|
-
export async function validateLicenseKeyForRefresh(licenseKey, publicKey, refreshAcceptDays = REFRESH_ACCEPT_DAYS) {
|
|
252
|
-
|
|
278
|
+
export async function validateLicenseKeyForRefresh(licenseKey, publicKey, expectedCustomerId, refreshAcceptDays = REFRESH_ACCEPT_DAYS) {
|
|
279
|
+
const bound = expectedCustomerId.trim();
|
|
280
|
+
if (bound.length === 0)
|
|
281
|
+
return null;
|
|
282
|
+
return verifyAndParseLicenseJwt(licenseKey, publicKey, refreshAcceptDays * 86_400, bound);
|
|
253
283
|
}
|
|
254
284
|
/**
|
|
255
285
|
* Shared verify + parse path for {@link validateLicenseKey} and
|
|
@@ -468,26 +498,66 @@ export async function initializeLicense() {
|
|
|
468
498
|
}
|
|
469
499
|
return payload.tier;
|
|
470
500
|
}
|
|
501
|
+
function isCacheStale() {
|
|
502
|
+
return cachedAt > 0 && Date.now() - cachedAt > cacheConfig.ttlMs;
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Re-validate the configured key. Single-flight so concurrent readers share one
|
|
506
|
+
* verify. On failure, keep the last validated state (do not flap to free).
|
|
507
|
+
*/
|
|
508
|
+
function scheduleLicenseRefresh() {
|
|
509
|
+
if (refreshInFlight)
|
|
510
|
+
return refreshInFlight;
|
|
511
|
+
const epoch = refreshEpoch;
|
|
512
|
+
refreshInFlight = initializeLicense()
|
|
513
|
+
.then((tier) => {
|
|
514
|
+
if (epoch !== refreshEpoch)
|
|
515
|
+
return cachedState.tier;
|
|
516
|
+
return tier;
|
|
517
|
+
})
|
|
518
|
+
.catch((err) => {
|
|
519
|
+
logger.warn('License revalidation failed; keeping last validated state', {
|
|
520
|
+
error: err instanceof Error ? err.message : String(err),
|
|
521
|
+
});
|
|
522
|
+
return cachedState.tier;
|
|
523
|
+
})
|
|
524
|
+
.finally(() => {
|
|
525
|
+
refreshInFlight = null;
|
|
526
|
+
});
|
|
527
|
+
return refreshInFlight;
|
|
528
|
+
}
|
|
471
529
|
/**
|
|
472
|
-
*
|
|
473
|
-
*
|
|
530
|
+
* When the cache TTL elapses, re-validate the current key instead of dropping
|
|
531
|
+
* to free. Request-path readers keep the last validated tier until refresh
|
|
532
|
+
* completes so a live Pro process cannot flap to free while a valid key is set.
|
|
474
533
|
*/
|
|
475
534
|
function evictStaleCache() {
|
|
476
|
-
if (
|
|
477
|
-
|
|
478
|
-
cachedAt = 0;
|
|
535
|
+
if (isCacheStale()) {
|
|
536
|
+
void scheduleLicenseRefresh();
|
|
479
537
|
}
|
|
480
538
|
}
|
|
539
|
+
/**
|
|
540
|
+
* Await an in-flight or due revalidation. Tests and async request gates use
|
|
541
|
+
* this so TTL expiry still proves licensed after the key is re-checked.
|
|
542
|
+
*/
|
|
543
|
+
export async function refreshLicenseIfStale() {
|
|
544
|
+
if (isCacheStale() || refreshInFlight) {
|
|
545
|
+
return scheduleLicenseRefresh();
|
|
546
|
+
}
|
|
547
|
+
return cachedState.tier;
|
|
548
|
+
}
|
|
481
549
|
/**
|
|
482
550
|
* Returns the current license tier.
|
|
483
|
-
*
|
|
551
|
+
* Uninitialized state is free. A stale cache keeps the last validated tier
|
|
552
|
+
* and triggers revalidation of the configured key.
|
|
484
553
|
*/
|
|
485
554
|
export function getCurrentTier() {
|
|
486
555
|
evictStaleCache();
|
|
487
556
|
return cachedState.tier;
|
|
488
557
|
}
|
|
489
558
|
/**
|
|
490
|
-
* Returns the full license payload, or null if no valid license
|
|
559
|
+
* Returns the full license payload, or null if no valid license is cached.
|
|
560
|
+
* Stale TTL does not clear a still-valid payload.
|
|
491
561
|
*/
|
|
492
562
|
export function getLicensePayload() {
|
|
493
563
|
evictStaleCache();
|
|
@@ -504,20 +574,24 @@ export function getLicensePayload() {
|
|
|
504
574
|
*
|
|
505
575
|
* Matching rules (no authored regex, per the fleet no-regex rule):
|
|
506
576
|
* - `host` is lower-cased and stripped of any `:port` suffix
|
|
507
|
-
* - `localhost` / `127.0.0.1`
|
|
508
|
-
*
|
|
577
|
+
* - `localhost` / `127.0.0.1` match only when `allowLoopback` is true (trial /
|
|
578
|
+
* unpublished). A published API with a `domains` claim must not accept a
|
|
579
|
+
* spoofed Host: localhost bypass.
|
|
509
580
|
* - otherwise `host` must equal a licensed domain OR be a subdomain of one
|
|
510
581
|
* (`app.example.com` matches `example.com`)
|
|
511
582
|
*
|
|
512
583
|
* @param host raw Host header value or URL hostname (a `:port` suffix is tolerated)
|
|
513
584
|
* @param domains the license payload's `domains` claim
|
|
585
|
+
* @param options.allowLoopback opt-in loopback bypass (default false)
|
|
514
586
|
*/
|
|
515
|
-
export function hostMatchesLicensedDomains(host, domains) {
|
|
587
|
+
export function hostMatchesLicensedDomains(host, domains, options = {}) {
|
|
516
588
|
const normalized = (host.trim().toLowerCase().split(':')[0] ?? '').trim();
|
|
517
589
|
if (!normalized)
|
|
518
590
|
return false;
|
|
519
|
-
if (
|
|
591
|
+
if (options.allowLoopback === true &&
|
|
592
|
+
(normalized === 'localhost' || normalized === '127.0.0.1')) {
|
|
520
593
|
return true;
|
|
594
|
+
}
|
|
521
595
|
return domains.some((domain) => {
|
|
522
596
|
const d = domain.trim().toLowerCase();
|
|
523
597
|
return d.length > 0 && (normalized === d || normalized.endsWith(`.${d}`));
|
|
@@ -658,7 +732,8 @@ export function getMaxUsers() {
|
|
|
658
732
|
}
|
|
659
733
|
/**
|
|
660
734
|
* Returns the maximum agent tasks per billing cycle for the current license.
|
|
661
|
-
* Track B metering: free=
|
|
735
|
+
* Track B metering: free=0 (Local AI, no public agent quota), pro=10K,
|
|
736
|
+
* max=50K, enterprise=unlimited.
|
|
662
737
|
*/
|
|
663
738
|
export function getMaxAgentTasks() {
|
|
664
739
|
evictStaleCache();
|
|
@@ -668,7 +743,7 @@ export function getMaxAgentTasks() {
|
|
|
668
743
|
return 50_000;
|
|
669
744
|
if (cachedState.tier === 'pro')
|
|
670
745
|
return 10_000;
|
|
671
|
-
return
|
|
746
|
+
return 0;
|
|
672
747
|
}
|
|
673
748
|
/**
|
|
674
749
|
* Default subscription license JWT lifetime (seconds) — one year. Used as the
|
|
@@ -825,6 +900,8 @@ export async function generateLicenseKey(payload, privateKey, expiresInSeconds =
|
|
|
825
900
|
* Reset license state. Primarily for testing.
|
|
826
901
|
*/
|
|
827
902
|
export function resetLicenseState() {
|
|
903
|
+
refreshEpoch += 1;
|
|
904
|
+
refreshInFlight = null;
|
|
828
905
|
cachedState = { tier: 'free', payload: null, validatedAt: null, keyPresentButInvalid: false };
|
|
829
906
|
cachedAt = 0;
|
|
830
907
|
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GAP-256 PR-3 — pure margin admission decide (no DB, no I/O).
|
|
3
|
+
*
|
|
4
|
+
* Call sites: apps/server admitFreeIntake (reads snapshot, then this).
|
|
5
|
+
* HARD: never call after users insert for free_signup; pure module has no COUNT.
|
|
6
|
+
*
|
|
7
|
+
* @see docs/specs/2026-08-09-gap-256-margin-admission-layers-2-3.md
|
|
8
|
+
*/
|
|
9
|
+
export type AdmissionChannel = 'free_signup' | 'paid_signup' | 'paid_checkout' | 'invite_accept' | 'admin_provision' | 'waitlist_claim_free' | 'waitlist_claim_paid';
|
|
10
|
+
export type PayingIntentSignal = {
|
|
11
|
+
kind: 'none';
|
|
12
|
+
} | {
|
|
13
|
+
kind: 'checkout';
|
|
14
|
+
tier: 'pro' | 'max' | 'enterprise';
|
|
15
|
+
} | {
|
|
16
|
+
kind: 'existing_paid';
|
|
17
|
+
accountId: string;
|
|
18
|
+
tier: string;
|
|
19
|
+
status: 'active';
|
|
20
|
+
};
|
|
21
|
+
export type CohortLimits = {
|
|
22
|
+
maxSites: number;
|
|
23
|
+
maxUsers: number;
|
|
24
|
+
maxAgentTasks: number;
|
|
25
|
+
};
|
|
26
|
+
export type SnapshotMode = 'open' | 'lean' | 'waitlist';
|
|
27
|
+
/** Minimal snapshot view for pure decide (from margin_snapshots row). */
|
|
28
|
+
export interface MarginSnapshotView {
|
|
29
|
+
id: string;
|
|
30
|
+
mode: SnapshotMode;
|
|
31
|
+
computedAt: Date;
|
|
32
|
+
netCents?: number;
|
|
33
|
+
freeCostRatio?: string | null;
|
|
34
|
+
projected7dCents?: number | null;
|
|
35
|
+
}
|
|
36
|
+
export interface GovernorFlags {
|
|
37
|
+
/** Master; off → admit always with open limits */
|
|
38
|
+
enabled: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* When true (default for first enable): compute mode for logs but never
|
|
41
|
+
* waitlist / never lean-enforce on admit (always admit).
|
|
42
|
+
*/
|
|
43
|
+
shadow: boolean;
|
|
44
|
+
/** Hours after which a snapshot is ignored (fail-open to open). Default 36. */
|
|
45
|
+
staleHours: number;
|
|
46
|
+
}
|
|
47
|
+
export type PureAdmitResult = {
|
|
48
|
+
decision: 'admit';
|
|
49
|
+
mode: SnapshotMode | 'bypass' | 'disabled' | 'shadow';
|
|
50
|
+
cohortLimits: CohortLimits;
|
|
51
|
+
snapshotId: string | null;
|
|
52
|
+
reason: string;
|
|
53
|
+
shadow: boolean;
|
|
54
|
+
} | {
|
|
55
|
+
decision: 'waitlist';
|
|
56
|
+
mode: 'waitlist';
|
|
57
|
+
reason: string;
|
|
58
|
+
snapshotId: string | null;
|
|
59
|
+
shadow: false;
|
|
60
|
+
httpStatus: 202;
|
|
61
|
+
code: 'WAITLISTED';
|
|
62
|
+
};
|
|
63
|
+
/** Open free cohort = standard hosted free limits (caller supplies base). */
|
|
64
|
+
export declare function freeCohortLimitsForMode(mode: 'open' | 'lean', openLimits: CohortLimits, leanMaxAgentTasks?: number): CohortLimits;
|
|
65
|
+
/** Paid-signup pending only — never free cohort (K20). */
|
|
66
|
+
export declare function paidPendingLimits(): CohortLimits;
|
|
67
|
+
export declare function governorFlagsFromEnv(env?: Record<string, string | undefined>): GovernorFlags;
|
|
68
|
+
/**
|
|
69
|
+
* Pure admit decision for free intake.
|
|
70
|
+
* Does not touch users table, COUNT, or waitlist I/O.
|
|
71
|
+
*/
|
|
72
|
+
export declare function decideFreeIntake(input: {
|
|
73
|
+
channel: AdmissionChannel;
|
|
74
|
+
deploymentMode: 'hosted' | 'forge' | 'unknown';
|
|
75
|
+
payingIntent: PayingIntentSignal;
|
|
76
|
+
snapshot: MarginSnapshotView | null;
|
|
77
|
+
flags: GovernorFlags;
|
|
78
|
+
openLimits: CohortLimits;
|
|
79
|
+
leanMaxAgentTasks?: number;
|
|
80
|
+
now?: Date;
|
|
81
|
+
}): PureAdmitResult;
|
|
82
|
+
//# sourceMappingURL=margin-governor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"margin-governor.d.ts","sourceRoot":"","sources":["../src/margin-governor.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,MAAM,gBAAgB,GACxB,aAAa,GACb,aAAa,GACb,eAAe,GACf,eAAe,GACf,iBAAiB,GACjB,qBAAqB,GACrB,qBAAqB,CAAC;AAE1B,MAAM,MAAM,kBAAkB,GAC1B;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,KAAK,GAAG,KAAK,GAAG,YAAY,CAAA;CAAE,GACxD;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,QAAQ,CAAA;CAAE,CAAC;AAEjF,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,CAAC;AAExD,yEAAyE;AACzE,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,YAAY,CAAC;IACnB,UAAU,EAAE,IAAI,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,aAAa;IAC5B,kDAAkD;IAClD,OAAO,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,MAAM,EAAE,OAAO,CAAC;IAChB,+EAA+E;IAC/E,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,eAAe,GACvB;IACE,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,YAAY,GAAG,QAAQ,GAAG,UAAU,GAAG,QAAQ,CAAC;IACtD,YAAY,EAAE,YAAY,CAAC;IAC3B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;CACjB,GACD;IACE,QAAQ,EAAE,UAAU,CAAC;IACrB,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,KAAK,CAAC;IACd,UAAU,EAAE,GAAG,CAAC;IAChB,IAAI,EAAE,YAAY,CAAC;CACpB,CAAC;AAEN,6EAA6E;AAC7E,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,GAAG,MAAM,EACrB,UAAU,EAAE,YAAY,EACxB,iBAAiB,SAAM,GACtB,YAAY,CASd;AAED,0DAA0D;AAC1D,wBAAgB,iBAAiB,IAAI,YAAY,CAEhD;AAED,wBAAgB,oBAAoB,CAClC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GACpD,aAAa,CAOf;AAkCD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACtC,OAAO,EAAE,gBAAgB,CAAC;IAC1B,cAAc,EAAE,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;IAC/C,YAAY,EAAE,kBAAkB,CAAC;IACjC,QAAQ,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACpC,KAAK,EAAE,aAAa,CAAC;IACrB,UAAU,EAAE,YAAY,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ,GAAG,eAAe,CAyFlB"}
|