@logto/schemas 1.43.0 → 1.45.0

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.
Files changed (106) hide show
  1. package/alterations/1.44.0-1788946735-add-saml-authn-request-config.ts +18 -0
  2. package/alterations/1.44.0-1789000000-extend-user-id-length.ts +61 -0
  3. package/alterations/1.45.0-1791531333-add-tenant-viewer-role.ts +38 -0
  4. package/alterations-js/1.44.0-1788946735-add-saml-authn-request-config.js +14 -0
  5. package/alterations-js/1.44.0-1789000000-extend-user-id-length.js +47 -0
  6. package/alterations-js/1.45.0-1791531333-add-tenant-viewer-role.js +32 -0
  7. package/lib/consts/experience.d.ts +3 -0
  8. package/lib/consts/experience.js +3 -0
  9. package/lib/consts/index.d.ts +1 -0
  10. package/lib/consts/index.js +1 -0
  11. package/lib/consts/license.d.ts +222 -0
  12. package/lib/consts/license.js +81 -0
  13. package/lib/consts/license.test.d.ts +1 -0
  14. package/lib/consts/license.test.js +96 -0
  15. package/lib/consts/oidc.d.ts +15 -1
  16. package/lib/consts/oidc.js +11 -0
  17. package/lib/consts/oidc.test.d.ts +1 -0
  18. package/lib/consts/oidc.test.js +15 -0
  19. package/lib/consts/subscriptions.d.ts +13 -1
  20. package/lib/consts/subscriptions.js +12 -0
  21. package/lib/db-entries/aggregated-daily-active-user.js +2 -2
  22. package/lib/db-entries/application-access-control-user-relation.js +2 -2
  23. package/lib/db-entries/application-user-consent-organization.js +2 -2
  24. package/lib/db-entries/cimd-grant-organization.js +2 -2
  25. package/lib/db-entries/daily-active-user.js +2 -2
  26. package/lib/db-entries/oidc-session-extension.js +2 -2
  27. package/lib/db-entries/organization-invitation.js +4 -4
  28. package/lib/db-entries/organization-role-user-relation.js +2 -2
  29. package/lib/db-entries/organization-user-relation.js +2 -2
  30. package/lib/db-entries/personal-access-token.js +2 -2
  31. package/lib/db-entries/saml-application-config.d.ts +4 -2
  32. package/lib/db-entries/saml-application-config.js +5 -1
  33. package/lib/db-entries/secret.js +2 -2
  34. package/lib/db-entries/subject-token.js +2 -2
  35. package/lib/db-entries/trusted-device.js +2 -2
  36. package/lib/db-entries/user-geo-location.js +2 -2
  37. package/lib/db-entries/user-sign-in-country.js +2 -2
  38. package/lib/db-entries/user-sso-identity.js +2 -2
  39. package/lib/db-entries/user.js +2 -2
  40. package/lib/db-entries/users-role.js +2 -2
  41. package/lib/db-entries/verification-record.js +2 -2
  42. package/lib/db-entries/verification-status.js +2 -2
  43. package/lib/foundations/jsonb-types/captcha.d.ts +47 -1
  44. package/lib/foundations/jsonb-types/captcha.js +20 -0
  45. package/lib/foundations/jsonb-types/hooks.d.ts +0 -3
  46. package/lib/foundations/jsonb-types/hooks.js +2 -6
  47. package/lib/foundations/jsonb-types/saml-application-configs.d.ts +23 -0
  48. package/lib/foundations/jsonb-types/saml-application-configs.js +16 -0
  49. package/lib/foundations/jsonb-types/saml-application-configs.test.js +24 -1
  50. package/lib/types/authentication-context.d.ts +224 -0
  51. package/lib/types/authentication-context.js +238 -0
  52. package/lib/types/authentication-context.test.d.ts +1 -0
  53. package/lib/types/authentication-context.test.js +186 -0
  54. package/lib/types/cookie.d.ts +4 -0
  55. package/lib/types/cookie.js +6 -1
  56. package/lib/types/index.d.ts +1 -0
  57. package/lib/types/index.js +1 -0
  58. package/lib/types/interactions.d.ts +161 -0
  59. package/lib/types/interactions.js +23 -0
  60. package/lib/types/log/interaction.d.ts +15 -2
  61. package/lib/types/log/interaction.js +5 -0
  62. package/lib/types/logto-config/index.d.ts +47 -0
  63. package/lib/types/logto-config/index.js +1 -0
  64. package/lib/types/logto-config/jwt-customizer.d.ts +92 -0
  65. package/lib/types/saml-application.d.ts +55 -1
  66. package/lib/types/saml-application.js +7 -1
  67. package/lib/types/sign-in-experience.d.ts +11 -7
  68. package/lib/types/sign-in-experience.js +2 -2
  69. package/lib/types/ssr.d.ts +2 -0
  70. package/lib/types/system.d.ts +57 -5
  71. package/lib/types/system.js +36 -0
  72. package/lib/types/system.test.d.ts +1 -0
  73. package/lib/types/system.test.js +26 -0
  74. package/lib/types/tenant-organization.d.ts +4 -2
  75. package/lib/types/tenant-organization.js +6 -2
  76. package/lib/types/tenant-organization.test.d.ts +1 -0
  77. package/lib/types/tenant-organization.test.js +16 -0
  78. package/lib/types/user-sessions.d.ts +156 -0
  79. package/lib/types/verification-records/code-verification.d.ts +17 -0
  80. package/lib/types/verification-records/code-verification.js +1 -0
  81. package/lib/utils/management-api.d.ts +5 -0
  82. package/lib/utils/management-api.js +7 -1
  83. package/lib/utils/oidc-private-key.test.js +44 -0
  84. package/package.json +7 -7
  85. package/tables/aggregated_daily_active_users.sql +1 -1
  86. package/tables/application_access_control_user_relations.sql +1 -1
  87. package/tables/application_user_consent_organizations.sql +1 -1
  88. package/tables/cimd_grant_organizations.sql +1 -1
  89. package/tables/daily_active_users.sql +1 -1
  90. package/tables/oidc_session_extensions.sql +1 -1
  91. package/tables/organization_invitations.sql +2 -2
  92. package/tables/organization_role_user_relations.sql +1 -1
  93. package/tables/organization_user_relations.sql +1 -1
  94. package/tables/personal_access_tokens.sql +1 -1
  95. package/tables/saml_application_configs.sql +1 -0
  96. package/tables/secrets.sql +1 -1
  97. package/tables/subject_tokens.sql +1 -1
  98. package/tables/trusted_devices.sql +1 -1
  99. package/tables/user_geo_locations.sql +1 -1
  100. package/tables/user_sign_in_countries.sql +1 -1
  101. package/tables/user_sso_identities.sql +1 -1
  102. package/tables/users.sql +1 -1
  103. package/tables/users_roles.sql +1 -1
  104. package/tables/verification_records.sql +1 -1
  105. package/tables/verification_statuses.sql +1 -1
  106. package/LICENSE +0 -373
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import { NameIdFormat } from '../foundations/index.js';
2
+ import { NameIdFormat, type SamlAuthnRequestConfig } from '../foundations/index.js';
3
3
  export declare const samlApplicationCreateGuard: z.ZodObject<{
4
4
  name: z.ZodType<string, z.ZodTypeDef, string>;
5
5
  customData: z.ZodOptional<z.ZodOptional<z.ZodType<import("@withtyped/server/lib/types.js").JsonObject, z.ZodTypeDef, import("@withtyped/server/lib/types.js").JsonObject>>>;
@@ -16,6 +16,15 @@ export declare const samlApplicationCreateGuard: z.ZodObject<{
16
16
  encryptThenSign?: boolean | undefined;
17
17
  certificate?: string | undefined;
18
18
  } | null>>;
19
+ authnRequestConfig: z.ZodOptional<z.ZodType<{
20
+ forceAuthn?: boolean | undefined;
21
+ requireSignedAuthnRequests?: boolean | undefined;
22
+ signingCertificate?: string | undefined;
23
+ } | null, z.ZodTypeDef, {
24
+ forceAuthn?: boolean | undefined;
25
+ requireSignedAuthnRequests?: boolean | undefined;
26
+ signingCertificate?: string | undefined;
27
+ } | null>>;
19
28
  } & {
20
29
  nameIdFormat: z.ZodDefault<z.ZodOptional<z.ZodNativeEnum<typeof NameIdFormat>>>;
21
30
  }, "strip", z.ZodTypeAny, {
@@ -31,6 +40,11 @@ export declare const samlApplicationCreateGuard: z.ZodObject<{
31
40
  encryptThenSign?: boolean | undefined;
32
41
  certificate?: string | undefined;
33
42
  } | null | undefined;
43
+ authnRequestConfig?: {
44
+ forceAuthn?: boolean | undefined;
45
+ requireSignedAuthnRequests?: boolean | undefined;
46
+ signingCertificate?: string | undefined;
47
+ } | null | undefined;
34
48
  }, {
35
49
  name: string;
36
50
  customData?: import("@withtyped/server/lib/types.js").JsonObject;
@@ -43,6 +57,11 @@ export declare const samlApplicationCreateGuard: z.ZodObject<{
43
57
  encryptThenSign?: boolean | undefined;
44
58
  certificate?: string | undefined;
45
59
  } | null | undefined;
60
+ authnRequestConfig?: {
61
+ forceAuthn?: boolean | undefined;
62
+ requireSignedAuthnRequests?: boolean | undefined;
63
+ signingCertificate?: string | undefined;
64
+ } | null | undefined;
46
65
  nameIdFormat?: NameIdFormat | undefined;
47
66
  }>;
48
67
  export type CreateSamlApplication = z.input<typeof samlApplicationCreateGuard>;
@@ -63,6 +82,15 @@ export declare const samlApplicationPatchGuard: z.ZodObject<{
63
82
  encryptThenSign?: boolean | undefined;
64
83
  certificate?: string | undefined;
65
84
  } | null>>;
85
+ authnRequestConfig: z.ZodOptional<z.ZodType<{
86
+ forceAuthn?: boolean | undefined;
87
+ requireSignedAuthnRequests?: boolean | undefined;
88
+ signingCertificate?: string | undefined;
89
+ } | null, z.ZodTypeDef, {
90
+ forceAuthn?: boolean | undefined;
91
+ requireSignedAuthnRequests?: boolean | undefined;
92
+ signingCertificate?: string | undefined;
93
+ } | null>>;
66
94
  } & {
67
95
  nameIdFormat: z.ZodOptional<z.ZodNativeEnum<typeof NameIdFormat>>;
68
96
  }, "strip", z.ZodTypeAny, {
@@ -78,6 +106,11 @@ export declare const samlApplicationPatchGuard: z.ZodObject<{
78
106
  encryptThenSign?: boolean | undefined;
79
107
  certificate?: string | undefined;
80
108
  } | null | undefined;
109
+ authnRequestConfig?: {
110
+ forceAuthn?: boolean | undefined;
111
+ requireSignedAuthnRequests?: boolean | undefined;
112
+ signingCertificate?: string | undefined;
113
+ } | null | undefined;
81
114
  nameIdFormat?: NameIdFormat | undefined;
82
115
  }, {
83
116
  name?: string | undefined;
@@ -92,6 +125,11 @@ export declare const samlApplicationPatchGuard: z.ZodObject<{
92
125
  encryptThenSign?: boolean | undefined;
93
126
  certificate?: string | undefined;
94
127
  } | null | undefined;
128
+ authnRequestConfig?: {
129
+ forceAuthn?: boolean | undefined;
130
+ requireSignedAuthnRequests?: boolean | undefined;
131
+ signingCertificate?: string | undefined;
132
+ } | null | undefined;
95
133
  nameIdFormat?: NameIdFormat | undefined;
96
134
  }>;
97
135
  export type PatchSamlApplication = z.infer<typeof samlApplicationPatchGuard>;
@@ -117,6 +155,15 @@ export declare const samlApplicationResponseGuard: z.ZodObject<{
117
155
  encryptThenSign?: boolean | undefined;
118
156
  certificate?: string | undefined;
119
157
  } | null>;
158
+ authnRequestConfig: z.ZodType<{
159
+ forceAuthn?: boolean | undefined;
160
+ requireSignedAuthnRequests?: boolean | undefined;
161
+ signingCertificate?: string | undefined;
162
+ } | null, z.ZodTypeDef, {
163
+ forceAuthn?: boolean | undefined;
164
+ requireSignedAuthnRequests?: boolean | undefined;
165
+ signingCertificate?: string | undefined;
166
+ } | null>;
120
167
  } & {
121
168
  nameIdFormat: z.ZodNativeEnum<typeof NameIdFormat>;
122
169
  }, "strip", z.ZodTypeAny, {
@@ -133,6 +180,7 @@ export declare const samlApplicationResponseGuard: z.ZodObject<{
133
180
  entityId: string | null;
134
181
  acsUrl: import("../foundations/index.js").SamlAcsUrl | null;
135
182
  encryption: import("../foundations/index.js").SamlEncryption | null;
183
+ authnRequestConfig: SamlAuthnRequestConfig | null;
136
184
  nameIdFormat: NameIdFormat;
137
185
  }, {
138
186
  type: import("../index.js").ApplicationType;
@@ -148,6 +196,7 @@ export declare const samlApplicationResponseGuard: z.ZodObject<{
148
196
  entityId: string | null;
149
197
  acsUrl: import("../foundations/index.js").SamlAcsUrl | null;
150
198
  encryption: import("../foundations/index.js").SamlEncryption | null;
199
+ authnRequestConfig: SamlAuthnRequestConfig | null;
151
200
  nameIdFormat: NameIdFormat;
152
201
  }>;
153
202
  export type SamlApplicationResponse = z.infer<typeof samlApplicationResponseGuard>;
@@ -238,4 +287,9 @@ export declare const samlApplicationSecretResponseGuard: z.ZodObject<Omit<{
238
287
  };
239
288
  }>;
240
289
  export type SamlApplicationSecretResponse = z.infer<typeof samlApplicationSecretResponseGuard>;
290
+ /**
291
+ * Whether a SAML application forces fresh authentication. Forcing is the default; only an explicit
292
+ * `forceAuthn: false` lets the application reuse an existing session.
293
+ */
294
+ export declare const isSamlForceAuthnEnabled: (config?: SamlAuthnRequestConfig | null) => boolean;
241
295
  export {};
@@ -2,13 +2,14 @@ import { z } from 'zod';
2
2
  import { Applications } from '../db-entries/application.js';
3
3
  import { SamlApplicationConfigs } from '../db-entries/saml-application-config.js';
4
4
  import { SamlApplicationSecrets } from '../db-entries/saml-application-secret.js';
5
- import { nameIdFormatGuard, NameIdFormat } from '../foundations/index.js';
5
+ import { nameIdFormatGuard, NameIdFormat, } from '../foundations/index.js';
6
6
  import { applicationCreateGuard, applicationPatchGuard } from './application.js';
7
7
  const samlAppConfigGuard = SamlApplicationConfigs.guard.pick({
8
8
  attributeMapping: true,
9
9
  entityId: true,
10
10
  acsUrl: true,
11
11
  encryption: true,
12
+ authnRequestConfig: true,
12
13
  nameIdFormat: true,
13
14
  });
14
15
  export const samlApplicationCreateGuard = applicationCreateGuard
@@ -61,3 +62,8 @@ export const samlApplicationSecretResponseGuard = SamlApplicationSecrets.guard
61
62
  .extend({
62
63
  fingerprints: certificateFingerprintsGuard,
63
64
  });
65
+ /**
66
+ * Whether a SAML application forces fresh authentication. Forcing is the default; only an explicit
67
+ * `forceAuthn: false` lets the application reuse an existing session.
68
+ */
69
+ export const isSamlForceAuthnEnabled = (config) => config?.forceAuthn !== false;
@@ -12,9 +12,7 @@ type ForgotPassword = {
12
12
  * the need to load the full connector metadata that is not needed for rendering.
13
13
  */
14
14
  export type ExperienceSocialConnector = Omit<ConnectorMetadata, 'description' | 'configTemplate' | 'formItems' | 'readme' | 'customData'>;
15
- export type FullSignInExperience = Omit<SignInExperience, 'emailBlocklistPolicy' | 'forgotPasswordMethods' | 'trustedDevice'> & {
16
- /** Available while the MFA trusted-device feature is under development. */
17
- trustedDevice?: SignInExperience['trustedDevice'];
15
+ export type FullSignInExperience = Omit<SignInExperience, 'emailBlocklistPolicy' | 'forgotPasswordMethods'> & {
18
16
  socialConnectors: ExperienceSocialConnector[];
19
17
  ssoConnectors: SsoConnectorMetadata[];
20
18
  forgotPassword: ForgotPassword;
@@ -34,6 +32,8 @@ export type FullSignInExperience = Omit<SignInExperience, 'emailBlocklistPolicy'
34
32
  captchaConfig?: {
35
33
  type: CaptchaType;
36
34
  siteKey: string;
35
+ /** The Cap Standalone instance URL, only available for {@link CaptchaType.Cap}. */
36
+ endpoint?: string;
37
37
  domain?: string;
38
38
  mode?: RecaptchaEnterpriseMode;
39
39
  };
@@ -163,8 +163,7 @@ export declare const fullSignInExperienceGuard: z.ZodObject<Omit<{
163
163
  }[] | null>;
164
164
  passwordExpiration: z.ZodType<import("../foundations/jsonb-types/sign-in-experience.js").PasswordExpirationPolicy, z.ZodTypeDef, import("../foundations/jsonb-types/sign-in-experience.js").PasswordExpirationPolicy>;
165
165
  usernamePolicy: z.ZodType<import("../foundations/jsonb-types/index.js").UsernamePolicy, z.ZodTypeDef, import("../foundations/jsonb-types/index.js").UsernamePolicy>;
166
- }, "trustedDevice" | "emailBlocklistPolicy" | "forgotPasswordMethods"> & {
167
- trustedDevice: z.ZodOptional<z.ZodType<import("../foundations/jsonb-types/sign-in-experience.js").TrustedDevicePolicy, z.ZodTypeDef, import("../foundations/jsonb-types/sign-in-experience.js").TrustedDevicePolicy>>;
166
+ }, "emailBlocklistPolicy" | "forgotPasswordMethods"> & {
168
167
  socialConnectors: z.ZodArray<z.ZodObject<Omit<{
169
168
  id: z.ZodString;
170
169
  target: z.ZodString;
@@ -703,6 +702,7 @@ export declare const fullSignInExperienceGuard: z.ZodObject<Omit<{
703
702
  captchaConfig: z.ZodOptional<z.ZodObject<{
704
703
  type: z.ZodNativeEnum<typeof CaptchaType>;
705
704
  siteKey: z.ZodString;
705
+ endpoint: z.ZodOptional<z.ZodString>;
706
706
  domain: z.ZodOptional<z.ZodString>;
707
707
  mode: z.ZodOptional<z.ZodNativeEnum<typeof RecaptchaEnterpriseMode>>;
708
708
  }, "strip", z.ZodTypeAny, {
@@ -710,11 +710,13 @@ export declare const fullSignInExperienceGuard: z.ZodObject<Omit<{
710
710
  siteKey: string;
711
711
  domain?: string | undefined;
712
712
  mode?: RecaptchaEnterpriseMode | undefined;
713
+ endpoint?: string | undefined;
713
714
  }, {
714
715
  type: CaptchaType;
715
716
  siteKey: string;
716
717
  domain?: string | undefined;
717
718
  mode?: RecaptchaEnterpriseMode | undefined;
719
+ endpoint?: string | undefined;
718
720
  }>>;
719
721
  customProfileFields: z.ZodArray<import("../index.js").Guard<CustomProfileField>, "many">;
720
722
  customProfileFieldCatalog: z.ZodOptional<z.ZodArray<import("../index.js").Guard<CustomProfileField>, "many">>;
@@ -722,6 +724,7 @@ export declare const fullSignInExperienceGuard: z.ZodObject<Omit<{
722
724
  id: string;
723
725
  tenantId: string;
724
726
  mfa: import("../foundations/jsonb-types/sign-in-experience.js").Mfa;
727
+ trustedDevice: import("../foundations/jsonb-types/sign-in-experience.js").TrustedDevicePolicy;
725
728
  customCss: string | null;
726
729
  color: import("../foundations/jsonb-types/sign-in-experience.js").Color;
727
730
  branding: import("../foundations/jsonb-types/sign-in-experience.js").Branding;
@@ -906,7 +909,6 @@ export declare const fullSignInExperienceGuard: z.ZodObject<Omit<{
906
909
  };
907
910
  isDevelopmentTenant: boolean;
908
911
  customProfileFields: CustomProfileField[];
909
- trustedDevice?: import("../foundations/jsonb-types/sign-in-experience.js").TrustedDevicePolicy | undefined;
910
912
  googleOneTap?: {
911
913
  clientId: string;
912
914
  connectorId: string;
@@ -920,12 +922,14 @@ export declare const fullSignInExperienceGuard: z.ZodObject<Omit<{
920
922
  siteKey: string;
921
923
  domain?: string | undefined;
922
924
  mode?: RecaptchaEnterpriseMode | undefined;
925
+ endpoint?: string | undefined;
923
926
  } | undefined;
924
927
  customProfileFieldCatalog?: CustomProfileField[] | undefined;
925
928
  }, {
926
929
  id: string;
927
930
  tenantId: string;
928
931
  mfa: import("../foundations/jsonb-types/sign-in-experience.js").Mfa;
932
+ trustedDevice: import("../foundations/jsonb-types/sign-in-experience.js").TrustedDevicePolicy;
929
933
  customCss: string | null;
930
934
  color: import("../foundations/jsonb-types/sign-in-experience.js").Color;
931
935
  branding: import("../foundations/jsonb-types/sign-in-experience.js").Branding;
@@ -1110,7 +1114,6 @@ export declare const fullSignInExperienceGuard: z.ZodObject<Omit<{
1110
1114
  };
1111
1115
  isDevelopmentTenant: boolean;
1112
1116
  customProfileFields: CustomProfileField[];
1113
- trustedDevice?: import("../foundations/jsonb-types/sign-in-experience.js").TrustedDevicePolicy | undefined;
1114
1117
  googleOneTap?: {
1115
1118
  clientId: string;
1116
1119
  connectorId: string;
@@ -1124,6 +1127,7 @@ export declare const fullSignInExperienceGuard: z.ZodObject<Omit<{
1124
1127
  siteKey: string;
1125
1128
  domain?: string | undefined;
1126
1129
  mode?: RecaptchaEnterpriseMode | undefined;
1130
+ endpoint?: string | undefined;
1127
1131
  } | undefined;
1128
1132
  customProfileFieldCatalog?: CustomProfileField[] | undefined;
1129
1133
  }>;
@@ -4,9 +4,8 @@ import { CustomProfileFields, SignInExperiences, } from '../db-entries/index.js'
4
4
  import { CaptchaType, RecaptchaEnterpriseMode } from '../foundations/jsonb-types/index.js';
5
5
  import { ssoConnectorMetadataGuard } from './sso-connector.js';
6
6
  export const fullSignInExperienceGuard = SignInExperiences.guard
7
- .omit({ emailBlocklistPolicy: true, forgotPasswordMethods: true, trustedDevice: true })
7
+ .omit({ emailBlocklistPolicy: true, forgotPasswordMethods: true })
8
8
  .extend({
9
- trustedDevice: SignInExperiences.guard.shape.trustedDevice.optional(),
10
9
  socialConnectors: connectorMetadataGuard
11
10
  .omit({
12
11
  description: true,
@@ -26,6 +25,7 @@ export const fullSignInExperienceGuard = SignInExperiences.guard
26
25
  .object({
27
26
  type: z.nativeEnum(CaptchaType),
28
27
  siteKey: z.string(),
28
+ endpoint: z.string().optional(),
29
29
  domain: z.string().optional(),
30
30
  mode: z.nativeEnum(RecaptchaEnterpriseMode).optional(),
31
31
  })
@@ -1,6 +1,7 @@
1
1
  import { type LocalePhrase } from '@logto/phrases-experience';
2
2
  import { type SignInExperience } from '../db-entries/index.js';
3
3
  import { type FullSignInExperience } from './sign-in-experience.js';
4
+ import { type Theme } from './theme.js';
4
5
  /**
5
6
  * The server-side rendering data type for **experience**.
6
7
  */
@@ -9,6 +10,7 @@ export type SsrData = {
9
10
  appId?: string;
10
11
  organizationId?: string;
11
12
  uiLocales?: string;
13
+ theme?: Theme;
12
14
  data: FullSignInExperience;
13
15
  };
14
16
  phrases: {
@@ -48,8 +48,8 @@ export declare const storageProviderDataGuard: z.ZodDiscriminatedUnion<"provider
48
48
  accessKeyId: string;
49
49
  accessSecretKey: string;
50
50
  region?: string | undefined;
51
- publicUrl?: string | undefined;
52
51
  endpoint?: string | undefined;
52
+ publicUrl?: string | undefined;
53
53
  forcePathStyle?: boolean | undefined;
54
54
  }, {
55
55
  provider: StorageProvider.S3Storage;
@@ -57,8 +57,8 @@ export declare const storageProviderDataGuard: z.ZodDiscriminatedUnion<"provider
57
57
  accessKeyId: string;
58
58
  accessSecretKey: string;
59
59
  region?: string | undefined;
60
- publicUrl?: string | undefined;
61
60
  endpoint?: string | undefined;
61
+ publicUrl?: string | undefined;
62
62
  forcePathStyle?: boolean | undefined;
63
63
  }>, z.ZodObject<{
64
64
  publicUrl: z.ZodOptional<z.ZodString>;
@@ -292,8 +292,60 @@ export type CloudflareType = {
292
292
  export declare const cloudflareGuard: Readonly<{
293
293
  [key in CloudflareKey]: ZodType<CloudflareType[key]>;
294
294
  }>;
295
- export type SystemKey = AlterationStateKey | StorageProviderKey | DemoSocialKey | CloudflareKey | EmailServiceProviderKey;
296
- export type SystemType = AlterationStateType | StorageProviderType | DemoSocialType | CloudflareType | EmailServiceProviderType;
297
- export type SystemGuard = typeof alterationStateGuard & typeof storageProviderGuard & typeof demoSocialGuard & typeof cloudflareGuard & typeof emailServiceProviderGuard;
295
+ /**
296
+ * The license key installed on a self-hosted instance, stored verbatim so it can be re-verified on
297
+ * every read and re-signed on refresh. The `systems` table is global, so one deployment installs
298
+ * one license no matter how many instances share the database.
299
+ */
300
+ export declare const installedLicenseGuard: z.ZodObject<{
301
+ /** The raw ES256-signed license key JWT, exactly as it was installed. */
302
+ jwt: z.ZodString;
303
+ /** When the key was installed, as an ISO 8601 timestamp. */
304
+ installedAt: z.ZodString;
305
+ }, "strip", z.ZodTypeAny, {
306
+ jwt: string;
307
+ installedAt: string;
308
+ }, {
309
+ jwt: string;
310
+ installedAt: string;
311
+ }>;
312
+ export type InstalledLicense = z.infer<typeof installedLicenseGuard>;
313
+ export declare enum LicenseKey {
314
+ License = "license",
315
+ LicenseRefreshState = "licenseRefreshState",
316
+ LicenseDeploymentId = "licenseDeploymentId"
317
+ }
318
+ export type LicenseType = {
319
+ [LicenseKey.License]: InstalledLicense;
320
+ [LicenseKey.LicenseRefreshState]: LicenseRefreshState;
321
+ [LicenseKey.LicenseDeploymentId]: LicenseDeploymentId;
322
+ };
323
+ /** The state shared by all instances while they lazily refresh the installed license. */
324
+ export declare const licenseRefreshStateGuard: z.ZodObject<{
325
+ /** The last successful refresh, or the `iat` of the installed key. */
326
+ lastRefreshedAt: z.ZodString;
327
+ /** The last refresh attempt. Absent before the first read after installation. */
328
+ lastAttemptAt: z.ZodOptional<z.ZodString>;
329
+ /** The refusal reason returned by the license service, if the last attempt was refused. */
330
+ refusalReason: z.ZodOptional<z.ZodString>;
331
+ }, "strip", z.ZodTypeAny, {
332
+ lastRefreshedAt: string;
333
+ lastAttemptAt?: string | undefined;
334
+ refusalReason?: string | undefined;
335
+ }, {
336
+ lastRefreshedAt: string;
337
+ lastAttemptAt?: string | undefined;
338
+ refusalReason?: string | undefined;
339
+ }>;
340
+ export type LicenseRefreshState = z.infer<typeof licenseRefreshStateGuard>;
341
+ /** The stable identifier shared by every Core instance using this `systems` table. */
342
+ export declare const licenseDeploymentIdGuard: z.ZodString;
343
+ export type LicenseDeploymentId = z.infer<typeof licenseDeploymentIdGuard>;
344
+ export declare const licenseGuard: Readonly<{
345
+ [key in LicenseKey]: ZodType<LicenseType[key]>;
346
+ }>;
347
+ export type SystemKey = AlterationStateKey | StorageProviderKey | DemoSocialKey | CloudflareKey | EmailServiceProviderKey | LicenseKey;
348
+ export type SystemType = AlterationStateType | StorageProviderType | DemoSocialType | CloudflareType | EmailServiceProviderType | LicenseType;
349
+ export type SystemGuard = typeof alterationStateGuard & typeof storageProviderGuard & typeof demoSocialGuard & typeof cloudflareGuard & typeof emailServiceProviderGuard & typeof licenseGuard;
298
350
  export declare const systemKeys: readonly SystemKey[];
299
351
  export declare const systemGuards: SystemGuard;
@@ -155,12 +155,47 @@ export const cloudflareGuard = Object.freeze({
155
155
  [CloudflareKey.ProtectedAppHostnameProvider]: hostnameProviderDataGuard,
156
156
  [CloudflareKey.CustomJwtWorkerConfig]: customJwtWorkerConfigGuard,
157
157
  });
158
+ // Self-hosted license
159
+ /**
160
+ * The license key installed on a self-hosted instance, stored verbatim so it can be re-verified on
161
+ * every read and re-signed on refresh. The `systems` table is global, so one deployment installs
162
+ * one license no matter how many instances share the database.
163
+ */
164
+ export const installedLicenseGuard = z.object({
165
+ /** The raw ES256-signed license key JWT, exactly as it was installed. */
166
+ jwt: z.string(),
167
+ /** When the key was installed, as an ISO 8601 timestamp. */
168
+ installedAt: z.string(),
169
+ });
170
+ export var LicenseKey;
171
+ (function (LicenseKey) {
172
+ LicenseKey["License"] = "license";
173
+ LicenseKey["LicenseRefreshState"] = "licenseRefreshState";
174
+ LicenseKey["LicenseDeploymentId"] = "licenseDeploymentId";
175
+ })(LicenseKey || (LicenseKey = {}));
176
+ /** The state shared by all instances while they lazily refresh the installed license. */
177
+ export const licenseRefreshStateGuard = z.object({
178
+ /** The last successful refresh, or the `iat` of the installed key. */
179
+ lastRefreshedAt: z.string(),
180
+ /** The last refresh attempt. Absent before the first read after installation. */
181
+ lastAttemptAt: z.string().optional(),
182
+ /** The refusal reason returned by the license service, if the last attempt was refused. */
183
+ refusalReason: z.string().optional(),
184
+ });
185
+ /** The stable identifier shared by every Core instance using this `systems` table. */
186
+ export const licenseDeploymentIdGuard = z.string().min(1);
187
+ export const licenseGuard = Object.freeze({
188
+ [LicenseKey.License]: installedLicenseGuard,
189
+ [LicenseKey.LicenseRefreshState]: licenseRefreshStateGuard,
190
+ [LicenseKey.LicenseDeploymentId]: licenseDeploymentIdGuard,
191
+ });
158
192
  export const systemKeys = Object.freeze([
159
193
  ...Object.values(AlterationStateKey),
160
194
  ...Object.values(StorageProviderKey),
161
195
  ...Object.values(DemoSocialKey),
162
196
  ...Object.values(CloudflareKey),
163
197
  ...Object.values(EmailServiceProviderKey),
198
+ ...Object.values(LicenseKey),
164
199
  ]);
165
200
  export const systemGuards = Object.freeze({
166
201
  ...alterationStateGuard,
@@ -168,4 +203,5 @@ export const systemGuards = Object.freeze({
168
203
  ...demoSocialGuard,
169
204
  ...cloudflareGuard,
170
205
  ...emailServiceProviderGuard,
206
+ ...licenseGuard,
171
207
  });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,26 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { LicenseKey, systemGuards, systemKeys } from './system.js';
3
+ describe('license system entries', () => {
4
+ it('registers the license key, so the CLI accepts it', () => {
5
+ expect(systemKeys).toContain(LicenseKey.License);
6
+ expect(systemKeys).toContain(LicenseKey.LicenseRefreshState);
7
+ expect(systemKeys).toContain(LicenseKey.LicenseDeploymentId);
8
+ });
9
+ it('parses an installed license row', () => {
10
+ const value = { jwt: 'header.payload.signature', installedAt: '2026-01-01T00:00:00.000Z' };
11
+ expect(systemGuards[LicenseKey.License].parse(value)).toEqual(value);
12
+ });
13
+ it('rejects an installed license row without a key', () => {
14
+ expect(systemGuards[LicenseKey.License].safeParse({ installedAt: '2026-01-01T00:00:00.000Z' })
15
+ .success).toBe(false);
16
+ });
17
+ it('parses refresh state and a deployment ID', () => {
18
+ const state = {
19
+ lastRefreshedAt: '2026-01-01T00:00:00.000Z',
20
+ lastAttemptAt: '2026-01-02T00:00:00.000Z',
21
+ refusalReason: 'canceled',
22
+ };
23
+ expect(systemGuards[LicenseKey.LicenseRefreshState].parse(state)).toEqual(state);
24
+ expect(systemGuards[LicenseKey.LicenseDeploymentId].parse('deployment_123')).toBe('deployment_123');
25
+ });
26
+ });
@@ -39,7 +39,7 @@ export declare const getTenantOrganizationCreateData: (tenantId: string) => Read
39
39
  export declare enum TenantScope {
40
40
  /** Read the tenant data. */
41
41
  ReadData = "read:data",
42
- /** Write the tenant data, including creating and updating the tenant. */
42
+ /** Write the tenant data. */
43
43
  WriteData = "write:data",
44
44
  /** Delete data of the tenant. */
45
45
  DeleteData = "delete:data",
@@ -83,7 +83,9 @@ export declare enum TenantRole {
83
83
  /** Admin of the tenant, who has all permissions. */
84
84
  Admin = "admin",
85
85
  /** Collaborator of the tenant, who has permissions to operate the tenant data, but not the tenant settings. */
86
- Collaborator = "collaborator"
86
+ Collaborator = "collaborator",
87
+ /** Viewer of the tenant, who can read the tenant data and members but cannot change anything. */
88
+ Viewer = "viewer"
87
89
  }
88
90
  /**
89
91
  * Given a tenant role, return the corresponding organization role data in the admin tenant.
@@ -50,7 +50,7 @@ export var TenantScope;
50
50
  (function (TenantScope) {
51
51
  /** Read the tenant data. */
52
52
  TenantScope["ReadData"] = "read:data";
53
- /** Write the tenant data, including creating and updating the tenant. */
53
+ /** Write the tenant data. */
54
54
  TenantScope["WriteData"] = "write:data";
55
55
  /** Delete data of the tenant. */
56
56
  TenantScope["DeleteData"] = "delete:data";
@@ -92,7 +92,7 @@ export const getTenantScope = (scope) => Object.freeze({
92
92
  });
93
93
  const tenantScopeDescriptions = Object.freeze({
94
94
  [TenantScope.ReadData]: 'Read the tenant data.',
95
- [TenantScope.WriteData]: 'Write the tenant data, including creating and updating the tenant.',
95
+ [TenantScope.WriteData]: 'Write the tenant data.',
96
96
  [TenantScope.DeleteData]: 'Delete data of the tenant.',
97
97
  [TenantScope.ReadMember]: 'Read members of the tenant.',
98
98
  [TenantScope.InviteMember]: 'Invite members to the tenant.',
@@ -112,10 +112,13 @@ export var TenantRole;
112
112
  TenantRole["Admin"] = "admin";
113
113
  /** Collaborator of the tenant, who has permissions to operate the tenant data, but not the tenant settings. */
114
114
  TenantRole["Collaborator"] = "collaborator";
115
+ /** Viewer of the tenant, who can read the tenant data and members but cannot change anything. */
116
+ TenantRole["Viewer"] = "viewer";
115
117
  })(TenantRole || (TenantRole = {}));
116
118
  const tenantRoleDescriptions = Object.freeze({
117
119
  [TenantRole.Admin]: 'Admin of the tenant, who has all permissions.',
118
120
  [TenantRole.Collaborator]: 'Collaborator of the tenant, who has permissions to operate the tenant data, but not the tenant settings.',
121
+ [TenantRole.Viewer]: 'Viewer of the tenant, who can read the tenant data and members but cannot change anything.',
119
122
  });
120
123
  /**
121
124
  * Given a tenant role, return the corresponding organization role data in the admin tenant.
@@ -155,4 +158,5 @@ export const tenantRoleScopes = Object.freeze({
155
158
  TenantScope.DeleteData,
156
159
  TenantScope.ReadMember,
157
160
  ],
161
+ [TenantRole.Viewer]: [TenantScope.ReadData, TenantScope.ReadMember],
158
162
  });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,16 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { TenantRole, TenantScope, tenantRoleScopes } from './tenant-organization.js';
3
+ describe('tenantRoleScopes', () => {
4
+ it('grants each tenant role its scopes', () => {
5
+ expect(tenantRoleScopes).toEqual({
6
+ [TenantRole.Admin]: Object.values(TenantScope),
7
+ [TenantRole.Collaborator]: [
8
+ TenantScope.ReadData,
9
+ TenantScope.WriteData,
10
+ TenantScope.DeleteData,
11
+ TenantScope.ReadMember,
12
+ ],
13
+ [TenantRole.Viewer]: [TenantScope.ReadData, TenantScope.ReadMember],
14
+ });
15
+ });
16
+ });