@revealui/core 0.15.0 → 0.15.4

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.
@@ -1 +1 @@
1
- {"version":3,"file":"rest.d.ts","sourceRoot":"","sources":["../../src/api/rest.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EACV,MAAM,EACN,YAAY,EAGZ,cAAc,EAGd,YAAY,EACZ,gBAAgB,EAChB,WAAW,EACZ,MAAM,mBAAmB,CAAC;AAE3B,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,cAAc;IAC7C,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;IACX,GAAG,CAAC,EAAE,CAAC,CAAC;IACR,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,QAAQ,CAAC,EAAE,YAAY,CAAC;CACzB;AA8aD,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,gBAAgB,GACzB,OAAO,CAAC,QAAQ,CAAC,CAiGnB;AAGD,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,gBAAgB,GACzB;IACD,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAChE,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IACjE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAChE,MAAM,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnE,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAClE,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;CACrE,CAaA"}
1
+ {"version":3,"file":"rest.d.ts","sourceRoot":"","sources":["../../src/api/rest.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EACV,MAAM,EACN,YAAY,EAGZ,cAAc,EAGd,YAAY,EACZ,gBAAgB,EAChB,WAAW,EACZ,MAAM,mBAAmB,CAAC;AAE3B,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,cAAc;IAC7C,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;IACX,GAAG,CAAC,EAAE,CAAC,CAAC;IACR,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,QAAQ,CAAC,EAAE,YAAY,CAAC;CACzB;AAuaD,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,gBAAgB,GACzB,OAAO,CAAC,QAAQ,CAAC,CAiGnB;AAGD,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,gBAAgB,GACzB;IACD,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAChE,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IACjE,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAChE,MAAM,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnE,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAClE,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;CACrE,CAaA"}
package/dist/api/rest.js CHANGED
@@ -153,8 +153,10 @@ function getCorsOriginsFromConfig(config) {
153
153
  return [];
154
154
  }
155
155
  /**
156
- * Get allowed CORS origin for a request
157
- * Checks config first, then environment variable, then allows all for development
156
+ * Get allowed CORS origin for a request.
157
+ * The allowlist comes from config, then REVEALUI_CORS_ORIGINS.
158
+ * A request origin on that list is returned. A missing list, an empty list,
159
+ * or a non-matching origin returns an empty allow-origin so nothing is granted.
158
160
  */
159
161
  function getAllowedOrigin(request, config) {
160
162
  let allowedOrigins = getCorsOriginsFromConfig(config);
@@ -163,18 +165,10 @@ function getAllowedOrigin(request, config) {
163
165
  allowedOrigins = process.env.REVEALUI_CORS_ORIGINS.split(',').map((s) => s.trim());
164
166
  }
165
167
  const origin = request.headers.get('origin');
166
- // If config has specific origins and request origin matches, use it
167
168
  if (allowedOrigins.length > 0 && origin && allowedOrigins.includes(origin)) {
168
169
  return origin;
169
170
  }
170
- // In development, allow all origins for convenience
171
- // In production, this should never be reached if CORS is properly configured
172
- if (process.env.NODE_ENV === 'development') {
173
- return '*';
174
- }
175
- // In production, if no allowed origins configured, deny all (secure default)
176
- // This prevents accidental exposure in production
177
- return origin || '';
171
+ return '';
178
172
  }
179
173
  /**
180
174
  * Get standard CORS headers for a response
package/dist/index.d.ts CHANGED
@@ -20,7 +20,7 @@ export type { UniversalPostgresAdapterConfig } from './database/universal-postgr
20
20
  export { universalPostgresAdapter } from './database/universal-postgres.js';
21
21
  export { createRevealUI, createRevealUIAccessRule, createRevealUIBlock, createRevealUICollection, createRevealUIField, } from './factories/index.js';
22
22
  export { type FeatureFlags, getFeatures, getFeaturesForTier, getRequiredTier, isFeatureEnabled, } from './features.js';
23
- export { computeKeyId, configureGracePeriods, configureLicenseCache, type GracePeriodConfig, generateLicenseKey, getCurrentTier, getGraceConfig, getLicensePayload, getLicenseStatus, getMaxSites, getMaxUsers, initializeLicense, isLicensed, type LicenseCacheConfig, type LicenseCheckResult, type LicenseMode, type LicensePayload, type LicenseTier, readLicenseExp, readLicenseJti, resetLicenseState, validateLicenseKey, } from './license.js';
23
+ export { computeKeyId, configureGracePeriods, configureLicenseCache, type GracePeriodConfig, generateLicenseKey, getCurrentTier, getGraceConfig, getLicensePayload, getLicensePublicKeyTrustManifest, getLicenseStatus, getMaxSites, getMaxUsers, initializeLicense, isLicensed, type LicenseCacheConfig, type LicenseCheckResult, type LicenseMode, type LicensePayload, type LicensePublicKeyTrustManifest, type LicenseTier, readLicenseExp, readLicenseJti, resetLicenseState, validateLicenseKey, } from './license.js';
24
24
  export { getRevealUI as getRevealUINext } from './nextjs/index.js';
25
25
  export { formBuilderPlugin } from './plugins/form-builder.js';
26
26
  export { nestedDocsPlugin } from './plugins/nested-docs.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -36,7 +36,7 @@ export { universalPostgresAdapter } from './database/universal-postgres.js';
36
36
  export { createRevealUI, createRevealUIAccessRule, createRevealUIBlock, createRevealUICollection, createRevealUIField, } from './factories/index.js';
37
37
  export { getFeatures, getFeaturesForTier, getRequiredTier, isFeatureEnabled, } from './features.js';
38
38
  // License and feature flags
39
- export { computeKeyId, configureGracePeriods, configureLicenseCache, generateLicenseKey, getCurrentTier, getGraceConfig, getLicensePayload, getLicenseStatus, getMaxSites, getMaxUsers, initializeLicense, isLicensed, readLicenseExp, readLicenseJti, resetLicenseState, validateLicenseKey, } from './license.js';
39
+ export { computeKeyId, configureGracePeriods, configureLicenseCache, generateLicenseKey, getCurrentTier, getGraceConfig, getLicensePayload, getLicensePublicKeyTrustManifest, getLicenseStatus, getMaxSites, getMaxUsers, initializeLicense, isLicensed, readLicenseExp, readLicenseJti, resetLicenseState, validateLicenseKey, } from './license.js';
40
40
  // Next.js integration
41
41
  // Note: `withRevealUI` is intentionally NOT re-exported here — it pulls in
42
42
  // `node:fs` at module load, which causes Next's NFT tracer to pull the whole
package/dist/license.d.ts CHANGED
@@ -18,7 +18,7 @@ export type LicenseTier = 'free' | 'pro' | 'max' | 'enterprise';
18
18
  *
19
19
  * - active: License is valid and current
20
20
  * - grace: License has an issue but is within a grace period (still allowed)
21
- * - read-only: Perpetual support lapsed past grace — reads allowed, writes blocked
21
+ * - read-only: Reserved compatibility state; support lapse does not block perpetual writes
22
22
  * - expired: Grace period exhausted — degraded to free tier
23
23
  * - invalid: Signature invalid or tampered — hard fail
24
24
  * - missing: No license configured — free tier
@@ -36,14 +36,14 @@ export interface LicenseCheckResult {
36
36
  reason?: string;
37
37
  /** Milliseconds remaining in grace period (undefined if not in grace) */
38
38
  graceRemainingMs?: number;
39
- /** Whether writes should be blocked (read-only mode for lapsed perpetual) */
39
+ /** Whether writes should be blocked; perpetual support lapse does not set this flag */
40
40
  readOnly: boolean;
41
41
  }
42
42
  /** Grace period configuration (in days). Overridable via env for testing. */
43
43
  export interface GracePeriodConfig {
44
44
  /** Days after subscription expiry before degrading to free (default: 3) */
45
45
  subscriptionDays: number;
46
- /** Days after perpetual support lapse before read-only mode (default: 30) */
46
+ /** Deprecated compatibility setting; does not limit acquired perpetual runtime access */
47
47
  perpetualDays: number;
48
48
  /** Days of cached-license grace when infra is unreachable (default: 7) */
49
49
  infraDays: number;
@@ -140,6 +140,35 @@ export declare function readPemEnv(name: string, env?: Record<string, string | u
140
140
  * live request.
141
141
  */
142
142
  export declare function getPublicKeys(): string[];
143
+ type LicensePublicKeyRole = 'current' | 'next';
144
+ declare const LICENSE_TRUST_ISSUER = "https://revealui.com";
145
+ declare const LICENSE_TRUST_AUDIENCE = "revealui-license";
146
+ export type LicensePublicKeyTrustManifest = {
147
+ version: 1;
148
+ issuer: typeof LICENSE_TRUST_ISSUER;
149
+ audience: typeof LICENSE_TRUST_AUDIENCE;
150
+ keys: Array<{
151
+ role: LicensePublicKeyRole;
152
+ algorithm: 'EdDSA';
153
+ publicKey: string;
154
+ jwtKid: string;
155
+ keyId: string;
156
+ }>;
157
+ digest: string;
158
+ /** Compatibility field for clients of the original current-key response. */
159
+ publicKey: string;
160
+ };
161
+ export type LicenseTrustVerification = {
162
+ payload: LicensePayload;
163
+ verifiedKeyId: string;
164
+ trustSetDigest: string;
165
+ };
166
+ /**
167
+ * Build the public, versioned trust manifest from the same ordered key snapshot
168
+ * consumed by license verification. Any malformed configured key makes the
169
+ * endpoint fail closed; a malformed NEXT entry is never silently discarded.
170
+ */
171
+ export declare function getLicensePublicKeyTrustManifest(): Promise<LicensePublicKeyTrustManifest>;
143
172
  /**
144
173
  * Computes a deterministic Key ID (kid) from a public key PEM string.
145
174
  * Returns the first 8 characters of the SHA-256 hex digest of the PEM.
@@ -173,6 +202,12 @@ export declare function computeKeyId(publicKeyPem: string): Promise<string>;
173
202
  * options below. Signature is enforced via the public key.
174
203
  */
175
204
  export declare function validateLicenseKey(licenseKey: string, publicKey: string | readonly string[], expectedCustomerId?: string): Promise<LicensePayload | null>;
205
+ /**
206
+ * Verify against one already-built trust manifest and identify the exact
207
+ * configured key that accepted the token. The returned digest and key ID let
208
+ * a strict online verifier bind its result to the same set the caller fetched.
209
+ */
210
+ export declare function validateLicenseKeyAgainstTrustManifest(licenseKey: string, manifest: LicensePublicKeyTrustManifest, expectedCustomerId?: string): Promise<LicenseTrustVerification | null>;
176
211
  /**
177
212
  * How far past `exp` a license JWT is still accepted by the refresh endpoint
178
213
  * (GAP-287 PR-1). Ratified by the owner 2026-07-18 at 30 days.
@@ -201,6 +236,8 @@ export declare const REFRESH_ACCEPT_DAYS = 30;
201
236
  * the bind succeeds. It never mints and never grants entitlement on its own.
202
237
  */
203
238
  export declare function validateLicenseKeyForRefresh(licenseKey: string, publicKey: string | readonly string[], expectedCustomerId: string, refreshAcceptDays?: number): Promise<LicensePayload | null>;
239
+ /** Authenticated operator containment: validates issuer/signature/customer, not entitlement expiry. */
240
+ export declare function validateLicenseKeyForOperator(licenseKey: string, publicKey: string | readonly string[], expectedCustomerId: string): Promise<LicensePayload | null>;
204
241
  /**
205
242
  * Outcome of a signing-keypair self-test. See {@link selfVerifyLicenseKeypair}.
206
243
  */
@@ -1 +1 @@
1
- {"version":3,"file":"license.d.ts","sourceRoot":"","sources":["../src/license.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"license.d.ts","sourceRoot":"","sources":["../src/license.ts"],"names":[],"mappings":"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"}
package/dist/license.js CHANGED
@@ -162,10 +162,10 @@ export function readPemEnv(name, env = process.env) {
162
162
  const raw = env[name];
163
163
  if (raw === undefined)
164
164
  return undefined;
165
- const trimmed = raw.trim();
165
+ const trimmed = normalizePem(raw).trim();
166
166
  if (trimmed.length === 0)
167
167
  return undefined;
168
- return normalizePem(trimmed);
168
+ return trimmed;
169
169
  }
170
170
  /**
171
171
  * The ordered public-key candidates used to verify license JWTs.
@@ -183,14 +183,133 @@ export function readPemEnv(name, env = process.env) {
183
183
  * live request.
184
184
  */
185
185
  export function getPublicKeys() {
186
- const keys = [];
187
- const current = process.env.REVEALUI_LICENSE_PUBLIC_KEY;
186
+ return getPublicKeySnapshot().map(({ publicKey }) => publicKey);
187
+ }
188
+ const LICENSE_TRUST_ISSUER = 'https://revealui.com';
189
+ const LICENSE_TRUST_AUDIENCE = 'revealui-license';
190
+ const LICENSE_TRUST_MANIFEST_MAX_BYTES = 16 * 1024;
191
+ const LICENSE_TRUST_PEM_MAX_BYTES = 2 * 1024;
192
+ const LICENSE_TRUST_SPKI_BEGIN = '-----BEGIN PUBLIC KEY-----';
193
+ const LICENSE_TRUST_SPKI_END = '-----END PUBLIC KEY-----';
194
+ function getPublicKeySnapshot() {
195
+ const current = readPemEnv('REVEALUI_LICENSE_PUBLIC_KEY');
196
+ const next = readPemEnv('REVEALUI_LICENSE_PUBLIC_KEY_NEXT');
197
+ const candidates = [];
188
198
  if (current)
189
- keys.push(normalizePem(current));
190
- const next = process.env.REVEALUI_LICENSE_PUBLIC_KEY_NEXT;
199
+ candidates.push({ role: 'current', publicKey: current });
191
200
  if (next)
192
- keys.push(normalizePem(next));
193
- return keys;
201
+ candidates.push({ role: 'next', publicKey: next });
202
+ return candidates;
203
+ }
204
+ function toHex(bytes) {
205
+ let hex = '';
206
+ for (const byte of bytes)
207
+ hex += byte.toString(16).padStart(2, '0');
208
+ return hex;
209
+ }
210
+ function decodePublicKeySpki(publicKeyPem) {
211
+ if (new TextEncoder().encode(publicKeyPem).byteLength > LICENSE_TRUST_PEM_MAX_BYTES) {
212
+ throw new Error('License public key exceeds the supported size');
213
+ }
214
+ if (!(publicKeyPem.startsWith(LICENSE_TRUST_SPKI_BEGIN) &&
215
+ publicKeyPem.endsWith(LICENSE_TRUST_SPKI_END))) {
216
+ throw new Error('License trust entries must contain a public SPKI PEM');
217
+ }
218
+ const body = publicKeyPem
219
+ .slice(LICENSE_TRUST_SPKI_BEGIN.length, -LICENSE_TRUST_SPKI_END.length)
220
+ .split('\n')
221
+ .join('')
222
+ .split('\r')
223
+ .join('');
224
+ if (body.length === 0 || body.includes('-')) {
225
+ throw new Error('License public key PEM body is invalid');
226
+ }
227
+ let binary;
228
+ try {
229
+ binary = atob(body);
230
+ }
231
+ catch {
232
+ throw new Error('License public key PEM body is invalid');
233
+ }
234
+ if (btoa(binary) !== body)
235
+ throw new Error('License public key PEM body is not canonical base64');
236
+ const der = new Uint8Array(binary.length);
237
+ for (let index = 0; index < binary.length; index += 1) {
238
+ der[index] = binary.charCodeAt(index);
239
+ }
240
+ return der;
241
+ }
242
+ async function describeLicensePublicKey(candidate) {
243
+ const spki = decodePublicKeySpki(candidate.publicKey);
244
+ const spkiBuffer = new ArrayBuffer(spki.byteLength);
245
+ new Uint8Array(spkiBuffer).set(spki);
246
+ const key = await crypto.subtle.importKey('spki', spkiBuffer, { name: 'Ed25519' }, true, [
247
+ 'verify',
248
+ ]);
249
+ if (key.algorithm.name !== 'Ed25519') {
250
+ throw new Error('License trust entries must use Ed25519');
251
+ }
252
+ const canonicalSpki = new Uint8Array(await crypto.subtle.exportKey('spki', key));
253
+ if (canonicalSpki.length !== spki.length ||
254
+ canonicalSpki.some((byte, index) => byte !== spki[index])) {
255
+ throw new Error('License public key SPKI is not canonical');
256
+ }
257
+ const keyId = toHex(new Uint8Array(await crypto.subtle.digest('SHA-256', canonicalSpki)));
258
+ const jwtKid = await computeKeyId(candidate.publicKey);
259
+ return {
260
+ role: candidate.role,
261
+ algorithm: 'EdDSA',
262
+ publicKey: candidate.publicKey,
263
+ jwtKid,
264
+ keyId,
265
+ };
266
+ }
267
+ /**
268
+ * Build the public, versioned trust manifest from the same ordered key snapshot
269
+ * consumed by license verification. Any malformed configured key makes the
270
+ * endpoint fail closed; a malformed NEXT entry is never silently discarded.
271
+ */
272
+ export async function getLicensePublicKeyTrustManifest() {
273
+ if (LICENSE_ISSUER !== LICENSE_TRUST_ISSUER || LICENSE_AUDIENCE !== LICENSE_TRUST_AUDIENCE) {
274
+ throw new Error('License issuer configuration does not match the hosted trust contract');
275
+ }
276
+ const snapshot = getPublicKeySnapshot();
277
+ if (snapshot.length === 0 || snapshot[0]?.role !== 'current' || snapshot.length > 2) {
278
+ throw new Error('License current public key is not configured');
279
+ }
280
+ const keys = await Promise.all(snapshot.map(describeLicensePublicKey));
281
+ const keyIds = new Set();
282
+ const jwtKids = new Set();
283
+ for (const key of keys) {
284
+ if (keyIds.has(key.keyId) || jwtKids.has(key.jwtKid)) {
285
+ throw new Error('License trust set contains duplicate key identities');
286
+ }
287
+ keyIds.add(key.keyId);
288
+ jwtKids.add(key.jwtKid);
289
+ }
290
+ const version = 1;
291
+ const digestInput = JSON.stringify({
292
+ version,
293
+ issuer: LICENSE_TRUST_ISSUER,
294
+ audience: LICENSE_TRUST_AUDIENCE,
295
+ keys: keys.map(({ role, algorithm, keyId }) => ({ role, algorithm, keyId })),
296
+ });
297
+ const digest = toHex(new Uint8Array(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(digestInput))));
298
+ const current = keys[0];
299
+ if (!current)
300
+ throw new Error('License current public key is not configured');
301
+ const manifest = {
302
+ version,
303
+ issuer: LICENSE_TRUST_ISSUER,
304
+ audience: LICENSE_TRUST_AUDIENCE,
305
+ keys,
306
+ digest,
307
+ publicKey: current.publicKey,
308
+ };
309
+ if (new TextEncoder().encode(JSON.stringify(manifest)).byteLength > LICENSE_TRUST_MANIFEST_MAX_BYTES) {
310
+ throw new Error('License trust manifest exceeds the supported size');
311
+ }
312
+ return manifest;
194
313
  }
195
314
  /**
196
315
  * Reads the license key from environment.
@@ -248,6 +367,79 @@ export async function validateLicenseKey(licenseKey, publicKey, expectedCustomer
248
367
  // is available for grace-period calculations in isLicensed().
249
368
  return verifyAndParseLicenseJwt(licenseKey, publicKey, graceConfig.subscriptionDays * 86_400, expectedCustomerId);
250
369
  }
370
+ /**
371
+ * Verify against one already-built trust manifest and identify the exact
372
+ * configured key that accepted the token. The returned digest and key ID let
373
+ * a strict online verifier bind its result to the same set the caller fetched.
374
+ */
375
+ export async function validateLicenseKeyAgainstTrustManifest(licenseKey, manifest, expectedCustomerId) {
376
+ try {
377
+ // Detach from the caller before the first await. Callers may retain and
378
+ // mutate their input object while signature verification is in flight.
379
+ const snapshot = Object.freeze({
380
+ version: manifest.version,
381
+ issuer: manifest.issuer,
382
+ audience: manifest.audience,
383
+ digest: manifest.digest,
384
+ publicKey: manifest.publicKey,
385
+ keys: Array.isArray(manifest.keys)
386
+ ? Object.freeze(manifest.keys.map((key) => Object.freeze({ ...key })))
387
+ : manifest.keys,
388
+ });
389
+ if (snapshot.version !== 1 ||
390
+ snapshot.issuer !== LICENSE_TRUST_ISSUER ||
391
+ snapshot.audience !== LICENSE_TRUST_AUDIENCE ||
392
+ !/^[0-9a-f]{64}$/.test(snapshot.digest) ||
393
+ !Array.isArray(snapshot.keys) ||
394
+ snapshot.keys.length === 0 ||
395
+ snapshot.keys.length > 2 ||
396
+ snapshot.keys[0]?.role !== 'current' ||
397
+ snapshot.keys.some((key, index) => key.role !== (index === 0 ? 'current' : 'next'))) {
398
+ return null;
399
+ }
400
+ const described = await Promise.all(snapshot.keys.map((key) => describeLicensePublicKey({ role: key.role, publicKey: key.publicKey })));
401
+ const keyIds = new Set();
402
+ const jwtKids = new Set();
403
+ for (let index = 0; index < described.length; index += 1) {
404
+ const actual = described[index];
405
+ const supplied = snapshot.keys[index];
406
+ if (!(actual && supplied) ||
407
+ actual.algorithm !== supplied.algorithm ||
408
+ actual.keyId !== supplied.keyId ||
409
+ actual.jwtKid !== supplied.jwtKid ||
410
+ keyIds.has(actual.keyId) ||
411
+ jwtKids.has(actual.jwtKid)) {
412
+ return null;
413
+ }
414
+ keyIds.add(actual.keyId);
415
+ jwtKids.add(actual.jwtKid);
416
+ }
417
+ const digestInput = JSON.stringify({
418
+ version: snapshot.version,
419
+ issuer: snapshot.issuer,
420
+ audience: snapshot.audience,
421
+ keys: described.map(({ role, algorithm, keyId }) => ({ role, algorithm, keyId })),
422
+ });
423
+ const expectedDigest = toHex(new Uint8Array(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(digestInput))));
424
+ if (snapshot.digest !== expectedDigest || snapshot.publicKey !== described[0]?.publicKey) {
425
+ return null;
426
+ }
427
+ for (const key of described) {
428
+ const payload = await validateLicenseKey(licenseKey, key.publicKey, expectedCustomerId);
429
+ if (payload) {
430
+ return {
431
+ payload,
432
+ verifiedKeyId: key.keyId,
433
+ trustSetDigest: snapshot.digest,
434
+ };
435
+ }
436
+ }
437
+ }
438
+ catch {
439
+ return null;
440
+ }
441
+ return null;
442
+ }
251
443
  /**
252
444
  * How far past `exp` a license JWT is still accepted by the refresh endpoint
253
445
  * (GAP-287 PR-1). Ratified by the owner 2026-07-18 at 30 days.
@@ -281,6 +473,12 @@ export async function validateLicenseKeyForRefresh(licenseKey, publicKey, expect
281
473
  return null;
282
474
  return verifyAndParseLicenseJwt(licenseKey, publicKey, refreshAcceptDays * 86_400, bound);
283
475
  }
476
+ /** Authenticated operator containment: validates issuer/signature/customer, not entitlement expiry. */
477
+ export async function validateLicenseKeyForOperator(licenseKey, publicKey, expectedCustomerId) {
478
+ if (!expectedCustomerId.trim())
479
+ return null;
480
+ return verifyAndParseLicenseJwt(licenseKey, publicKey, 0, expectedCustomerId, true);
481
+ }
284
482
  /**
285
483
  * Shared verify + parse path for {@link validateLicenseKey} and
286
484
  * {@link validateLicenseKeyForRefresh}. The ONLY behavioral difference between
@@ -288,7 +486,7 @@ export async function validateLicenseKeyForRefresh(licenseKey, publicKey, expect
288
486
  * still accepted — so the rotation-aware multi-key loop and the payload schema
289
487
  * live in exactly one place.
290
488
  */
291
- async function verifyAndParseLicenseJwt(licenseKey, publicKey, clockToleranceSeconds, expectedCustomerId) {
489
+ async function verifyAndParseLicenseJwt(licenseKey, publicKey, clockToleranceSeconds, expectedCustomerId, operator = false) {
292
490
  const candidates = typeof publicKey === 'string' ? [publicKey] : [...publicKey];
293
491
  if (candidates.length === 0)
294
492
  return null;
@@ -303,12 +501,27 @@ async function verifyAndParseLicenseJwt(licenseKey, publicKey, clockToleranceSec
303
501
  for (const candidate of ordered) {
304
502
  try {
305
503
  const key = await jose.importSPKI(candidate, 'EdDSA');
306
- const { payload } = await jose.jwtVerify(licenseKey, key, {
307
- algorithms: ['EdDSA'],
308
- clockTolerance: clockToleranceSeconds,
309
- issuer: LICENSE_ISSUER,
310
- audience: LICENSE_AUDIENCE,
311
- });
504
+ let payload;
505
+ if (operator) {
506
+ const verified = await jose.compactVerify(licenseKey, key, { algorithms: ['EdDSA'] });
507
+ const claims = JSON.parse(new TextDecoder().decode(verified.payload));
508
+ if (claims.iss !== LICENSE_ISSUER ||
509
+ !(claims.aud === LICENSE_AUDIENCE ||
510
+ (Array.isArray(claims.aud) && claims.aud.includes(LICENSE_AUDIENCE))) ||
511
+ (claims.nbf !== undefined &&
512
+ (typeof claims.nbf !== 'number' || claims.nbf > Date.now() / 1000))) {
513
+ continue;
514
+ }
515
+ payload = claims;
516
+ }
517
+ else {
518
+ ({ payload } = await jose.jwtVerify(licenseKey, key, {
519
+ algorithms: ['EdDSA'],
520
+ clockTolerance: clockToleranceSeconds,
521
+ issuer: LICENSE_ISSUER,
522
+ audience: LICENSE_AUDIENCE,
523
+ }));
524
+ }
312
525
  verifiedPayload = payload;
313
526
  verified = true;
314
527
  break;
@@ -882,17 +1095,18 @@ export async function generateLicenseKey(payload, privateKey, expiresInSeconds =
882
1095
  // Strip the optional jti from the spread so jose.setJti() is the single
883
1096
  // source of the claim (avoids a duplicate field in the payload).
884
1097
  const { jti: _ignoredJti, ...rest } = payload;
1098
+ const issuedAt = Math.floor(Date.now() / 1000);
885
1099
  const builder = new jose.SignJWT({ ...rest })
886
1100
  .setProtectedHeader(header)
887
- .setIssuedAt()
1101
+ .setIssuedAt(issuedAt)
888
1102
  // Phase 1 audit B-2: enforce nbf so tokens cannot be replayed pre-issue
889
1103
  // by a clock-skewed client.
890
- .setNotBefore('0s')
1104
+ .setNotBefore(issuedAt)
891
1105
  .setJti(jti)
892
1106
  .setIssuer(LICENSE_ISSUER)
893
1107
  .setAudience(LICENSE_AUDIENCE);
894
1108
  if (expiresInSeconds !== null) {
895
- builder.setExpirationTime(`${expiresInSeconds}s`);
1109
+ builder.setExpirationTime(issuedAt + expiresInSeconds);
896
1110
  }
897
1111
  return builder.sign(key);
898
1112
  }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Sentry `environment` for a deploy.
3
+ *
4
+ * `NODE_ENV=production` is set on staging as well as production, so it is not
5
+ * a safe Sentry label. Prefer `SENTRY_ENVIRONMENT` (then `REVEALUI_DEPLOY_ENV`,
6
+ * then `NEXT_PUBLIC_SENTRY_ENVIRONMENT`). When those are unset, derive the
7
+ * label from public hosts. A `*.staging.revealui.com` host is never reported
8
+ * as production. Vercel preview stays `preview`.
9
+ */
10
+ export type SentryEnvironmentName = 'production' | 'staging' | 'preview' | 'development' | 'test';
11
+ /** Non-secret label. Set `SENTRY_ENVIRONMENT=staging` on staging Vercel and Fly. */
12
+ export declare const SENTRY_ENVIRONMENT_VAR = "SENTRY_ENVIRONMENT";
13
+ export interface SentryEnvironmentSource {
14
+ SENTRY_ENVIRONMENT?: string | undefined;
15
+ REVEALUI_DEPLOY_ENV?: string | undefined;
16
+ NEXT_PUBLIC_SENTRY_ENVIRONMENT?: string | undefined;
17
+ NODE_ENV?: string | undefined;
18
+ VERCEL_ENV?: string | undefined;
19
+ REVEALUI_API_URL?: string | undefined;
20
+ NEXT_PUBLIC_API_URL?: string | undefined;
21
+ API_URL?: string | undefined;
22
+ REVEALUI_PUBLIC_SERVER_URL?: string | undefined;
23
+ NEXT_PUBLIC_SERVER_URL?: string | undefined;
24
+ VERCEL_PROJECT_PRODUCTION_URL?: string | undefined;
25
+ VERCEL_URL?: string | undefined;
26
+ SESSION_COOKIE_DOMAIN?: string | undefined;
27
+ PASSKEY_RP_ID?: string | undefined;
28
+ PASSKEY_ORIGIN?: string | undefined;
29
+ CORS_ORIGIN?: string | undefined;
30
+ }
31
+ /**
32
+ * Resolve the Sentry environment label.
33
+ * Staging hosts report `staging`, or `preview` when that explicit label is set.
34
+ * They do not report `production`.
35
+ */
36
+ export declare function resolveSentryEnvironment(env: SentryEnvironmentSource): SentryEnvironmentName;
37
+ //# sourceMappingURL=sentry-environment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sentry-environment.d.ts","sourceRoot":"","sources":["../src/sentry-environment.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,MAAM,MAAM,qBAAqB,GAAG,YAAY,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,GAAG,MAAM,CAAC;AAElG,oFAAoF;AACpF,eAAO,MAAM,sBAAsB,uBAAuB,CAAC;AAE3D,MAAM,WAAW,uBAAuB;IACtC,kBAAkB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,mBAAmB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,8BAA8B,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpD,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,mBAAmB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,0BAA0B,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChD,sBAAsB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,6BAA6B,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnD,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,qBAAqB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3C,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC;AAyED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,uBAAuB,GAAG,qBAAqB,CAoD5F"}
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Sentry `environment` for a deploy.
3
+ *
4
+ * `NODE_ENV=production` is set on staging as well as production, so it is not
5
+ * a safe Sentry label. Prefer `SENTRY_ENVIRONMENT` (then `REVEALUI_DEPLOY_ENV`,
6
+ * then `NEXT_PUBLIC_SENTRY_ENVIRONMENT`). When those are unset, derive the
7
+ * label from public hosts. A `*.staging.revealui.com` host is never reported
8
+ * as production. Vercel preview stays `preview`.
9
+ */
10
+ import { isStagingRevealHost, normalizeRequestHost } from './session-cookie-domain.js';
11
+ /** Non-secret label. Set `SENTRY_ENVIRONMENT=staging` on staging Vercel and Fly. */
12
+ export const SENTRY_ENVIRONMENT_VAR = 'SENTRY_ENVIRONMENT';
13
+ const HOST_KEYS = [
14
+ 'REVEALUI_API_URL',
15
+ 'NEXT_PUBLIC_API_URL',
16
+ 'API_URL',
17
+ 'REVEALUI_PUBLIC_SERVER_URL',
18
+ 'NEXT_PUBLIC_SERVER_URL',
19
+ 'VERCEL_PROJECT_PRODUCTION_URL',
20
+ 'VERCEL_URL',
21
+ 'SESSION_COOKIE_DOMAIN',
22
+ 'PASSKEY_RP_ID',
23
+ 'PASSKEY_ORIGIN',
24
+ 'CORS_ORIGIN',
25
+ ];
26
+ const PRODUCTION_HOSTS = new Set([
27
+ 'revealui.com',
28
+ 'www.revealui.com',
29
+ 'api.revealui.com',
30
+ 'admin.revealui.com',
31
+ 'docs.revealui.com',
32
+ ]);
33
+ function lockedEnvironment(raw) {
34
+ if (raw == null)
35
+ return null;
36
+ const value = raw.trim().toLowerCase();
37
+ switch (value) {
38
+ case 'production':
39
+ case 'staging':
40
+ case 'preview':
41
+ case 'development':
42
+ case 'test':
43
+ return value;
44
+ default:
45
+ return null;
46
+ }
47
+ }
48
+ function firstLocked(...values) {
49
+ for (const value of values) {
50
+ const locked = lockedEnvironment(value);
51
+ if (locked)
52
+ return locked;
53
+ }
54
+ return null;
55
+ }
56
+ function splitComma(raw) {
57
+ const parts = [];
58
+ let start = 0;
59
+ for (let index = 0; index <= raw.length; index += 1) {
60
+ if (index === raw.length || raw.charCodeAt(index) === 44) {
61
+ const part = raw.slice(start, index).trim();
62
+ if (part.length > 0)
63
+ parts.push(part);
64
+ start = index + 1;
65
+ }
66
+ }
67
+ return parts;
68
+ }
69
+ function collectHosts(env) {
70
+ const hosts = [];
71
+ for (const key of HOST_KEYS) {
72
+ const raw = env[key];
73
+ if (typeof raw !== 'string' || raw.trim().length === 0)
74
+ continue;
75
+ for (const part of splitComma(raw)) {
76
+ const host = normalizeRequestHost(part);
77
+ if (host)
78
+ hosts.push(host);
79
+ }
80
+ }
81
+ return hosts;
82
+ }
83
+ /**
84
+ * Resolve the Sentry environment label.
85
+ * Staging hosts report `staging`, or `preview` when that explicit label is set.
86
+ * They do not report `production`.
87
+ */
88
+ export function resolveSentryEnvironment(env) {
89
+ const hosts = collectHosts(env);
90
+ let stagingHost = false;
91
+ let productionHost = false;
92
+ for (const host of hosts) {
93
+ if (isStagingRevealHost(host))
94
+ stagingHost = true;
95
+ if (PRODUCTION_HOSTS.has(host))
96
+ productionHost = true;
97
+ }
98
+ const explicit = firstLocked(env.SENTRY_ENVIRONMENT, env.REVEALUI_DEPLOY_ENV, env.NEXT_PUBLIC_SENTRY_ENVIRONMENT);
99
+ if (stagingHost) {
100
+ if (explicit === 'staging' ||
101
+ explicit === 'preview' ||
102
+ explicit === 'development' ||
103
+ explicit === 'test') {
104
+ return explicit;
105
+ }
106
+ return 'staging';
107
+ }
108
+ if (explicit)
109
+ return explicit;
110
+ if (productionHost)
111
+ return 'production';
112
+ const vercel = lockedEnvironment(env.VERCEL_ENV);
113
+ if (vercel === 'preview' ||
114
+ vercel === 'development' ||
115
+ vercel === 'test' ||
116
+ vercel === 'staging') {
117
+ return vercel;
118
+ }
119
+ const nodeEnv = lockedEnvironment(env.NODE_ENV);
120
+ if (nodeEnv === 'development' ||
121
+ nodeEnv === 'test' ||
122
+ nodeEnv === 'staging' ||
123
+ nodeEnv === 'preview') {
124
+ return nodeEnv;
125
+ }
126
+ if (nodeEnv === 'production' || vercel === 'production')
127
+ return 'production';
128
+ return 'development';
129
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@revealui/core",
3
- "version": "0.15.0",
3
+ "version": "0.15.4",
4
4
  "description": "RevealUI's runtime engine — admin UI, REST API, auth, rich text, plugin system, and access control. The heart of the open-source platform.",
5
5
  "license": "MIT",
6
6
  "dependencies": {
@@ -23,10 +23,10 @@
23
23
  "pg": "^8.22.0",
24
24
  "yjs": "^13.6.31",
25
25
  "zod": "^4.4.3",
26
- "@revealui/contracts": "0.12.0",
27
- "@revealui/presentation": "0.14.8",
26
+ "@revealui/contracts": "0.13.2",
27
+ "@revealui/presentation": "0.15.2",
28
28
  "@revealui/resilience": "0.2.4",
29
- "@revealui/security": "0.8.2",
29
+ "@revealui/security": "0.10.1",
30
30
  "@revealui/utils": "0.3.6"
31
31
  },
32
32
  "devDependencies": {
@@ -71,6 +71,11 @@
71
71
  "import": "./dist/session-cookie-domain.js",
72
72
  "default": "./dist/session-cookie-domain.js"
73
73
  },
74
+ "./sentry-environment": {
75
+ "types": "./dist/sentry-environment.d.ts",
76
+ "import": "./dist/sentry-environment.js",
77
+ "default": "./dist/sentry-environment.js"
78
+ },
74
79
  "./margin-governor": {
75
80
  "types": "./dist/margin-governor.d.ts",
76
81
  "import": "./dist/margin-governor.js",