@maudecode/kanvil 0.1.2 → 0.2.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.
Files changed (55) hide show
  1. package/apps/server/dist/{auth-BscXb1NQ.mjs → auth-I1XEr0d4.mjs} +272 -7
  2. package/apps/server/dist/{config-Qxr_7oqZ.mjs → config-CbNqtJEj.mjs} +8 -2
  3. package/apps/server/dist/index.mjs +29 -190
  4. package/apps/server/dist/migrate.mjs +1 -1
  5. package/apps/server/dist/recover.mjs +21 -6
  6. package/apps/server/drizzle/0013_authentication_policy.sql +2 -0
  7. package/apps/server/drizzle/meta/0013_snapshot.json +4240 -0
  8. package/apps/server/drizzle/meta/_journal.json +7 -0
  9. package/apps/web/dist/assets/{CompositeItem-D9KvUNiq.js → CompositeItem-BWPJ5Mol.js} +1 -1
  10. package/apps/web/dist/assets/admin-settings-DZqkC6ju.js +1 -0
  11. package/apps/web/dist/assets/{date-picker-lhXxKIlN.js → date-picker-6H_iwP6t.js} +1 -1
  12. package/apps/web/dist/assets/{ellipsis-CN74O_EP.js → ellipsis-BT172Uvu.js} +1 -1
  13. package/apps/web/dist/assets/{forms-456UbfIC.js → forms-B4Vg3CPz.js} +23 -23
  14. package/apps/web/dist/assets/image-edit-trigger-DGmVtWLC.js +1 -0
  15. package/apps/web/dist/assets/index-C_a21taR.js +50 -0
  16. package/apps/web/dist/assets/index-DM-D_3Sv.css +2 -0
  17. package/apps/web/dist/assets/kbd-tTQGZxaK.js +1 -0
  18. package/apps/web/dist/assets/link-DoIP9KJ6.js +2 -0
  19. package/apps/web/dist/assets/{list-order-1UoOT5nR.js → list-order-Bzsrihyr.js} +1 -1
  20. package/apps/web/dist/assets/member-directory-BiCE8DRq.js +1 -0
  21. package/apps/web/dist/assets/popover-BhzZ0U1d.js +1 -0
  22. package/apps/web/dist/assets/project-label-provider-9L7Uts3_.js +1 -0
  23. package/apps/web/dist/assets/project-settings-Q7Q9Reec.js +1 -0
  24. package/apps/web/dist/assets/{rotate-ccw-BLeWpuu1.js → rotate-ccw-sjyLwLLy.js} +1 -1
  25. package/apps/web/dist/assets/settings-B6qD6q1J.js +1 -0
  26. package/apps/web/dist/assets/settings-layout-B-AwUIMu.js +1 -0
  27. package/apps/web/dist/assets/switch-D0nFHj6l.js +1 -0
  28. package/apps/web/dist/assets/{table-BQbZG4BK.js → table-CYZiwo9g.js} +1 -1
  29. package/apps/web/dist/assets/ticket-detail-B-3vbHSB.js +2 -0
  30. package/apps/web/dist/assets/ticket-editor-C3d8TZIT.js +1 -0
  31. package/apps/web/dist/assets/ticket-list-LtGGOnwN.js +5 -0
  32. package/apps/web/dist/assets/ticket-tags-2rdmkcJV.js +1 -0
  33. package/apps/web/dist/assets/{upload-5Wg5ou_Z.js → upload-DXPRzAaa.js} +1 -1
  34. package/apps/web/dist/assets/useNavigate-DFohfUD3.js +1 -0
  35. package/apps/web/dist/assets/workspace-settings-D3tCm1vH.js +1 -0
  36. package/apps/web/dist/index.html +8 -4
  37. package/docs/api.md +4 -0
  38. package/docs/releasing.md +4 -2
  39. package/docs/self-hosting.md +9 -1
  40. package/package.json +2 -1
  41. package/apps/web/dist/assets/admin-settings-B0u2-aAK.js +0 -1
  42. package/apps/web/dist/assets/camera-Cv5wKkKn.js +0 -1
  43. package/apps/web/dist/assets/index-CVzq9BMC.css +0 -2
  44. package/apps/web/dist/assets/index-Dt8wYIVA.js +0 -51
  45. package/apps/web/dist/assets/instance-BCNvOvYa.js +0 -1
  46. package/apps/web/dist/assets/member-directory-C81ugis6.js +0 -1
  47. package/apps/web/dist/assets/popover-BWk65ByY.js +0 -1
  48. package/apps/web/dist/assets/project-settings-DnpVfVf5.js +0 -1
  49. package/apps/web/dist/assets/settings-DUXuOgWn.js +0 -1
  50. package/apps/web/dist/assets/settings-layout-xR4hrfte.js +0 -1
  51. package/apps/web/dist/assets/ticket-detail-B9M_eaVu.js +0 -2
  52. package/apps/web/dist/assets/ticket-editor-Chg1lDuU.js +0 -1
  53. package/apps/web/dist/assets/ticket-list-C8500tXj.js +0 -5
  54. package/apps/web/dist/assets/ticket-tags-cbwQ93Gt.js +0 -1
  55. package/apps/web/dist/assets/workspace-settings-Dte44Kkx.js +0 -1
@@ -1,10 +1,12 @@
1
- import { c as searchAuthentication, h as user, l as searchSettingsSchema, m as session, p as auth_schema_exports, s as s3SettingsSchema } from "./config-Qxr_7oqZ.mjs";
1
+ import { a as instanceUpdateSchema, c as s3SettingsSchema, f as account, g as user, h as session, i as brokerSettingsSchema, l as searchAuthentication, m as auth_schema_exports, r as authenticationPolicySchema, u as searchSettingsSchema } from "./config-CbNqtJEj.mjs";
2
2
  import { and, asc, count, desc, eq, getTableColumns, gt, ilike, inArray, isNotNull, isNull, lt, ne, notInArray, or, sql } from "drizzle-orm";
3
3
  import { alias, bigint, boolean, customType, index, integer, jsonb, pgTable, primaryKey, text, timestamp, unique, uuid } from "drizzle-orm/pg-core";
4
4
  import { z } from "zod";
5
- import { createHash, createHmac } from "node:crypto";
5
+ import { decodeJwt } from "jose";
6
+ import { createCipheriv, createDecipheriv, createHash, createHmac, randomBytes } from "node:crypto";
6
7
  import { DeleteObjectCommand, GetObjectCommand, PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
7
8
  import { icons } from "@iconify-json/lucide";
9
+ import { APIError, createAuthMiddleware } from "better-auth/api";
8
10
  import { betterAuth } from "better-auth";
9
11
  import { admin, genericOAuth, jwt } from "better-auth/plugins";
10
12
  import { drizzleAdapter } from "@better-auth/drizzle-adapter";
@@ -13,6 +15,57 @@ import { expo } from "@better-auth/expo";
13
15
  import { mcp } from "@better-auth/mcp";
14
16
  import { cimd } from "@better-auth/cimd";
15
17
  import { fetchClientMetadataResource } from "@better-auth/cimd/node";
18
+ //#region src/adapters/oidc.ts
19
+ const identitySchema = z.object({
20
+ sub: z.string().min(1),
21
+ email: z.email(),
22
+ email_verified: z.literal(true),
23
+ name: z.string().nullish(),
24
+ picture: z.string().nullish()
25
+ });
26
+ const discoverySchema = z.object({
27
+ issuer: z.string(),
28
+ userinfo_endpoint: z.url({ protocol: /^https?$/ })
29
+ });
30
+ function createOIDCUserInfo(config) {
31
+ return async (tokens) => {
32
+ if (!tokens.idToken) return null;
33
+ const claims = decodeJwt(tokens.idToken);
34
+ let profile = identitySchema.safeParse(claims);
35
+ if (!profile.success) {
36
+ if (!config.OIDC_DISCOVERY_URL || !tokens.accessToken) return null;
37
+ const discovery = await fetch(config.OIDC_DISCOVERY_URL, { signal: AbortSignal.timeout(1e4) });
38
+ if (!discovery.ok) return null;
39
+ const metadata = discoverySchema.parse(await discovery.json());
40
+ if (metadata.issuer !== claims.iss) return null;
41
+ const response = await fetch(metadata.userinfo_endpoint, {
42
+ headers: { Authorization: `Bearer ${tokens.accessToken}` },
43
+ signal: AbortSignal.timeout(1e4)
44
+ });
45
+ if (!response.ok) return null;
46
+ profile = identitySchema.safeParse(await response.json());
47
+ }
48
+ if (!profile.success || profile.data.sub !== claims.sub) return null;
49
+ return {
50
+ ...profile.data,
51
+ id: profile.data.sub,
52
+ emailVerified: true,
53
+ name: profile.data.name ?? "",
54
+ image: profile.data.picture ?? void 0
55
+ };
56
+ };
57
+ }
58
+ function matchesOIDCBinding(binding, issuer, clientId) {
59
+ if (!binding.idToken || !issuer || !clientId) return false;
60
+ try {
61
+ const claims = decodeJwt(binding.idToken);
62
+ const audience = Array.isArray(claims.aud) ? claims.aud : [claims.aud];
63
+ return claims.iss === issuer && claims.sub === binding.accountId && audience.includes(clientId);
64
+ } catch {
65
+ return false;
66
+ }
67
+ }
68
+ //#endregion
16
69
  //#region ../../packages/contracts/src/index.ts
17
70
  const states = [
18
71
  "backlog",
@@ -325,6 +378,7 @@ const eventSchema = z.object({
325
378
  createdAt: timestamp$1
326
379
  });
327
380
  z.object({
381
+ authentication: authenticationPolicySchema.default(() => authenticationPolicySchema.parse({})),
328
382
  name: z.string(),
329
383
  description: z.string().default(""),
330
384
  version: z.string(),
@@ -815,7 +869,9 @@ const instanceSettings = pgTable("instance_settings", {
815
869
  description: text("description").notNull().default("Ticketing for people and agents"),
816
870
  storageBackendId: uuid("storage_backend_id").references(() => storageBackends.id),
817
871
  searchConfig: text("search_config"),
818
- brokerConfig: text("broker_config")
872
+ brokerConfig: text("broker_config"),
873
+ emailPasswordEnabled: boolean("email_password_enabled").notNull().default(true),
874
+ ssoAutoProvision: boolean("sso_auto_provision").notNull().default(false)
819
875
  });
820
876
  const deliveryCheckpoints = pgTable("delivery_checkpoints", {
821
877
  destination: text("destination").primaryKey(),
@@ -2593,15 +2649,204 @@ function createDomain(database, services = {}) {
2593
2649
  };
2594
2650
  }
2595
2651
  //#endregion
2652
+ //#region src/instance.ts
2653
+ function storageIdentity(settings) {
2654
+ return createHash("sha256").update(JSON.stringify([
2655
+ settings.endpoint,
2656
+ settings.region,
2657
+ settings.bucket,
2658
+ settings.prefix,
2659
+ settings.forcePathStyle
2660
+ ])).digest("hex");
2661
+ }
2662
+ function createInstance(database, config) {
2663
+ const key = createHash("sha256").update("kanvil:instance-config:v1:").update(config.BETTER_AUTH_SECRET).digest();
2664
+ function seal(value) {
2665
+ const iv = randomBytes(12), cipher = createCipheriv("aes-256-gcm", key, iv);
2666
+ const bytes = Buffer.concat([cipher.update(JSON.stringify(value)), cipher.final()]);
2667
+ return Buffer.concat([
2668
+ iv,
2669
+ cipher.getAuthTag(),
2670
+ bytes
2671
+ ]).toString("base64");
2672
+ }
2673
+ function unseal(value) {
2674
+ try {
2675
+ const bytes = Buffer.from(value, "base64"), decipher = createDecipheriv("aes-256-gcm", key, bytes.subarray(0, 12));
2676
+ decipher.setAuthTag(bytes.subarray(12, 28));
2677
+ return JSON.parse(Buffer.concat([decipher.update(bytes.subarray(28)), decipher.final()]).toString());
2678
+ } catch {
2679
+ return fail(503, "configuration_key_changed", "Saved integration credentials cannot be decrypted. Restore the instance secret from your backup.");
2680
+ }
2681
+ }
2682
+ async function read(db = database.db) {
2683
+ await db.insert(instanceSettings).values({ id: 1 }).onConflictDoNothing();
2684
+ return one(await db.select().from(instanceSettings).where(eq(instanceSettings.id, 1)));
2685
+ }
2686
+ async function backend(id, db = database.db) {
2687
+ const row = one(await db.select().from(storageBackends).where(eq(storageBackends.id, id)));
2688
+ const settings = s3SettingsSchema.parse(unseal(row.config));
2689
+ return {
2690
+ ...row,
2691
+ settings,
2692
+ spec: {
2693
+ provider: "s3",
2694
+ config: settings
2695
+ }
2696
+ };
2697
+ }
2698
+ async function resolveS3(settings, db = database.db) {
2699
+ settings = s3SettingsSchema.strip().parse(settings);
2700
+ const identity = storageIdentity(settings);
2701
+ const previous = (await db.select().from(storageBackends).where(eq(storageBackends.identity, identity)))[0];
2702
+ const old = previous ? s3SettingsSchema.parse(unseal(previous.config)) : void 0;
2703
+ const { secretAccessKey: suppliedSecret, ...publicSettings } = settings, secret = suppliedSecret || (old?.accessKeyId === settings.accessKeyId ? old.secretAccessKey : void 0);
2704
+ if (settings.accessKeyId && !secret) fail(400, "missing_secret", "Provide the S3 secret access key");
2705
+ return {
2706
+ ...publicSettings,
2707
+ ...secret ? { secretAccessKey: secret } : {}
2708
+ };
2709
+ }
2710
+ async function resolveSearch(settings, db = database.db) {
2711
+ settings = searchSettingsSchema.strip().parse(settings);
2712
+ const row = await read(db), old = row.searchConfig ? searchSettingsSchema.parse(unseal(row.searchConfig)) : null, same = old?.url === settings.url && old.index === settings.index;
2713
+ const { apiKey: suppliedKey, password: suppliedPassword, auth: requestedAuth, ...publicSettings } = settings, auth = requestedAuth ?? (suppliedKey ? "apiKey" : suppliedPassword && settings.username ? "basic" : same && old ? searchAuthentication(old) : searchAuthentication(settings)), apiKey = suppliedKey || (same ? old?.apiKey : void 0), password = suppliedPassword || (same && old?.username === settings.username ? old?.password : void 0);
2714
+ if (auth === "apiKey" && !apiKey) fail(400, "missing_secret", "Provide the search API key");
2715
+ if (auth === "basic" && (!settings.username || !password)) fail(400, "missing_secret", "Provide the search username and password");
2716
+ return {
2717
+ ...publicSettings,
2718
+ auth,
2719
+ ...auth === "apiKey" && apiKey ? { apiKey } : {},
2720
+ ...auth === "basic" && password ? { password } : {}
2721
+ };
2722
+ }
2723
+ async function saveBackend(settings, db = database.db) {
2724
+ const complete = await resolveS3(settings, db), identity = storageIdentity(settings);
2725
+ return one(await db.insert(storageBackends).values({
2726
+ identity,
2727
+ config: seal(complete)
2728
+ }).onConflictDoUpdate({
2729
+ target: storageBackends.identity,
2730
+ set: { config: seal(complete) }
2731
+ }).returning());
2732
+ }
2733
+ async function storage() {
2734
+ if (config.KANVIL_STORAGE_PROVIDER === "local") return null;
2735
+ if (config.KANVIL_STORAGE_PROVIDER === "s3") return saveBackend(s3SettingsSchema.parse({
2736
+ endpoint: config.S3_ENDPOINT ?? "",
2737
+ region: config.S3_REGION,
2738
+ bucket: config.S3_BUCKET,
2739
+ prefix: config.S3_PREFIX,
2740
+ forcePathStyle: config.S3_FORCE_PATH_STYLE,
2741
+ accessKeyId: config.S3_ACCESS_KEY_ID ?? "",
2742
+ secretAccessKey: config.S3_SECRET_ACCESS_KEY
2743
+ }));
2744
+ const row = await read();
2745
+ return row.storageBackendId ? backend(row.storageBackendId) : null;
2746
+ }
2747
+ async function search() {
2748
+ const row = await read();
2749
+ if (config.KANVIL_SEARCH_PROVIDER === "postgres") return null;
2750
+ if (config.KANVIL_SEARCH_PROVIDER === "elasticsearch") {
2751
+ const settings = searchSettingsSchema.parse({
2752
+ url: config.ELASTICSEARCH_URL,
2753
+ index: config.ELASTICSEARCH_INDEX ?? `kanvil-${row.identity}`,
2754
+ auth: config.ELASTICSEARCH_AUTH,
2755
+ apiKey: config.ELASTICSEARCH_API_KEY,
2756
+ username: config.ELASTICSEARCH_USERNAME,
2757
+ password: config.ELASTICSEARCH_PASSWORD
2758
+ });
2759
+ return {
2760
+ ...settings,
2761
+ auth: searchAuthentication(settings)
2762
+ };
2763
+ }
2764
+ const settings = row.searchConfig ? searchSettingsSchema.parse(unseal(row.searchConfig)) : null;
2765
+ return settings ? {
2766
+ ...settings,
2767
+ auth: searchAuthentication(settings)
2768
+ } : null;
2769
+ }
2770
+ async function broker() {
2771
+ if (config.KANVIL_BROKER_PROVIDER === "postgres") return null;
2772
+ if (config.KANVIL_BROKER_PROVIDER === "rabbitmq") return brokerSettingsSchema.parse({
2773
+ url: config.RABBITMQ_URL,
2774
+ exchange: config.RABBITMQ_EXCHANGE
2775
+ });
2776
+ const row = await read();
2777
+ return row.brokerConfig ? brokerSettingsSchema.parse(unseal(row.brokerConfig)) : null;
2778
+ }
2779
+ async function resolveBroker(settings, db = database.db) {
2780
+ settings = brokerSettingsSchema.strip().parse(settings);
2781
+ const row = await read(db), old = row.brokerConfig ? brokerSettingsSchema.parse(unseal(row.brokerConfig)) : null;
2782
+ const url = settings.url || old?.url;
2783
+ if (!url) return fail(400, "missing_secret", "Provide the RabbitMQ connection URL");
2784
+ return {
2785
+ ...settings,
2786
+ url
2787
+ };
2788
+ }
2789
+ async function authentication() {
2790
+ return authenticationPolicySchema.strip().parse(await read());
2791
+ }
2792
+ async function update(value, actorId, oidcIssuer) {
2793
+ const input = instanceUpdateSchema.parse(value);
2794
+ return database.db.transaction(async (tx) => {
2795
+ await tx.execute(sql`SELECT pg_advisory_xact_lock(7361083)`);
2796
+ const current = await read(tx);
2797
+ assertVersion(current, input.expectedVersion, "instance settings", "saving");
2798
+ if (input.authentication && !input.authentication.emailPasswordEnabled) {
2799
+ if (!config.OIDC_DISCOVERY_URL || !config.OIDC_CLIENT_ID || !config.OIDC_CLIENT_SECRET) fail(409, "oidc_required", "Configure an OIDC provider before disabling password sign-in");
2800
+ if (!(actorId ? await tx.select({
2801
+ idToken: account.idToken,
2802
+ accountId: account.accountId
2803
+ }).from(account).where(and(eq(account.userId, actorId), eq(account.providerId, "oidc"))) : []).some((binding) => matchesOIDCBinding(binding, oidcIssuer, config.OIDC_CLIENT_ID))) fail(409, "oidc_binding_required", "Link your OIDC account in User settings using the configured provider before disabling password sign-in");
2804
+ }
2805
+ let storageId = current.storageBackendId, searchConfig = current.searchConfig, brokerConfig = current.brokerConfig;
2806
+ if (!config.KANVIL_STORAGE_PROVIDER) storageId = input.storage.provider === "s3" ? (await saveBackend(input.storage, tx)).id : null;
2807
+ if (!config.KANVIL_SEARCH_PROVIDER) {
2808
+ if (input.search.provider === "postgres") searchConfig = null;
2809
+ else searchConfig = seal(await resolveSearch(input.search, tx));
2810
+ }
2811
+ if (!config.KANVIL_BROKER_PROVIDER && input.broker) brokerConfig = input.broker.provider === "postgres" ? null : seal(await resolveBroker(input.broker, tx));
2812
+ await tx.update(instanceSettings).set({
2813
+ ...input.authentication,
2814
+ name: input.name,
2815
+ description: input.description,
2816
+ storageBackendId: storageId,
2817
+ searchConfig,
2818
+ brokerConfig,
2819
+ version: current.version + 1
2820
+ }).where(eq(instanceSettings.id, 1));
2821
+ });
2822
+ }
2823
+ return {
2824
+ read,
2825
+ authentication,
2826
+ storage,
2827
+ search,
2828
+ backend,
2829
+ saveBackend,
2830
+ resolveS3,
2831
+ resolveSearch,
2832
+ broker,
2833
+ resolveBroker,
2834
+ update,
2835
+ config
2836
+ };
2837
+ }
2838
+ //#endregion
2596
2839
  //#region src/auth.ts
2597
2840
  function createAuth(database, config) {
2841
+ const instance = createInstance(database, config);
2598
2842
  return betterAuth({
2599
2843
  appName: "Kanvil",
2600
2844
  baseURL: config.KANVIL_URL,
2601
2845
  secret: config.BETTER_AUTH_SECRET,
2602
2846
  database: drizzleAdapter(database.db, {
2603
2847
  provider: "pg",
2604
- schema: auth_schema_exports
2848
+ schema: auth_schema_exports,
2849
+ transaction: true
2605
2850
  }),
2606
2851
  trustedOrigins: config.trustedOrigins,
2607
2852
  emailAndPassword: {
@@ -2611,7 +2856,26 @@ function createAuth(database, config) {
2611
2856
  },
2612
2857
  account: { accountLinking: {
2613
2858
  enabled: true,
2614
- disableImplicitLinking: true
2859
+ disableImplicitLinking: true,
2860
+ allowDifferentEmails: true
2861
+ } },
2862
+ onAPIError: { errorURL: `${config.KANVIL_URL}/login` },
2863
+ hooks: { before: createAuthMiddleware(async (ctx) => {
2864
+ if (ctx.path === "/sign-in/email" && !(await instance.authentication()).emailPasswordEnabled) throw new APIError("FORBIDDEN", {
2865
+ code: "password_sign_in_disabled",
2866
+ message: "Use your identity provider to sign in"
2867
+ });
2868
+ }) },
2869
+ user: { validateUserInfo: async ({ user, source }) => {
2870
+ if (source.method !== "oauth") return void 0;
2871
+ if (source.oauth?.providerId !== "oidc" || !user.email || user.emailVerified !== true) return {
2872
+ error: "email_not_verified",
2873
+ errorDescription: "The identity provider must verify your email address"
2874
+ };
2875
+ if (source.action === "create-user" && !(await instance.authentication()).ssoAutoProvision) return {
2876
+ error: "signup_disabled",
2877
+ errorDescription: "Ask an administrator to create and link your account"
2878
+ };
2615
2879
  } },
2616
2880
  telemetry: { enabled: false },
2617
2881
  plugins: [
@@ -2649,6 +2913,7 @@ function createAuth(database, config) {
2649
2913
  providerId: "oidc",
2650
2914
  discoveryUrl: config.OIDC_DISCOVERY_URL,
2651
2915
  requireIdTokenVerification: true,
2916
+ getUserInfo: createOIDCUserInfo(config),
2652
2917
  clientId: config.OIDC_CLIENT_ID,
2653
2918
  clientSecret: config.OIDC_CLIENT_SECRET,
2654
2919
  scopes: [
@@ -2657,10 +2922,10 @@ function createAuth(database, config) {
2657
2922
  "email"
2658
2923
  ],
2659
2924
  pkce: true,
2660
- disableSignUp: true
2925
+ disableSignUp: false
2661
2926
  }] : [] })
2662
2927
  ]
2663
2928
  });
2664
2929
  }
2665
2930
  //#endregion
2666
- export { storageBackends as A, notificationPreferencesInput as B, members as C, projects as D, profilePhotos as E, ApiError as F, roles as G, okSchema as H, errorSchema as I, scopes as K, hasAdminRole as L, tickets as M, workspaceMembers as N, pushDeliveries as O, workspaces as P, isOperation as R, instanceSettings as S, notifications as T, operations as U, notificationPreferencesSchema as V, pushDeviceInput as W, attachments as _, json as a, eventClock as b, projectRoleSQL as c, createBlobStorage as d, decodeImage as f, attachmentContent as g, createPollingJob as h, fail as i, subscriptions as j, pushDevices as k, createTicketSearch as l, createObjectStore as m, assertVersion as n, one as o, imageResponse as p, createDomain as r, requireRow as s, createAuth as t, createTicketIndex as u, blobObjects as v, notificationPreferences as w, events as x, deliveryCheckpoints as y, memberSchema as z };
2931
+ export { pushDevices as A, memberSchema as B, instanceSettings as C, profilePhotos as D, notifications as E, workspaces as F, pushDeviceInput as G, notificationPreferencesSchema as H, ApiError as I, matchesOIDCBinding as J, roles as K, errorSchema as L, subscriptions as M, tickets as N, projects as O, workspaceMembers as P, hasAdminRole as R, events as S, notificationPreferences as T, okSchema as U, notificationPreferencesInput as V, operations as W, attachmentContent as _, fail as a, deliveryCheckpoints as b, requireRow as c, createTicketIndex as d, createBlobStorage as f, createPollingJob as g, createObjectStore as h, createDomain as i, storageBackends as j, pushDeliveries as k, projectRoleSQL as l, imageResponse as m, createInstance as n, json as o, decodeImage as p, scopes as q, assertVersion as r, one as s, createAuth as t, createTicketSearch as u, attachments as v, members as w, eventClock as x, blobObjects as y, isOperation as z };
@@ -385,13 +385,18 @@ const brokerSettingsSchema = z.object({
385
385
  exchange: z.string().min(1).max(200).regex(/^[a-zA-Z0-9_.-]+$/).refine((value) => !value.startsWith("amq."), "Use an application exchange").default("kanvil.events")
386
386
  }).strict();
387
387
  const eventDeliverySettingsSchema = z.discriminatedUnion("provider", [z.object({ provider: z.literal("postgres") }).strict(), brokerSettingsSchema.extend({ provider: z.literal("rabbitmq") })]);
388
+ const authenticationPolicySchema = z.object({
389
+ emailPasswordEnabled: z.boolean().default(true),
390
+ ssoAutoProvision: z.boolean().default(false)
391
+ }).strict();
388
392
  const instanceUpdateSchema = z.object({
389
393
  expectedVersion: z.number().int().positive(),
390
394
  name: z.string().trim().min(1).max(100),
391
395
  description: z.string().max(500),
392
396
  storage: storageSettingsSchema,
393
397
  search: externalSearchSettingsSchema,
394
- broker: eventDeliverySettingsSchema.optional()
398
+ broker: eventDeliverySettingsSchema.optional(),
399
+ authentication: authenticationPolicySchema.optional()
395
400
  }).strict();
396
401
  z.object({
397
402
  name: z.string(),
@@ -402,6 +407,7 @@ const adminInstanceSchema = z.object({
402
407
  version: z.number().int(),
403
408
  name: z.string(),
404
409
  description: z.string(),
410
+ authentication: authenticationPolicySchema.default(() => authenticationPolicySchema.parse({})),
405
411
  storage: z.object({
406
412
  provider: z.enum(["local", "s3"]),
407
413
  source: z.enum(["deployment", "admin"]),
@@ -572,4 +578,4 @@ function readConfig(env = process.env) {
572
578
  };
573
579
  }
574
580
  //#endregion
575
- export { photoInputSchema as a, searchAuthentication as c, account as d, apikey as f, user as h, instanceUpdateSchema as i, searchSettingsSchema as l, session as m, adminInstanceSchema as n, photoResultSchema as o, auth_schema_exports as p, brokerSettingsSchema as r, s3SettingsSchema as s, readConfig as t, createDatabase as u };
581
+ export { instanceUpdateSchema as a, s3SettingsSchema as c, createDatabase as d, account as f, user as g, session as h, brokerSettingsSchema as i, searchAuthentication as l, auth_schema_exports as m, adminInstanceSchema as n, photoInputSchema as o, apikey as p, authenticationPolicySchema as r, photoResultSchema as s, readConfig as t, searchSettingsSchema as u };