openship 0.6.5 → 0.6.6

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 (54) hide show
  1. package/dist/{bare-XQIPDO4R.js → bare-OXJVW3AK.js} +11 -10
  2. package/dist/{chunk-WE7BXW6L.js → chunk-2YM3NI2E.js} +2 -2
  3. package/dist/{chunk-RFWJE7ZN.js → chunk-4I2BM2DT.js} +3 -3
  4. package/dist/{chunk-QLX7NBEA.js → chunk-5DMWSTOM.js} +78 -1836
  5. package/dist/{chunk-VDESJSG6.js → chunk-77QDF6UD.js} +135 -21
  6. package/dist/{chunk-7334NSJL.js → chunk-A4GXYE4L.js} +67 -31
  7. package/dist/{chunk-HPXP6LXZ.js → chunk-CBLD2HS5.js} +3 -3
  8. package/dist/{chunk-6JELW75A.js → chunk-FE7ZZQSE.js} +3 -3
  9. package/dist/{chunk-XVOBL7BC.js → chunk-HKRQXPTI.js} +1 -1
  10. package/dist/{chunk-GRTXMUHA.js → chunk-LFE5KQ7Y.js} +11 -11
  11. package/dist/{chunk-TM2IQPYU.js → chunk-LH2PLRQO.js} +2 -2
  12. package/dist/{chunk-LFCSC4IU.js → chunk-LUKYPVJ4.js} +3 -3
  13. package/dist/{chunk-VHMOQL5Y.js → chunk-MLYSEZQB.js} +115 -19
  14. package/dist/{chunk-7S4USQYF.js → chunk-NK5N5LXN.js} +50 -122
  15. package/dist/{chunk-3ZAGC5NT.js → chunk-OBPKYXQL.js} +17 -6
  16. package/dist/{chunk-5OUWIPOB.js → chunk-PCPVTH6M.js} +12 -84
  17. package/dist/chunk-Q7WGTQQ5.js +88 -0
  18. package/dist/{chunk-VLQ3UIVO.js → chunk-QVW76WDL.js} +52 -26
  19. package/dist/{chunk-PZRRL5PC.js → chunk-RSR7PFIQ.js} +14 -3
  20. package/dist/{chunk-A7EWM2KA.js → chunk-SM26HDS2.js} +1 -1
  21. package/dist/{chunk-LEYSL46B.js → chunk-UJ6JZM65.js} +3426 -632
  22. package/dist/{chunk-WFRXYWK2.js → chunk-UVNMQM7W.js} +1 -1
  23. package/dist/chunk-VTGPEC6B.js +308 -0
  24. package/dist/chunk-WVAML4H6.js +2145 -0
  25. package/dist/{chunk-MSZUCNPV.js → chunk-YIMOCZBZ.js} +1 -1
  26. package/dist/{chunk-EZPPTWJQ.js → chunk-YMMQRE5J.js} +1 -1
  27. package/dist/{cloud-3DODOWTR.js → cloud-75VIT4KV.js} +16 -15
  28. package/dist/{detect-P4CIQBRH.js → detect-OIE5V3VZ.js} +3 -3
  29. package/dist/{docker-3K2NEBD6.js → docker-VTR2GK5Y.js} +7 -6
  30. package/dist/{docker-edge-executor-YZZ6P7IC.js → docker-edge-executor-22IBXNYJ.js} +4 -4
  31. package/dist/{edge-import-MCJNVTZ2.js → edge-import-WJUB4M7F.js} +23 -22
  32. package/dist/{edge-preflight-ES6FH4JK.js → edge-preflight-GBNCPNVU.js} +10 -10
  33. package/dist/{ensure-container-edge-YPITNH45.js → ensure-container-edge-KPI2CVXJ.js} +9 -9
  34. package/dist/{executor-DZGX55K2.js → executor-7LXXZPBR.js} +6 -3
  35. package/dist/{host-channel-preflight-MUXE3KZU.js → host-channel-preflight-KE6OONYK.js} +23 -22
  36. package/dist/index.js +95 -84
  37. package/dist/{nginx-NSAHWGJV.js → nginx-KUFMLMAC.js} +9 -7
  38. package/dist/{openresty-lua-DR4D3EZ6.js → openresty-lua-ATTH35BE.js} +6 -4
  39. package/dist/{privilege-YTVI4SKF.js → privilege-I4W3ZNZK.js} +4 -4
  40. package/dist/server/engine/samples/amavisd/amavisd.conf +25 -0
  41. package/dist/server/engine/samples/postfix/master.cf +14 -0
  42. package/dist/server/index.js +35002 -27533
  43. package/dist/server/migrations/0105_mail_inbound_rules.sql +80 -0
  44. package/dist/server/migrations/0106_mcp_call_tracking.sql +23 -0
  45. package/dist/server/migrations/0107_build_minute_metering_indexes.sql +20 -0
  46. package/dist/server/migrations/0108_credential.sql +68 -0
  47. package/dist/server/migrations/meta/_journal.json +28 -0
  48. package/dist/{setup-WUUZURHS.js → setup-XGLGUUFW.js} +10 -10
  49. package/dist/ssh-client-JBMA7SN4.js +28 -0
  50. package/dist/{up-XEO3SR6Z.js → up-K4BV7466.js} +26 -27
  51. package/package.json +1 -1
  52. package/dist/chunk-6HOA5JT7.js +0 -124
  53. package/dist/chunk-XRNRT37W.js +0 -316
  54. /package/dist/{dist-es-ZP5QDSJ3.js → dist-es-AXFRS7PS.js} +0 -0
@@ -953,136 +953,6 @@ function ownsNetworkEndpoint(resolvedNetworkMode) {
953
953
 
954
954
  // ../../packages/core/src/constants.ts
955
955
  var DEFAULT_IMAGE_REGISTRY = "ghcr.io/oblien";
956
- var PLANS = {
957
- free: {
958
- id: "free",
959
- name: "Free",
960
- description: "Get started for free",
961
- price: { monthly: 0, annual: 0 },
962
- stripePriceId: { monthly: null, annual: null },
963
- // Public dollar `price` on the paid tiers is `null` — the UI renders
964
- // "coming soon" and the Subscribe CTA stays disabled. Cloud pricing is
965
- // intentionally NOT published yet. `monthlyCredits` (the CPU-time/credit
966
- // quota pushed to Oblien) is real so tiers still enforce — it's an internal
967
- // number, never shown as a price. NOTE (tune before launch): these credit
968
- // numbers are placeholders sized to Oblien's 10,000,000-credit ceiling; the
969
- // true credit-per-$/per-cpu-minute rate is Oblien-configured. 1 openship
970
- // credit = 1 Oblien credit = 1000 milli (the wrapper divides by 1000 at the
971
- // Oblien boundary). Self-hosted is free and surfaces none of these numbers.
972
- monthlyCredits: 5e5,
973
- // 500 credits — free-tier allowance
974
- oblienLimits: {
975
- max_workspaces: 1,
976
- max_vcpus: 2,
977
- max_ram_mb: 2048,
978
- max_disk_gb: 10
979
- },
980
- features: [
981
- "1 workspace",
982
- "Community support"
983
- ],
984
- popular: false,
985
- support: "community"
986
- },
987
- pro: {
988
- id: "pro",
989
- name: "Pro",
990
- description: "For solo builders shipping production workloads",
991
- price: { monthly: null, annual: null },
992
- // coming soon — pricing not published
993
- stripePriceId: {
994
- monthly: process.env.STRIPE_PRICE_PRO_MONTHLY ?? "price_pro_monthly_placeholder",
995
- annual: process.env.STRIPE_PRICE_PRO_ANNUAL ?? "price_pro_annual_placeholder"
996
- },
997
- monthlyCredits: 1e7,
998
- // 10,000 credits/mo (placeholder — tune before launch)
999
- oblienLimits: {
1000
- max_workspaces: 10,
1001
- max_vcpus: 16,
1002
- max_ram_mb: 32768,
1003
- max_disk_gb: 100
1004
- },
1005
- features: [
1006
- "Up to 10 workspaces",
1007
- "Email support"
1008
- ],
1009
- popular: true,
1010
- support: "email"
1011
- },
1012
- team: {
1013
- id: "team",
1014
- name: "Team",
1015
- description: "For teams collaborating on shared infra",
1016
- price: { monthly: null, annual: null },
1017
- // coming soon — pricing not published
1018
- stripePriceId: {
1019
- monthly: process.env.STRIPE_PRICE_TEAM_MONTHLY ?? "price_team_monthly_placeholder",
1020
- annual: process.env.STRIPE_PRICE_TEAM_ANNUAL ?? "price_team_annual_placeholder"
1021
- },
1022
- monthlyCredits: 6e7,
1023
- // 60,000 credits/mo (placeholder — tune before launch)
1024
- oblienLimits: {
1025
- max_workspaces: 50,
1026
- max_vcpus: 64,
1027
- max_ram_mb: 131072,
1028
- max_disk_gb: 500
1029
- },
1030
- features: [
1031
- "Up to 50 workspaces",
1032
- "Team collaboration",
1033
- "Priority email support"
1034
- ],
1035
- popular: false,
1036
- support: "priority email"
1037
- },
1038
- enterprise: {
1039
- id: "enterprise",
1040
- name: "Enterprise",
1041
- description: "Custom limits, SLAs, dedicated infra",
1042
- price: { monthly: null, annual: null },
1043
- // contact sales
1044
- stripePriceId: { monthly: null, annual: null },
1045
- monthlyCredits: null,
1046
- // admin grants
1047
- oblienLimits: null,
1048
- // custom per contract
1049
- features: [
1050
- "Custom credit allocation",
1051
- "Custom workspace limits",
1052
- "SSO / SAML",
1053
- "Dedicated support & SLA"
1054
- ],
1055
- popular: false,
1056
- support: "dedicated",
1057
- contactSales: "mailto:sales@openship.io"
1058
- }
1059
- };
1060
- var CREDIT_PACKS = [
1061
- {
1062
- id: "pack_5k",
1063
- name: "5,000 credits",
1064
- credits_milli: 5e6,
1065
- price_cents: 500,
1066
- stripePriceId: process.env.STRIPE_PRICE_PACK_5K ?? "price_pack_5k_placeholder",
1067
- sortOrder: 10
1068
- },
1069
- {
1070
- id: "pack_25k",
1071
- name: "25,000 credits",
1072
- credits_milli: 25e6,
1073
- price_cents: 2e3,
1074
- stripePriceId: process.env.STRIPE_PRICE_PACK_25K ?? "price_pack_25k_placeholder",
1075
- sortOrder: 20
1076
- },
1077
- {
1078
- id: "pack_100k",
1079
- name: "100,000 credits",
1080
- credits_milli: 1e8,
1081
- price_cents: 7e3,
1082
- stripePriceId: process.env.STRIPE_PRICE_PACK_100K ?? "price_pack_100k_placeholder",
1083
- sortOrder: 30
1084
- }
1085
- ];
1086
956
 
1087
957
  // ../../packages/core/src/image-ref.ts
1088
958
  function buildImageRef(opts, image) {
@@ -1096,6 +966,32 @@ function buildImageRef(opts, image) {
1096
966
  const tag = image.tagOverride?.trim() || process.env.OPENSHIP_VERSION?.trim() || opts?.fallbackTag?.trim() || "latest";
1097
967
  return `${registry2}/${image.name}:${tag}`;
1098
968
  }
969
+ var DOCKER_HUB_REGISTRY = "docker.io";
970
+ var DOCKER_HUB_ALIASES = [
971
+ "https://index.docker.io/v1/",
972
+ "https://index.docker.io/v1",
973
+ "index.docker.io",
974
+ "registry-1.docker.io",
975
+ DOCKER_HUB_REGISTRY
976
+ ];
977
+ function registryForImage(ref) {
978
+ const trimmed2 = ref.trim();
979
+ const slash = trimmed2.indexOf("/");
980
+ if (slash === -1) return DOCKER_HUB_REGISTRY;
981
+ const first = trimmed2.slice(0, slash);
982
+ const looksLikeHost = first.includes(".") || first.includes(":") || first === "localhost";
983
+ return looksLikeHost ? first.toLowerCase() : DOCKER_HUB_REGISTRY;
984
+ }
985
+ function normalizeRegistryHost(input) {
986
+ const bare = input.trim().toLowerCase().replace(/^[a-z][a-z0-9+.-]*:\/\//, "").replace(/\/+$/, "");
987
+ const host = bare.split("/")[0] ?? "";
988
+ return DOCKER_HUB_ALIASES.includes(bare) || DOCKER_HUB_ALIASES.includes(host) ? DOCKER_HUB_REGISTRY : host;
989
+ }
990
+ function registryConfigKeys(registry2) {
991
+ const host = normalizeRegistryHost(registry2);
992
+ if (host === DOCKER_HUB_REGISTRY) return [...DOCKER_HUB_ALIASES];
993
+ return [host, `https://${host}`, `https://${host}/`, `${host}/`, `http://${host}`];
994
+ }
1099
995
 
1100
996
  // ../../packages/core/src/edge-image-ref.ts
1101
997
  function buildEdgeImageRef(opts) {
@@ -1342,7 +1238,8 @@ var OPENSHIP_RESOURCE_TIERS = [
1342
1238
  "micro",
1343
1239
  "low",
1344
1240
  "medium",
1345
- "high"
1241
+ "high",
1242
+ "xlarge"
1346
1243
  ];
1347
1244
 
1348
1245
  // ../../packages/core/src/openship-config/parse.ts
@@ -2440,6 +2337,13 @@ var HOST_CHANNEL_NOT_PROVISIONED = (
2440
2337
  `Re-run \`${HOST_CHANNEL_PROVISION_COMMAND}\` to provision the host channel. \`${HOST_CHANNEL_RECHECK_COMMAND}\` reports whether it worked.`
2441
2338
  );
2442
2339
  var HOST_CHANNEL_UNPROVISIONED = "Openship is running in a container with no host channel \u2014 OPENSHIP_HOST_SSH_HOST is unset, so there is no address to reach the host at and nothing has been dialed.";
2340
+ var HOST_CHANNEL_DEFAULT_ACCOUNT = "root";
2341
+ function hostChannelAccount(env) {
2342
+ return env.OPENSHIP_HOST_SSH_USER?.trim() || HOST_CHANNEL_DEFAULT_ACCOUNT;
2343
+ }
2344
+ var HOST_CHANNEL_AUTH_REJECTED = "The host accepted the connection and then refused Openship's key. Either the key is no longer in the target account's `authorized_keys`, or sshd does not permit that account to log in at all (for a root channel, check `sshd -T | grep -i permitrootlogin`).";
2345
+ var HOST_CHANNEL_AUTH_REJECTED_SHORT = "the host refused the channel key \u2014 re-authorize it, or permit that account to log in";
2346
+ var HOST_CHANNEL_ROW_CREDENTIALS_UNUSED = `The SSH credentials stored on this server are not used for this connection \u2014 the host channel has its own key, provisioned by \`${HOST_CHANNEL_PROVISION_COMMAND}\`.`;
2443
2347
  var HOST_CHANNEL_OPT_OUT = (
2444
2348
  // The command sits early in the sentence on purpose: every surface wraps this to a
2445
2349
  // terminal width, and a line break through the middle of a command is a command an
@@ -2777,7 +2681,19 @@ var dashboardRuntimeOrigins = Object.values(DASHBOARD_RUNTIME_TARGETS).flatMap(
2777
2681
  );
2778
2682
 
2779
2683
  // ../../packages/core/src/resources.ts
2780
- var RESOURCE_TIER_ORDER = ["micro", "low", "medium", "high"];
2684
+ var RESOURCE_TIER_SPECS = {
2685
+ micro: { cpuCores: 0.25, memoryMb: 256, diskMb: 4096 },
2686
+ low: { cpuCores: 0.5, memoryMb: 512, diskMb: 8192 },
2687
+ medium: { cpuCores: 1, memoryMb: 1024, diskMb: 16384 },
2688
+ high: { cpuCores: 2, memoryMb: 2048, diskMb: 32768 },
2689
+ // The table stopped at `high` (2 vCPU), which meant the top paid plan could
2690
+ // offer no more POWER per service than the one below it — only more services —
2691
+ // and anything larger was reachable only through `custom`, the one path with no
2692
+ // bounds at all. 4 vCPU / 8 GB matches the build machine and the
2693
+ // resource-schema ceiling, so it needs no new limits anywhere.
2694
+ xlarge: { cpuCores: 4, memoryMb: 8192, diskMb: 65536 }
2695
+ };
2696
+ var RESOURCE_TIER_ORDER = ["micro", "low", "medium", "high", "xlarge"];
2781
2697
  var ALL_RESOURCE_TIERS = [
2782
2698
  "unlimited",
2783
2699
  ...RESOURCE_TIER_ORDER,
@@ -4356,7 +4272,7 @@ COMMIT;
4356
4272
  },
4357
4273
  {
4358
4274
  available: true,
4359
- verified: true,
4275
+ verified: false,
4360
4276
  id: "mongodb",
4361
4277
  name: "MongoDB",
4362
4278
  description: "MongoDB document database with the Mongo Express web admin UI.",
@@ -4403,9 +4319,6 @@ COMMIT;
4403
4319
  image: "mongo-express:1.0.2",
4404
4320
  exposedPort: 8081,
4405
4321
  exposed: true,
4406
- ports: [
4407
- "8081:8081"
4408
- ],
4409
4322
  routes: [
4410
4323
  {
4411
4324
  port: 8081
@@ -4429,7 +4342,10 @@ COMMIT;
4429
4342
  retries: 5,
4430
4343
  startPeriod: "20s"
4431
4344
  },
4432
- restart: "unless-stopped"
4345
+ restart: "unless-stopped",
4346
+ ports: [
4347
+ "8216:8081"
4348
+ ]
4433
4349
  }
4434
4350
  ],
4435
4351
  configFields: [
@@ -4488,19 +4404,19 @@ COMMIT;
4488
4404
  {
4489
4405
  id: "dbUrl",
4490
4406
  label: "Database URL",
4491
- source: "template:mongodb://root:{{env:mongo:MONGO_INITDB_ROOT_PASSWORD}}@{{host}}:27017/",
4492
- sourceLabel: "Public",
4407
+ source: "template:mongodb://root:{{env:mongo:MONGO_INITDB_ROOT_PASSWORD}}@mongo:27017/",
4408
+ sourceLabel: "Internal",
4493
4409
  variants: [
4494
4410
  {
4495
- id: "internal",
4496
- label: "Internal",
4497
- source: "template:mongodb://root:{{env:mongo:MONGO_INITDB_ROOT_PASSWORD}}@mongo:27017/"
4411
+ id: "host",
4412
+ label: "From this server",
4413
+ source: "template:mongodb://root:{{env:mongo:MONGO_INITDB_ROOT_PASSWORD}}@{{host}}:27017/"
4498
4414
  }
4499
4415
  ],
4500
4416
  secret: true,
4501
4417
  envKey: "MONGODB_URI",
4502
4418
  recommended: true,
4503
- help: "Direct MongoDB connection (root user). Published on port 27017. Switch to Internal for apps on the same project network."
4419
+ help: 'Direct MongoDB connection (root user) on the project\u2019s private network \u2014 use it from another service, or bind this app into a project. Port 27017 publishes on 127.0.0.1 only, so the "From this server" form works from the box itself or through an SSH tunnel, not from the internet.'
4504
4420
  }
4505
4421
  ]
4506
4422
  },
@@ -4712,9 +4628,6 @@ COMMIT;
4712
4628
  {
4713
4629
  name: "n8n",
4714
4630
  image: "n8nio/n8n:latest",
4715
- ports: [
4716
- "5678:5678"
4717
- ],
4718
4631
  exposedPort: 5678,
4719
4632
  exposed: true,
4720
4633
  environment: {
@@ -4729,7 +4642,10 @@ COMMIT;
4729
4642
  volumes: [
4730
4643
  "n8n_data:/home/node/.n8n"
4731
4644
  ],
4732
- restart: "unless-stopped"
4645
+ restart: "unless-stopped",
4646
+ ports: [
4647
+ "8213:5678"
4648
+ ]
4733
4649
  }
4734
4650
  ],
4735
4651
  configFields: [
@@ -4795,13 +4711,43 @@ COMMIT;
4795
4711
  }
4796
4712
  ]
4797
4713
  }
4798
- ]
4714
+ ],
4715
+ connection: {
4716
+ title: "Set up n8n",
4717
+ description: "n8n has no preset login. Open the editor and the first screen creates your owner account.",
4718
+ guide: {
4719
+ defaultMode: "public",
4720
+ intro: "Your project gets a private n8n instance for building workflows.",
4721
+ useHint: "Webhook nodes are published under the webhook base URL below \u2014 use that origin when registering callbacks with third parties."
4722
+ },
4723
+ outputs: [
4724
+ {
4725
+ id: "url",
4726
+ label: "Editor",
4727
+ source: "publicUrl:n8n",
4728
+ kind: "url",
4729
+ recommended: true,
4730
+ help: "First visit asks you to create the owner account."
4731
+ },
4732
+ {
4733
+ id: "webhookUrl",
4734
+ label: "Webhook base URL",
4735
+ source: "env:n8n:WEBHOOK_URL",
4736
+ kind: "url",
4737
+ envKey: "N8N_WEBHOOK_URL",
4738
+ help: "Base origin n8n advertises for Webhook nodes."
4739
+ }
4740
+ ],
4741
+ firstLogin: {
4742
+ note: "Create the owner account as soon as it deploys \u2014 the setup screen is unauthenticated until you do. Never change the generated encryption key afterwards or every stored credential becomes unreadable. n8n issues https-only session cookies, so sign in over the domain rather than a plain-http address."
4743
+ }
4744
+ }
4799
4745
  },
4800
4746
  {
4801
- available: false,
4747
+ available: true,
4802
4748
  id: "ghost",
4803
4749
  name: "Ghost",
4804
- description: "Modern publishing platform for blogs, newsletters, and membership sites.",
4750
+ description: "Modern publishing platform for blogs, newsletters, and membership sites. Claim the owner account on your first visit.",
4805
4751
  kind: "template",
4806
4752
  logo: "ghost",
4807
4753
  category: "cms",
@@ -4829,9 +4775,6 @@ COMMIT;
4829
4775
  {
4830
4776
  name: "ghost",
4831
4777
  image: "ghost:5-alpine",
4832
- ports: [
4833
- "2368:2368"
4834
- ],
4835
4778
  exposedPort: 2368,
4836
4779
  exposed: true,
4837
4780
  dependsOn: [
@@ -4851,7 +4794,10 @@ COMMIT;
4851
4794
  volumes: [
4852
4795
  "ghost_content:/var/lib/ghost/content"
4853
4796
  ],
4854
- restart: "unless-stopped"
4797
+ restart: "unless-stopped",
4798
+ ports: [
4799
+ "8212:2368"
4800
+ ]
4855
4801
  }
4856
4802
  ],
4857
4803
  configFields: [
@@ -4871,13 +4817,43 @@ COMMIT;
4871
4817
  generateGroup: "ghostdb",
4872
4818
  secret: true
4873
4819
  }
4874
- ]
4820
+ ],
4821
+ connection: {
4822
+ title: "Set up Ghost",
4823
+ description: "Ghost has no preset login. Open Ghost Admin and the first screen creates the owner account.",
4824
+ guide: {
4825
+ defaultMode: "public"
4826
+ },
4827
+ outputs: [
4828
+ {
4829
+ id: "admin",
4830
+ label: "Ghost Admin",
4831
+ source: "template:{{env:ghost:url}}/ghost/",
4832
+ kind: "url",
4833
+ recommended: true,
4834
+ help: "Create the owner account here on first visit."
4835
+ },
4836
+ {
4837
+ id: "url",
4838
+ label: "Site",
4839
+ source: "publicUrl:ghost",
4840
+ kind: "url",
4841
+ help: "The public blog."
4842
+ }
4843
+ ],
4844
+ firstLogin: {
4845
+ note: "Claim the owner account at /ghost/ immediately \u2014 it is unauthenticated until you do, so the first visitor owns the blog. Ghost may restart a few times on first boot while MySQL initialises; that is expected."
4846
+ }
4847
+ },
4848
+ verified: true
4875
4849
  },
4876
4850
  {
4877
- available: false,
4851
+ available: true,
4852
+ verified: true,
4878
4853
  id: "directus",
4879
4854
  name: "Directus",
4880
- description: "Headless CMS with an instant REST + GraphQL API over your data. Create the admin on first visit.",
4855
+ description: "Headless CMS with an instant REST + GraphQL API over your data. Sign in with the admin email and generated password below.",
4856
+ repository: "https://github.com/directus/directus",
4881
4857
  kind: "template",
4882
4858
  logo: "directus",
4883
4859
  category: "cms",
@@ -4891,24 +4867,41 @@ COMMIT;
4891
4867
  {
4892
4868
  name: "directus",
4893
4869
  image: "directus/directus:latest",
4894
- ports: [
4895
- "8055:8055"
4896
- ],
4897
4870
  exposedPort: 8055,
4898
4871
  exposed: true,
4872
+ routes: [
4873
+ {
4874
+ port: 8055
4875
+ }
4876
+ ],
4899
4877
  environment: {
4900
4878
  DB_CLIENT: "sqlite3",
4901
4879
  DB_FILENAME: "/directus/database/data.db",
4902
- PUBLIC_URL: "{{publicUrl:directus}}"
4880
+ PUBLIC_URL: "{{publicUrl:directus}}",
4881
+ ADMIN_EMAIL: "admin@example.com"
4903
4882
  },
4904
4883
  secretEnv: [
4905
- "SECRET"
4884
+ "SECRET",
4885
+ "ADMIN_PASSWORD"
4906
4886
  ],
4907
4887
  volumes: [
4908
4888
  "directus_database:/directus/database",
4909
4889
  "directus_uploads:/directus/uploads"
4910
4890
  ],
4911
- restart: "unless-stopped"
4891
+ healthcheck: {
4892
+ test: [
4893
+ "CMD-SHELL",
4894
+ "wget -q -O /dev/null http://127.0.0.1:8055/server/health || exit 1"
4895
+ ],
4896
+ interval: "15s",
4897
+ timeout: "5s",
4898
+ retries: 8,
4899
+ startPeriod: "30s"
4900
+ },
4901
+ restart: "unless-stopped",
4902
+ ports: [
4903
+ "8211:8055"
4904
+ ]
4912
4905
  }
4913
4906
  ],
4914
4907
  configFields: [
@@ -4919,14 +4912,76 @@ COMMIT;
4919
4912
  help: "Auto-generated. Signs access tokens.",
4920
4913
  generate: "secret",
4921
4914
  secret: true
4915
+ },
4916
+ {
4917
+ key: "ADMIN_PASSWORD",
4918
+ service: "directus",
4919
+ label: "Admin password",
4920
+ help: "Auto-generated. The password for the admin account created on first boot. Directus has no sign-up screen, so without this no account would exist at all.",
4921
+ generate: "secret",
4922
+ secret: true
4923
+ }
4924
+ ],
4925
+ management: {
4926
+ kind: "schema"
4927
+ },
4928
+ connection: {
4929
+ title: "Sign in to Directus",
4930
+ description: "The admin account below is created automatically on the first boot. Change the password after your first sign-in.",
4931
+ guide: {
4932
+ intro: "A headless CMS plus a REST and GraphQL API over whatever collections you create.",
4933
+ defaultMode: "public"
4934
+ },
4935
+ outputs: [
4936
+ {
4937
+ id: "admin",
4938
+ label: "Admin app",
4939
+ source: "template:{{env:directus:PUBLIC_URL}}/admin",
4940
+ kind: "url",
4941
+ recommended: true,
4942
+ help: "The Directus admin app. Sign in with the email and password below."
4943
+ },
4944
+ {
4945
+ id: "url",
4946
+ label: "API URL",
4947
+ source: "publicUrl:directus",
4948
+ kind: "url",
4949
+ envKey: "DIRECTUS_URL",
4950
+ help: "Base URL for the REST + GraphQL API."
4951
+ },
4952
+ {
4953
+ id: "email",
4954
+ label: "Admin email",
4955
+ source: "env:directus:ADMIN_EMAIL",
4956
+ width: "half"
4957
+ },
4958
+ {
4959
+ id: "password",
4960
+ label: "Admin password",
4961
+ source: "env:directus:ADMIN_PASSWORD",
4962
+ secret: true,
4963
+ width: "half"
4964
+ }
4965
+ ],
4966
+ firstLogin: {
4967
+ note: "The admin is created only on the FIRST boot against an empty database. If you reinstall over an existing directus_database volume no new admin is made \u2014 reset one with `npx directus users passwd` inside the container."
4968
+ }
4969
+ },
4970
+ endpoints: [
4971
+ {
4972
+ service: "directus",
4973
+ port: 8055,
4974
+ label: "Directus",
4975
+ kind: "http",
4976
+ defaultMode: "domain"
4922
4977
  }
4923
4978
  ]
4924
4979
  },
4925
4980
  {
4926
- available: false,
4981
+ available: true,
4927
4982
  id: "nocodb",
4928
4983
  name: "NocoDB",
4929
- description: "Airtable-style spreadsheet UI over an SQL database. The first sign-up becomes the admin.",
4984
+ description: "Airtable-style spreadsheet UI over an SQL database. Sign in with the admin account below.",
4930
4985
  kind: "template",
4931
4986
  logo: "nocodb",
4932
4987
  category: "database",
@@ -4940,23 +4995,73 @@ COMMIT;
4940
4995
  {
4941
4996
  name: "nocodb",
4942
4997
  image: "nocodb/nocodb:latest",
4943
- ports: [
4944
- "8080:8080"
4945
- ],
4946
4998
  exposedPort: 8080,
4947
4999
  exposed: true,
4948
5000
  volumes: [
4949
5001
  "nocodb_data:/usr/app/data"
4950
5002
  ],
4951
- restart: "unless-stopped"
5003
+ restart: "unless-stopped",
5004
+ environment: {
5005
+ NC_ADMIN_EMAIL: "admin@example.com"
5006
+ },
5007
+ secretEnv: [
5008
+ "NC_ADMIN_PASSWORD"
5009
+ ],
5010
+ ports: [
5011
+ "8208:8080"
5012
+ ]
4952
5013
  }
4953
- ]
5014
+ ],
5015
+ configFields: [
5016
+ {
5017
+ key: "NC_ADMIN_PASSWORD",
5018
+ service: "nocodb",
5019
+ label: "Admin password",
5020
+ help: "Auto-generated. Password for the super-admin account created on first boot.",
5021
+ generate: "secret",
5022
+ secret: true
5023
+ }
5024
+ ],
5025
+ connection: {
5026
+ title: "Sign in to NocoDB",
5027
+ description: "The super-admin account below is created on first boot. Change the password after signing in.",
5028
+ guide: {
5029
+ defaultMode: "public"
5030
+ },
5031
+ outputs: [
5032
+ {
5033
+ id: "url",
5034
+ label: "NocoDB",
5035
+ source: "publicUrl:nocodb",
5036
+ kind: "url",
5037
+ recommended: true,
5038
+ help: "Sign in with the email and password below."
5039
+ },
5040
+ {
5041
+ id: "email",
5042
+ label: "Admin email",
5043
+ source: "env:nocodb:NC_ADMIN_EMAIL",
5044
+ width: "half"
5045
+ },
5046
+ {
5047
+ id: "password",
5048
+ label: "Admin password",
5049
+ source: "env:nocodb:NC_ADMIN_PASSWORD",
5050
+ secret: true,
5051
+ width: "half"
5052
+ }
5053
+ ],
5054
+ firstLogin: {
5055
+ note: "Anyone who reaches this URL can still create their own account (they land in their own workspace and cannot see your bases). Turn sign-up off in Account Settings \u2192 Authentication right after your first sign-in \u2014 the NC_INVITE_ONLY_SIGNUP env var no longer works in this version."
5056
+ }
5057
+ },
5058
+ verified: true
4954
5059
  },
4955
5060
  {
4956
- available: false,
5061
+ available: true,
4957
5062
  id: "metabase",
4958
5063
  name: "Metabase",
4959
- description: "Open-source business intelligence \u2014 dashboards and questions over your data.",
5064
+ description: "Open-source business intelligence \u2014 dashboards and questions over your data. Create your admin account in the browser on first visit.",
4960
5065
  kind: "template",
4961
5066
  logo: "metabase",
4962
5067
  category: "analytics",
@@ -4970,20 +5075,55 @@ COMMIT;
4970
5075
  {
4971
5076
  name: "metabase",
4972
5077
  image: "metabase/metabase:latest",
4973
- ports: [
4974
- "3000:3000"
4975
- ],
4976
5078
  exposedPort: 3e3,
4977
5079
  exposed: true,
4978
5080
  environment: {
4979
- MB_DB_FILE: "/metabase-data/metabase.db"
5081
+ MB_DB_FILE: "/metabase-data/metabase.db",
5082
+ MB_SITE_URL: "{{publicUrl:metabase}}"
4980
5083
  },
4981
5084
  volumes: [
4982
5085
  "metabase_data:/metabase-data"
4983
5086
  ],
4984
- restart: "unless-stopped"
5087
+ restart: "unless-stopped",
5088
+ secretEnv: [
5089
+ "MB_ENCRYPTION_SECRET_KEY"
5090
+ ],
5091
+ ports: [
5092
+ "3010:3000"
5093
+ ]
4985
5094
  }
4986
- ]
5095
+ ],
5096
+ configFields: [
5097
+ {
5098
+ key: "MB_ENCRYPTION_SECRET_KEY",
5099
+ service: "metabase",
5100
+ label: "Encryption key",
5101
+ help: "Auto-generated. Encrypts saved database credentials at rest. Never change it after setup.",
5102
+ generate: "secret",
5103
+ secret: true
5104
+ }
5105
+ ],
5106
+ connection: {
5107
+ title: "Set up Metabase",
5108
+ description: "Metabase has no preset login. Open the link and the setup wizard will walk you through creating the admin account.",
5109
+ guide: {
5110
+ defaultMode: "public"
5111
+ },
5112
+ outputs: [
5113
+ {
5114
+ id: "url",
5115
+ label: "Metabase",
5116
+ source: "publicUrl:metabase",
5117
+ kind: "url",
5118
+ recommended: true,
5119
+ help: "First visit runs the setup wizard \u2014 the account you create there is the admin."
5120
+ }
5121
+ ],
5122
+ firstLogin: {
5123
+ note: "Complete the setup wizard immediately: it is unauthenticated, so whoever opens this URL first becomes the admin. Metabase can take a minute to finish migrations on first boot."
5124
+ }
5125
+ },
5126
+ verified: true
4987
5127
  },
4988
5128
  {
4989
5129
  available: true,
@@ -5036,10 +5176,10 @@ COMMIT;
5036
5176
  }
5037
5177
  },
5038
5178
  {
5039
- available: false,
5179
+ available: true,
5040
5180
  id: "gitea",
5041
5181
  name: "Gitea",
5042
- description: "Self-hosted Git with issues, pull requests, and a first-run setup wizard.",
5182
+ description: "Self-hosted Git with issues and pull requests. The admin account below is created for you.",
5043
5183
  kind: "template",
5044
5184
  logo: "gitea",
5045
5185
  category: "other",
@@ -5053,71 +5193,201 @@ COMMIT;
5053
5193
  {
5054
5194
  name: "gitea",
5055
5195
  image: "gitea/gitea:1",
5056
- ports: [
5057
- "3000:3000"
5058
- ],
5059
5196
  exposedPort: 3e3,
5060
5197
  exposed: true,
5061
5198
  environment: {
5062
- GITEA__server__ROOT_URL: "{{publicUrl:gitea}}"
5199
+ GITEA__server__ROOT_URL: "{{publicUrl:gitea}}",
5200
+ GITEA__security__INSTALL_LOCK: "true",
5201
+ GITEA__security__SECRET_KEY: "{{config:GITEA_SECRET_KEY}}",
5202
+ GITEA__database__DB_TYPE: "sqlite3",
5203
+ GITEA__service__DISABLE_REGISTRATION: "true",
5204
+ GITEA_ADMIN_USERNAME: "admin",
5205
+ GITEA_ADMIN_EMAIL: "admin@example.com"
5063
5206
  },
5064
5207
  volumes: [
5065
5208
  "gitea_data:/data"
5066
5209
  ],
5067
- restart: "unless-stopped"
5068
- }
5069
- ]
5070
- },
5071
- {
5072
- available: false,
5073
- id: "code-server",
5074
- name: "code-server",
5075
- description: "Run VS Code in your browser, on your server. Login uses an auto-generated password.",
5076
- kind: "template",
5077
- logo: "code-server",
5078
- category: "other",
5079
- tags: [
5080
- "developer",
5081
- "ide",
5082
- "vscode"
5083
- ],
5084
- framework: "docker-compose",
5085
- services: [
5086
- {
5087
- name: "code-server",
5088
- image: "codercom/code-server:latest",
5089
- ports: [
5090
- "8080:8080"
5091
- ],
5092
- exposedPort: 8080,
5093
- exposed: true,
5210
+ restart: "unless-stopped",
5094
5211
  secretEnv: [
5095
- "PASSWORD"
5096
- ],
5097
- volumes: [
5098
- "code_server_config:/home/coder/.config",
5099
- "code_server_local:/home/coder/.local",
5100
- "code_server_project:/home/coder/project"
5212
+ "GITEA_ADMIN_PASSWORD"
5101
5213
  ],
5102
- restart: "unless-stopped"
5214
+ ports: [
5215
+ "3009:3000"
5216
+ ]
5103
5217
  }
5104
5218
  ],
5105
5219
  configFields: [
5106
5220
  {
5107
- key: "PASSWORD",
5108
- service: "code-server",
5221
+ key: "GITEA_SECRET_KEY",
5222
+ service: "gitea",
5223
+ label: "Secret key",
5224
+ help: "Auto-generated. Signs Gitea's tokens and locks the installer.",
5225
+ generate: "secret",
5226
+ secret: true
5227
+ },
5228
+ {
5229
+ key: "GITEA_ADMIN_PASSWORD",
5230
+ service: "gitea",
5231
+ label: "Admin password",
5232
+ help: "Auto-generated. Password for the seeded admin account.",
5233
+ generate: "secret",
5234
+ secret: true
5235
+ }
5236
+ ],
5237
+ prepare: [
5238
+ {
5239
+ service: "gitea",
5240
+ title: "Create the Gitea admin",
5241
+ description: "Seeds the administrator account so you can sign in immediately.",
5242
+ command: 'su-exec git gitea admin user create --admin --username "$GITEA_ADMIN_USERNAME" --password "$GITEA_ADMIN_PASSWORD" --email "$GITEA_ADMIN_EMAIL" --must-change-password=false 2>&1 || true; echo done',
5243
+ capture: "gitea_admin",
5244
+ phase: "post-ready",
5245
+ readiness: {
5246
+ test: "su-exec git gitea admin user list >/dev/null 2>&1",
5247
+ interval: 3e3,
5248
+ retries: 40
5249
+ }
5250
+ }
5251
+ ],
5252
+ connection: {
5253
+ title: "Sign in to Gitea",
5254
+ description: "Your admin account is created during install. New self-registration is disabled \u2014 invite users from Site Administration.",
5255
+ guide: {
5256
+ defaultMode: "public"
5257
+ },
5258
+ outputs: [
5259
+ {
5260
+ id: "url",
5261
+ label: "Gitea",
5262
+ source: "publicUrl:gitea",
5263
+ kind: "url",
5264
+ recommended: true,
5265
+ help: "Sign in with the username and password below."
5266
+ },
5267
+ {
5268
+ id: "username",
5269
+ label: "Admin username",
5270
+ source: "env:gitea:GITEA_ADMIN_USERNAME",
5271
+ width: "half"
5272
+ },
5273
+ {
5274
+ id: "password",
5275
+ label: "Admin password",
5276
+ source: "env:gitea:GITEA_ADMIN_PASSWORD",
5277
+ secret: true,
5278
+ width: "half"
5279
+ }
5280
+ ],
5281
+ firstLogin: {
5282
+ note: "INSTALL_LOCK is on, so the public setup wizard is disabled and cannot be used to hijack the instance. The password above works as-is."
5283
+ }
5284
+ },
5285
+ verified: true
5286
+ },
5287
+ {
5288
+ available: true,
5289
+ verified: true,
5290
+ id: "code-server",
5291
+ name: "code-server",
5292
+ description: "Run VS Code in your browser, on your server. Sign in with the auto-generated password below.",
5293
+ repository: "https://github.com/coder/code-server",
5294
+ kind: "template",
5295
+ logo: "code-server",
5296
+ category: "other",
5297
+ tags: [
5298
+ "developer",
5299
+ "ide",
5300
+ "vscode"
5301
+ ],
5302
+ framework: "docker-compose",
5303
+ services: [
5304
+ {
5305
+ name: "code-server",
5306
+ image: "codercom/code-server:4.132.0",
5307
+ exposedPort: 8080,
5308
+ exposed: true,
5309
+ routes: [
5310
+ {
5311
+ port: 8080
5312
+ }
5313
+ ],
5314
+ secretEnv: [
5315
+ "PASSWORD"
5316
+ ],
5317
+ volumes: [
5318
+ "code_server_home:/home/coder"
5319
+ ],
5320
+ healthcheck: {
5321
+ test: [
5322
+ "CMD-SHELL",
5323
+ "curl -fsS -o /dev/null http://127.0.0.1:8080/healthz || exit 1"
5324
+ ],
5325
+ interval: "15s",
5326
+ timeout: "5s",
5327
+ retries: 6,
5328
+ startPeriod: "20s"
5329
+ },
5330
+ restart: "unless-stopped",
5331
+ ports: [
5332
+ "8207:8080"
5333
+ ]
5334
+ }
5335
+ ],
5336
+ configFields: [
5337
+ {
5338
+ key: "PASSWORD",
5339
+ service: "code-server",
5109
5340
  label: "Login password",
5110
- help: "Auto-generated. Required to sign in.",
5341
+ help: "Auto-generated. Required to sign in. Uses PASSWORD (plaintext) rather than HASHED_PASSWORD, which expects an argon2 digest.",
5111
5342
  generate: "secret",
5112
5343
  secret: true
5113
5344
  }
5345
+ ],
5346
+ management: {
5347
+ kind: "schema"
5348
+ },
5349
+ connection: {
5350
+ title: "Sign in to code-server",
5351
+ description: "VS Code in the browser. Sign in with the generated password below.",
5352
+ guide: {
5353
+ intro: "A full VS Code editor running on your server, reachable from any browser.",
5354
+ defaultMode: "public"
5355
+ },
5356
+ outputs: [
5357
+ {
5358
+ id: "url",
5359
+ label: "Editor",
5360
+ source: "publicUrl:code-server",
5361
+ kind: "url",
5362
+ recommended: true,
5363
+ help: "Opens the browser IDE. Sign in with the password below."
5364
+ },
5365
+ {
5366
+ id: "password",
5367
+ label: "Login password",
5368
+ source: "env:code-server:PASSWORD",
5369
+ secret: true
5370
+ }
5371
+ ],
5372
+ firstLogin: {
5373
+ note: "Your whole home directory is the persisted volume, so files, extensions and settings all survive a redeploy. The editor opens /home/coder by default."
5374
+ }
5375
+ },
5376
+ endpoints: [
5377
+ {
5378
+ service: "code-server",
5379
+ port: 8080,
5380
+ label: "Editor",
5381
+ kind: "http",
5382
+ defaultMode: "domain"
5383
+ }
5114
5384
  ]
5115
5385
  },
5116
5386
  {
5117
- available: false,
5387
+ available: true,
5118
5388
  id: "uptime-kuma",
5119
5389
  name: "Uptime Kuma",
5120
- description: "Self-hosted uptime monitoring with status pages and alerts.",
5390
+ description: "Self-hosted uptime monitoring with status pages and alerts. Create your admin account on the first visit.",
5121
5391
  kind: "template",
5122
5392
  logo: "uptime-kuma",
5123
5393
  category: "other",
@@ -5131,23 +5401,44 @@ COMMIT;
5131
5401
  {
5132
5402
  name: "uptime-kuma",
5133
5403
  image: "louislam/uptime-kuma:1",
5134
- ports: [
5135
- "3001:3001"
5136
- ],
5137
5404
  exposedPort: 3001,
5138
5405
  exposed: true,
5139
5406
  volumes: [
5140
5407
  "uptime_kuma_data:/app/data"
5141
5408
  ],
5142
- restart: "unless-stopped"
5409
+ restart: "unless-stopped",
5410
+ ports: [
5411
+ "3008:3001"
5412
+ ]
5143
5413
  }
5144
- ]
5414
+ ],
5415
+ connection: {
5416
+ title: "Set up Uptime Kuma",
5417
+ description: "Uptime Kuma ships with no default login \u2014 the first page you see creates the admin account.",
5418
+ guide: {
5419
+ defaultMode: "public"
5420
+ },
5421
+ outputs: [
5422
+ {
5423
+ id: "url",
5424
+ label: "Uptime Kuma",
5425
+ source: "publicUrl:uptime-kuma",
5426
+ kind: "url",
5427
+ recommended: true,
5428
+ help: "First visit prompts you to create the administrator account."
5429
+ }
5430
+ ],
5431
+ firstLogin: {
5432
+ note: "Do this immediately after deploy: until you create the admin, anyone who opens this URL can claim the instance. Monitors and history live in the uptime_kuma_data volume."
5433
+ }
5434
+ },
5435
+ verified: true
5145
5436
  },
5146
5437
  {
5147
- available: false,
5438
+ available: true,
5148
5439
  id: "vaultwarden",
5149
5440
  name: "Vaultwarden",
5150
- description: "Lightweight self-hosted password manager (Bitwarden-compatible).",
5441
+ description: "Lightweight self-hosted password manager (Bitwarden-compatible). Open the admin panel with the token below to invite your first account.",
5151
5442
  kind: "template",
5152
5443
  logo: "vaultwarden",
5153
5444
  category: "other",
@@ -5161,26 +5452,75 @@ COMMIT;
5161
5452
  {
5162
5453
  name: "vaultwarden",
5163
5454
  image: "vaultwarden/server:latest",
5164
- ports: [
5165
- "80:80"
5166
- ],
5167
5455
  exposedPort: 80,
5168
5456
  exposed: true,
5169
5457
  environment: {
5170
- DOMAIN: "{{publicUrl:vaultwarden}}"
5458
+ DOMAIN: "{{publicUrl:vaultwarden}}",
5459
+ SIGNUPS_ALLOWED: "false"
5171
5460
  },
5172
5461
  volumes: [
5173
5462
  "vaultwarden_data:/data"
5174
5463
  ],
5175
- restart: "unless-stopped"
5464
+ restart: "unless-stopped",
5465
+ secretEnv: [
5466
+ "ADMIN_TOKEN"
5467
+ ],
5468
+ ports: [
5469
+ "8206:80"
5470
+ ]
5176
5471
  }
5177
- ]
5472
+ ],
5473
+ configFields: [
5474
+ {
5475
+ key: "ADMIN_TOKEN",
5476
+ service: "vaultwarden",
5477
+ label: "Admin panel token",
5478
+ help: "Auto-generated. Unlocks /admin, where you invite your own account.",
5479
+ generate: "secret",
5480
+ secret: true
5481
+ }
5482
+ ],
5483
+ connection: {
5484
+ title: "Set up Vaultwarden",
5485
+ description: "Public sign-up is disabled. Open the admin panel with the token below, invite your own email address, then register that address in any Bitwarden client.",
5486
+ guide: {
5487
+ defaultMode: "public"
5488
+ },
5489
+ outputs: [
5490
+ {
5491
+ id: "admin",
5492
+ label: "Admin panel",
5493
+ source: "template:{{env:vaultwarden:DOMAIN}}/admin",
5494
+ kind: "url",
5495
+ recommended: true,
5496
+ help: "Paste the admin token below to sign in, then use Invite User."
5497
+ },
5498
+ {
5499
+ id: "url",
5500
+ label: "Vault URL",
5501
+ source: "publicUrl:vaultwarden",
5502
+ kind: "url",
5503
+ help: "Point the Bitwarden app/extension at this as its self-hosted server URL."
5504
+ },
5505
+ {
5506
+ id: "adminToken",
5507
+ label: "Admin token",
5508
+ source: "env:vaultwarden:ADMIN_TOKEN",
5509
+ secret: true,
5510
+ help: "Full control of the server. Treat it like a root password."
5511
+ }
5512
+ ],
5513
+ firstLogin: {
5514
+ note: "Invitations work without SMTP: after inviting your address in /admin, register that same address from the vault URL to set your master password."
5515
+ }
5516
+ },
5517
+ verified: true
5178
5518
  },
5179
5519
  {
5180
- available: false,
5520
+ available: true,
5181
5521
  id: "freshrss",
5182
5522
  name: "FreshRSS",
5183
- description: "Self-hosted RSS and Atom feed reader with a first-run setup wizard.",
5523
+ description: "Self-hosted RSS and Atom feed reader. Your account is created during install.",
5184
5524
  kind: "template",
5185
5525
  logo: "freshrss",
5186
5526
  category: "other",
@@ -5194,23 +5534,88 @@ COMMIT;
5194
5534
  {
5195
5535
  name: "freshrss",
5196
5536
  image: "freshrss/freshrss:latest",
5197
- ports: [
5198
- "80:80"
5199
- ],
5200
5537
  exposedPort: 80,
5201
5538
  exposed: true,
5202
5539
  volumes: [
5203
5540
  "freshrss_data:/var/www/FreshRSS/data"
5204
5541
  ],
5205
- restart: "unless-stopped"
5542
+ restart: "unless-stopped",
5543
+ environment: {
5544
+ FRESHRSS_ADMIN_USERNAME: "admin"
5545
+ },
5546
+ secretEnv: [
5547
+ "FRESHRSS_ADMIN_PASSWORD"
5548
+ ],
5549
+ ports: [
5550
+ "8204:80"
5551
+ ]
5206
5552
  }
5207
- ]
5553
+ ],
5554
+ configFields: [
5555
+ {
5556
+ key: "FRESHRSS_ADMIN_PASSWORD",
5557
+ service: "freshrss",
5558
+ label: "Admin password",
5559
+ help: "Auto-generated. Password for the account created during install.",
5560
+ generate: "secret",
5561
+ secret: true
5562
+ }
5563
+ ],
5564
+ prepare: [
5565
+ {
5566
+ service: "freshrss",
5567
+ title: "Install FreshRSS",
5568
+ description: "Runs the headless installer and creates your account.",
5569
+ command: 'cd /var/www/FreshRSS && { [ -f ./data/config.php ] || php ./cli/do-install.php --default-user="$FRESHRSS_ADMIN_USERNAME" --auth-type=form --db-type=sqlite --api-enabled; } && { php ./cli/list-users.php 2>/dev/null | grep -qx "$FRESHRSS_ADMIN_USERNAME" || php ./cli/create-user.php --user "$FRESHRSS_ADMIN_USERNAME" --password "$FRESHRSS_ADMIN_PASSWORD" --language en; } && ./cli/access-permissions.sh >/dev/null 2>&1; echo done',
5570
+ capture: "freshrss_install",
5571
+ phase: "post-ready",
5572
+ readiness: {
5573
+ test: "test -d /var/www/FreshRSS/cli",
5574
+ interval: 3e3,
5575
+ retries: 30
5576
+ }
5577
+ }
5578
+ ],
5579
+ connection: {
5580
+ title: "Sign in to FreshRSS",
5581
+ description: "FreshRSS is installed headlessly during setup \u2014 sign in with the credentials below.",
5582
+ guide: {
5583
+ defaultMode: "public"
5584
+ },
5585
+ outputs: [
5586
+ {
5587
+ id: "url",
5588
+ label: "Reader",
5589
+ source: "publicUrl:freshrss",
5590
+ kind: "url",
5591
+ recommended: true,
5592
+ help: "Sign in with the username and password below."
5593
+ },
5594
+ {
5595
+ id: "username",
5596
+ label: "Username",
5597
+ source: "env:freshrss:FRESHRSS_ADMIN_USERNAME",
5598
+ width: "half"
5599
+ },
5600
+ {
5601
+ id: "password",
5602
+ label: "Password",
5603
+ source: "env:freshrss:FRESHRSS_ADMIN_PASSWORD",
5604
+ secret: true,
5605
+ width: "half"
5606
+ }
5607
+ ],
5608
+ firstLogin: {
5609
+ note: 'Deliberately NOT deployed with the public web installer, so nobody can claim your instance first. Feeds refresh on a schedule only if you set CRON_MIN (e.g. "*/20").'
5610
+ }
5611
+ },
5612
+ verified: true
5208
5613
  },
5209
5614
  {
5210
- available: false,
5615
+ available: true,
5211
5616
  id: "stirling-pdf",
5212
5617
  name: "Stirling PDF",
5213
- description: "Split, merge, convert, OCR and edit PDFs locally. Default login is admin / stirling \u2014 change it.",
5618
+ description: "Split, merge, convert, OCR and edit PDFs locally. Sign in with the admin account below.",
5214
5619
  kind: "template",
5215
5620
  logo: "stirling-pdf",
5216
5621
  category: "other",
@@ -5224,21 +5629,72 @@ COMMIT;
5224
5629
  {
5225
5630
  name: "stirling-pdf",
5226
5631
  image: "stirlingtools/stirling-pdf:latest",
5227
- ports: [
5228
- "8080:8080"
5229
- ],
5230
5632
  exposedPort: 8080,
5231
5633
  exposed: true,
5232
5634
  volumes: [
5233
5635
  "stirling_config:/configs",
5234
5636
  "stirling_tessdata:/usr/share/tessdata"
5235
5637
  ],
5236
- restart: "unless-stopped"
5638
+ restart: "unless-stopped",
5639
+ environment: {
5640
+ SECURITY_ENABLELOGIN: "true",
5641
+ SECURITY_INITIALLOGIN_USERNAME: "admin"
5642
+ },
5643
+ secretEnv: [
5644
+ "SECURITY_INITIALLOGIN_PASSWORD"
5645
+ ],
5646
+ ports: [
5647
+ "8209:8080"
5648
+ ]
5237
5649
  }
5238
- ]
5650
+ ],
5651
+ configFields: [
5652
+ {
5653
+ key: "SECURITY_INITIALLOGIN_PASSWORD",
5654
+ service: "stirling-pdf",
5655
+ label: "Admin password",
5656
+ help: "Auto-generated. Replaces Stirling's public default password.",
5657
+ generate: "secret",
5658
+ secret: true
5659
+ }
5660
+ ],
5661
+ connection: {
5662
+ title: "Sign in to Stirling PDF",
5663
+ description: "Login is enabled and the admin account below is seeded on first boot, so the well-known default password is never used.",
5664
+ guide: {
5665
+ defaultMode: "public"
5666
+ },
5667
+ outputs: [
5668
+ {
5669
+ id: "url",
5670
+ label: "Stirling PDF",
5671
+ source: "publicUrl:stirling-pdf",
5672
+ kind: "url",
5673
+ recommended: true,
5674
+ help: "Sign in with the username and password below."
5675
+ },
5676
+ {
5677
+ id: "username",
5678
+ label: "Admin username",
5679
+ source: "env:stirling-pdf:SECURITY_INITIALLOGIN_USERNAME",
5680
+ width: "half"
5681
+ },
5682
+ {
5683
+ id: "password",
5684
+ label: "Admin password",
5685
+ source: "env:stirling-pdf:SECURITY_INITIALLOGIN_PASSWORD",
5686
+ secret: true,
5687
+ width: "half"
5688
+ }
5689
+ ],
5690
+ firstLogin: {
5691
+ note: "Seeding these two values is what prevents Stirling from creating its documented admin/stirling account. The password above works as-is."
5692
+ }
5693
+ },
5694
+ verified: true
5239
5695
  },
5240
5696
  {
5241
- available: false,
5697
+ available: true,
5242
5698
  id: "it-tools",
5243
5699
  name: "IT-Tools",
5244
5700
  description: "A handy collection of developer and sysadmin utilities. No login, no setup.",
@@ -5255,14 +5711,35 @@ COMMIT;
5255
5711
  {
5256
5712
  name: "it-tools",
5257
5713
  image: "corentinth/it-tools:latest",
5258
- ports: [
5259
- "80:80"
5260
- ],
5261
5714
  exposedPort: 80,
5262
5715
  exposed: true,
5263
- restart: "unless-stopped"
5716
+ restart: "unless-stopped",
5717
+ ports: [
5718
+ "8205:80"
5719
+ ]
5264
5720
  }
5265
- ]
5721
+ ],
5722
+ connection: {
5723
+ title: "Open IT-Tools",
5724
+ description: "A collection of developer and sysadmin utilities. No login, no setup, nothing stored.",
5725
+ guide: {
5726
+ defaultMode: "public"
5727
+ },
5728
+ outputs: [
5729
+ {
5730
+ id: "url",
5731
+ label: "IT-Tools",
5732
+ source: "publicUrl:it-tools",
5733
+ kind: "url",
5734
+ recommended: true,
5735
+ help: "Opens the tool collection. No sign-in required."
5736
+ }
5737
+ ],
5738
+ firstLogin: {
5739
+ note: "Nothing to log into and nothing persisted \u2014 every tool runs in your browser. Anyone who can reach this URL can use it, so keep it internal if that matters."
5740
+ }
5741
+ },
5742
+ verified: true
5266
5743
  },
5267
5744
  {
5268
5745
  available: true,
@@ -5283,14 +5760,34 @@ COMMIT;
5283
5760
  {
5284
5761
  name: "excalidraw",
5285
5762
  image: "excalidraw/excalidraw:latest",
5286
- ports: [
5287
- "8203:80"
5288
- ],
5289
5763
  exposedPort: 80,
5290
5764
  exposed: true,
5291
- restart: "unless-stopped"
5765
+ restart: "unless-stopped",
5766
+ ports: [
5767
+ "8203:80"
5768
+ ]
5292
5769
  }
5293
- ]
5770
+ ],
5771
+ connection: {
5772
+ title: "Open Excalidraw",
5773
+ description: "No login and no server-side storage \u2014 open the link and start drawing.",
5774
+ guide: {
5775
+ defaultMode: "public"
5776
+ },
5777
+ outputs: [
5778
+ {
5779
+ id: "url",
5780
+ label: "Whiteboard",
5781
+ source: "publicUrl:excalidraw",
5782
+ kind: "url",
5783
+ recommended: true,
5784
+ help: "Opens the whiteboard. No sign-in required."
5785
+ }
5786
+ ],
5787
+ firstLogin: {
5788
+ note: "There is nothing to log into and nothing stored on the server: drawings live in your browser, so use Export to save anything you want to keep. Anyone who can reach this URL can use it \u2014 put it behind your own access control if that matters."
5789
+ }
5790
+ }
5294
5791
  },
5295
5792
  {
5296
5793
  available: false,
@@ -5328,10 +5825,11 @@ COMMIT;
5328
5825
  {
5329
5826
  name: "minio",
5330
5827
  image: "minio/minio:latest",
5331
- command: "server /data --console-address :9001",
5332
- ports: [
5333
- "9000:9000",
5334
- "9001:9001"
5828
+ commandArgv: [
5829
+ "server",
5830
+ "/data",
5831
+ "--console-address",
5832
+ ":9001"
5335
5833
  ],
5336
5834
  exposedPort: 9001,
5337
5835
  routes: [
@@ -5353,7 +5851,11 @@ COMMIT;
5353
5851
  volumes: [
5354
5852
  "minio_data:/data"
5355
5853
  ],
5356
- restart: "unless-stopped"
5854
+ restart: "unless-stopped",
5855
+ ports: [
5856
+ "9001:9001",
5857
+ "9000:9000"
5858
+ ]
5357
5859
  }
5358
5860
  ],
5359
5861
  configFields: [
@@ -5394,8 +5896,7 @@ COMMIT;
5394
5896
  test: 'mc alias set local http://127.0.0.1:9000 "$MINIO_ROOT_USER" "$MINIO_ROOT_PASSWORD"',
5395
5897
  interval: 1e3,
5396
5898
  retries: 30
5397
- },
5398
- once: true
5899
+ }
5399
5900
  }
5400
5901
  ],
5401
5902
  endpoints: [
@@ -5476,7 +5977,7 @@ COMMIT;
5476
5977
  },
5477
5978
  {
5478
5979
  available: true,
5479
- verified: true,
5980
+ verified: false,
5480
5981
  id: "kafka",
5481
5982
  name: "Apache Kafka",
5482
5983
  description: "Apache Kafka event-streaming broker (KRaft mode \u2014 no ZooKeeper) with the Kafbat Kafka UI web console for topics, messages, and consumer groups.",
@@ -5521,9 +6022,6 @@ COMMIT;
5521
6022
  image: "ghcr.io/kafbat/kafka-ui:latest",
5522
6023
  exposedPort: 8080,
5523
6024
  exposed: true,
5524
- ports: [
5525
- "8080:8080"
5526
- ],
5527
6025
  routes: [
5528
6026
  {
5529
6027
  port: 8080
@@ -5542,7 +6040,10 @@ COMMIT;
5542
6040
  secretEnv: [
5543
6041
  "SPRING_SECURITY_USER_PASSWORD"
5544
6042
  ],
5545
- restart: "unless-stopped"
6043
+ restart: "unless-stopped",
6044
+ ports: [
6045
+ "8210:8080"
6046
+ ]
5546
6047
  }
5547
6048
  ],
5548
6049
  configFields: [
@@ -5619,9 +6120,6 @@ COMMIT;
5619
6120
  {
5620
6121
  name: "qdrant",
5621
6122
  image: "qdrant/qdrant:v1.18.3",
5622
- ports: [
5623
- "6333:6333"
5624
- ],
5625
6123
  exposedPort: 6333,
5626
6124
  exposed: true,
5627
6125
  routes: [
@@ -5635,7 +6133,10 @@ COMMIT;
5635
6133
  volumes: [
5636
6134
  "qdrant_storage:/qdrant/storage"
5637
6135
  ],
5638
- restart: "unless-stopped"
6136
+ restart: "unless-stopped",
6137
+ ports: [
6138
+ "8215:6333"
6139
+ ]
5639
6140
  }
5640
6141
  ],
5641
6142
  configFields: [
@@ -5697,9 +6198,6 @@ COMMIT;
5697
6198
  {
5698
6199
  name: "meilisearch",
5699
6200
  image: "getmeili/meilisearch:v1.12",
5700
- ports: [
5701
- "7700:7700"
5702
- ],
5703
6201
  exposedPort: 7700,
5704
6202
  exposed: true,
5705
6203
  routes: [
@@ -5727,7 +6225,10 @@ COMMIT;
5727
6225
  retries: 5,
5728
6226
  startPeriod: "10s"
5729
6227
  },
5730
- restart: "unless-stopped"
6228
+ restart: "unless-stopped",
6229
+ ports: [
6230
+ "8214:7700"
6231
+ ]
5731
6232
  }
5732
6233
  ],
5733
6234
  configFields: [
@@ -5745,7 +6246,7 @@ COMMIT;
5745
6246
  },
5746
6247
  connection: {
5747
6248
  title: "Connect to Meilisearch",
5748
- description: "Point a Meilisearch client at the URL with the master key. Every request needs the key as a Bearer token.",
6249
+ description: "Point a Meilisearch client at the URL with the master key. Every request needs the key as a Bearer token. There is no web dashboard in production mode \u2014 drive it from a Meilisearch client or curl.",
5749
6250
  guide: {
5750
6251
  intro: "Your project gets a Meilisearch endpoint plus its master key.",
5751
6252
  useHint: "Read `process.env.MEILISEARCH_URL` and `process.env.MEILISEARCH_KEY` in your code \u2014 set on your next deploy.",
@@ -5766,7 +6267,8 @@ COMMIT;
5766
6267
  ],
5767
6268
  envKey: "MEILISEARCH_URL",
5768
6269
  recommended: true,
5769
- help: "The Meilisearch HTTP endpoint. Switch to Internal for apps on the same project network."
6270
+ help: "HTTP API endpoint \u2014 opening it in a browser returns a JSON status, not a UI (the bundled dashboard is off in production mode). Switch to Internal for apps on the same project network.",
6271
+ kind: "url"
5770
6272
  },
5771
6273
  {
5772
6274
  id: "masterKey",
@@ -5801,14 +6303,14 @@ COMMIT;
5801
6303
  {
5802
6304
  available: true,
5803
6305
  verified: false,
5804
- hosting: "experimental",
5805
6306
  minResources: {
5806
- memoryMb: 8192
6307
+ memoryMb: 4096,
6308
+ cpuCores: 2
5807
6309
  },
5808
6310
  id: "neon",
5809
6311
  name: "Neon",
5810
- description: "Self-hosted Neon \u2014 the serverless-Postgres storage engine (pageserver, 3 safekeepers, storage broker) on S3-backed object storage, with a Neon compute node built from source. EXPERIMENTAL and heavy: ~9 containers, needs roughly 8 GB RAM, and is a test-grade topology, not a production HA cluster. Not for production data.",
5811
- repository: "https://github.com/neondatabase/neon",
6312
+ description: "Self-hosted Neon \u2014 serverless Postgres with database branching, a web console and per-branch connection strings, in a single container. Built on the community `neond` control plane (Apache-2.0), which bundles Neon's pageserver, safekeeper, storage broker and storage controller behind a management API and dashboard. Neon's own cloud console is proprietary and the upstream neon repo ships no web UI, so a community control plane is the only way to run self-hosted Neon with a dashboard. One container with no HA, a ~1.2 GB image, and it publishes fixed host ports for branch endpoints \u2014 sized for development and preview databases rather than critical data.",
6313
+ repository: "https://github.com/matisiekpl/neond",
5812
6314
  kind: "template",
5813
6315
  logo: "neon",
5814
6316
  category: "database",
@@ -5817,159 +6319,87 @@ COMMIT;
5817
6319
  "postgres",
5818
6320
  "postgresql",
5819
6321
  "serverless",
5820
- "sql"
6322
+ "sql",
6323
+ "branching"
5821
6324
  ],
5822
6325
  framework: "docker-compose",
5823
6326
  services: [
5824
6327
  {
5825
- name: "storage_broker",
5826
- image: "ghcr.io/neondatabase/neon:latest",
5827
- command: "storage_broker --listen-addr=0.0.0.0:50051",
5828
- restart: "unless-stopped"
5829
- },
5830
- {
5831
- name: "minio",
5832
- image: "minio/minio:RELEASE.2025-04-22T22-12-26Z",
5833
- command: "server /data --address :9000 --console-address :9001",
5834
- volumes: [
5835
- "neon_minio_data:/data"
5836
- ],
5837
- restart: "unless-stopped"
5838
- },
5839
- {
5840
- name: "pageserver",
5841
- image: "ghcr.io/neondatabase/neon:latest",
5842
- dependsOn: [
5843
- "storage_broker",
5844
- "minio"
5845
- ],
5846
- environment: {
5847
- AWS_ACCESS_KEY_ID: "{{config:MINIO_ROOT_USER}}",
5848
- AWS_SECRET_ACCESS_KEY: "{{config:MINIO_ROOT_PASSWORD}}"
5849
- },
5850
- secretEnv: [
5851
- "AWS_SECRET_ACCESS_KEY"
5852
- ],
5853
- restart: "unless-stopped"
5854
- },
5855
- {
5856
- name: "safekeeper1",
5857
- image: "ghcr.io/neondatabase/neon:latest",
5858
- command: "safekeeper --listen-pg=safekeeper1:5454 --listen-http=0.0.0.0:7676 --id=1 --broker-endpoint=http://storage_broker:50051 -D /data --remote-storage={endpoint='http://minio:9000',bucket_name='neon',bucket_region='eu-north-1',prefix_in_bucket='/safekeeper/'}",
5859
- dependsOn: [
5860
- "storage_broker",
5861
- "minio"
5862
- ],
5863
- environment: {
5864
- AWS_ACCESS_KEY_ID: "{{config:MINIO_ROOT_USER}}",
5865
- AWS_SECRET_ACCESS_KEY: "{{config:MINIO_ROOT_PASSWORD}}"
5866
- },
5867
- secretEnv: [
5868
- "AWS_SECRET_ACCESS_KEY"
5869
- ],
5870
- restart: "unless-stopped"
5871
- },
5872
- {
5873
- name: "safekeeper2",
5874
- image: "ghcr.io/neondatabase/neon:latest",
5875
- command: "safekeeper --listen-pg=safekeeper2:5454 --listen-http=0.0.0.0:7676 --id=2 --broker-endpoint=http://storage_broker:50051 -D /data --remote-storage={endpoint='http://minio:9000',bucket_name='neon',bucket_region='eu-north-1',prefix_in_bucket='/safekeeper/'}",
5876
- dependsOn: [
5877
- "storage_broker",
5878
- "minio"
5879
- ],
5880
- environment: {
5881
- AWS_ACCESS_KEY_ID: "{{config:MINIO_ROOT_USER}}",
5882
- AWS_SECRET_ACCESS_KEY: "{{config:MINIO_ROOT_PASSWORD}}"
5883
- },
5884
- secretEnv: [
5885
- "AWS_SECRET_ACCESS_KEY"
5886
- ],
5887
- restart: "unless-stopped"
5888
- },
5889
- {
5890
- name: "safekeeper3",
5891
- image: "ghcr.io/neondatabase/neon:latest",
5892
- command: "safekeeper --listen-pg=safekeeper3:5454 --listen-http=0.0.0.0:7676 --id=3 --broker-endpoint=http://storage_broker:50051 -D /data --remote-storage={endpoint='http://minio:9000',bucket_name='neon',bucket_region='eu-north-1',prefix_in_bucket='/safekeeper/'}",
5893
- dependsOn: [
5894
- "storage_broker",
5895
- "minio"
5896
- ],
5897
- environment: {
5898
- AWS_ACCESS_KEY_ID: "{{config:MINIO_ROOT_USER}}",
5899
- AWS_SECRET_ACCESS_KEY: "{{config:MINIO_ROOT_PASSWORD}}"
5900
- },
5901
- secretEnv: [
5902
- "AWS_SECRET_ACCESS_KEY"
5903
- ],
5904
- restart: "unless-stopped"
5905
- },
5906
- {
5907
- name: "compute",
5908
- build: {
5909
- dockerfile: `FROM ghcr.io/neondatabase/compute-node-v16:latest
5910
-
5911
- USER root
5912
- RUN echo 'Acquire::Retries "5";' > /etc/apt/apt.conf.d/80-retries && \\
5913
- apt-get update && \\
5914
- apt-get install -y curl jq netcat-openbsd && \\
5915
- rm -rf /var/lib/apt/lists/*
5916
-
5917
- COPY compute/compute.sh /shell/compute.sh
5918
- RUN chmod +x /shell/compute.sh
5919
-
5920
- USER postgres
5921
- ENTRYPOINT ["/shell/compute.sh"]
5922
- `,
5923
- files: [
5924
- {
5925
- path: "compute.sh",
5926
- content: '#!/usr/bin/env bash\nset -eux\n\n# Generate a random tenant or timeline ID\n#\n# Takes a variable name as argument. The result is stored in that variable.\ngenerate_id() {\n local -n resvar=${1}\n printf -v resvar \'%08x%08x%08x%08x\' ${SRANDOM} ${SRANDOM} ${SRANDOM} ${SRANDOM}\n}\n\nPG_VERSION=${PG_VERSION:-16}\n\nreadonly CONFIG_FILE_ORG=/var/db/postgres/configs/config.json\nreadonly CONFIG_FILE=/tmp/config.json\n\necho "Waiting pageserver become ready."\nwhile ! nc -z pageserver 6400; do\n sleep 1\ndone\necho "Page server is ready."\n\ncp "${CONFIG_FILE_ORG}" "${CONFIG_FILE}"\n\nif [[ -n "${TENANT_ID:-}" && -n "${TIMELINE_ID:-}" ]]; then\n tenant_id=${TENANT_ID}\n timeline_id=${TIMELINE_ID}\nelse\n echo "Check if a tenant present"\n PARAMS=(\n -X GET\n -H "Content-Type: application/json"\n "http://pageserver:9898/v1/tenant"\n )\n tenant_id=$(curl "${PARAMS[@]}" | jq -r .[0].id)\n if [[ -z "${tenant_id}" || "${tenant_id}" = null ]]; then\n echo "Create a tenant"\n generate_id tenant_id\n PARAMS=(\n -X PUT\n -H "Content-Type: application/json"\n -d "{\\"mode\\": \\"AttachedSingle\\", \\"generation\\": 1, \\"tenant_conf\\": {}}"\n "http://pageserver:9898/v1/tenant/${tenant_id}/location_config"\n )\n result=$(curl "${PARAMS[@]}")\n printf \'%s\\n\' "${result}" | jq .\n fi\n\n echo "Check if a timeline present"\n PARAMS=(\n -X GET\n -H "Content-Type: application/json"\n "http://pageserver:9898/v1/tenant/${tenant_id}/timeline"\n )\n timeline_id=$(curl "${PARAMS[@]}" | jq -r .[0].timeline_id)\n if [[ -z "${timeline_id}" || "${timeline_id}" = null ]]; then\n generate_id timeline_id\n PARAMS=(\n -sbf\n -X POST\n -H "Content-Type: application/json"\n -d "{\\"new_timeline_id\\": \\"${timeline_id}\\", \\"pg_version\\": ${PG_VERSION}}"\n "http://pageserver:9898/v1/tenant/${tenant_id}/timeline/"\n )\n result=$(curl "${PARAMS[@]}")\n printf \'%s\\n\' "${result}" | jq .\n fi\nfi\n\necho "Overwrite tenant id and timeline id in spec file"\nsed -i "s|TENANT_ID|${tenant_id}|" ${CONFIG_FILE}\nsed -i "s|TIMELINE_ID|${timeline_id}|" ${CONFIG_FILE}\n\ncat ${CONFIG_FILE}\n\necho "Start compute node"\n/usr/local/bin/compute_ctl --pgdata /var/db/postgres/compute \\\n -C "postgresql://cloud_admin@localhost:55433/postgres" \\\n -b /usr/local/bin/postgres \\\n --compute-id "compute-${RANDOM}" \\\n --config "${CONFIG_FILE}"\n'
5927
- }
5928
- ]
5929
- },
5930
- dependsOn: [
5931
- "pageserver",
5932
- "safekeeper1",
5933
- "safekeeper2",
5934
- "safekeeper3"
6328
+ name: "neond",
6329
+ image: "neond/neond:f04d396c133d81e28cf52560ea11ef7e9b814d71",
6330
+ exposed: true,
6331
+ exposedPort: 3e3,
6332
+ routes: [
6333
+ {
6334
+ port: 3e3
6335
+ }
5935
6336
  ],
5936
6337
  environment: {
5937
- PG_VERSION: "16"
6338
+ PORT: "3000",
6339
+ PORT_RANGE: "55432-55437",
6340
+ DO_NOT_TRACK: "1",
6341
+ TELEMETRY_DISABLED: "1",
6342
+ RUST_LOG: "info"
5938
6343
  },
5939
6344
  ports: [
5940
- "55433:55433"
6345
+ "8220:3000",
6346
+ "0.0.0.0:55432:55432",
6347
+ "0.0.0.0:55433:55433",
6348
+ "0.0.0.0:55434:55434",
6349
+ "0.0.0.0:55435:55435",
6350
+ "0.0.0.0:55436:55436",
6351
+ "0.0.0.0:55437:55437"
5941
6352
  ],
5942
6353
  volumes: [
5943
- "neon_compute_data:/var/db/postgres/compute"
6354
+ "neond_data:/neond"
5944
6355
  ],
5945
6356
  healthcheck: {
5946
6357
  test: [
5947
- "CMD-SHELL",
5948
- "pg_isready -h 127.0.0.1 -p 55433 -U cloud_admin || exit 1"
6358
+ "CMD",
6359
+ "curl",
6360
+ "-fsS",
6361
+ "http://127.0.0.1:3000/api/auth/setup"
5949
6362
  ],
5950
- interval: "10s",
6363
+ interval: "30s",
5951
6364
  timeout: "5s",
5952
- retries: 10,
5953
- startPeriod: "90s"
6365
+ retries: 3,
6366
+ startPeriod: "5m"
5954
6367
  },
6368
+ stopGracePeriod: "10m",
5955
6369
  restart: "unless-stopped"
5956
6370
  }
5957
6371
  ],
5958
6372
  configFields: [
5959
6373
  {
5960
- key: "MINIO_ROOT_USER",
5961
- service: "minio",
5962
- label: "Object-storage access key",
5963
- help: "The S3 access key the Neon storage layer uses against its bundled MinIO.",
6374
+ key: "SERVER_SECRET",
6375
+ service: "neond",
6376
+ label: "Server secret",
6377
+ help: "Auto-generated, and PERMANENT \u2014 it is also the password of the internal management Postgres role, so changing it after the first launch makes the control plane unable to open its own database.",
6378
+ generate: "secret",
6379
+ secret: true
6380
+ },
6381
+ {
6382
+ key: "ADMIN_EMAIL",
6383
+ service: "neond",
6384
+ label: "Console admin email",
6385
+ help: "The first account is created for you and is the instance admin. Sign-up closes as soon as it exists.",
5964
6386
  type: "text",
5965
- default: "neon",
6387
+ default: "admin@openship.local",
5966
6388
  required: true
5967
6389
  },
5968
6390
  {
5969
- key: "MINIO_ROOT_PASSWORD",
5970
- service: "minio",
5971
- label: "Object-storage secret key",
5972
- help: "Auto-generated. The S3 secret key shared by the pageserver and safekeepers.",
6391
+ key: "ADMIN_PASSWORD",
6392
+ service: "neond",
6393
+ label: "Console admin password",
6394
+ help: "Auto-generated. Use it with the admin email to sign in to the console.",
6395
+ generate: "secret",
6396
+ secret: true
6397
+ },
6398
+ {
6399
+ key: "PG_PASSWORD",
6400
+ service: "neond",
6401
+ label: "Database password",
6402
+ help: "Auto-generated. Set as the password of the `postgres` role on the first branch so the connection string below is usable immediately.",
5973
6403
  generate: "secret",
5974
6404
  secret: true
5975
6405
  }
@@ -5979,100 +6409,88 @@ ENTRYPOINT ["/shell/compute.sh"]
5979
6409
  },
5980
6410
  prepare: [
5981
6411
  {
5982
- service: "minio",
5983
- command: 'mc alias set local http://127.0.0.1:9000 "$MINIO_ROOT_USER" "$MINIO_ROOT_PASSWORD" > /dev/null && mc mb --ignore-existing --region eu-north-1 local/neon > /dev/null && printf %s neon',
5984
- capture: "bucket",
6412
+ service: "neond",
6413
+ title: "Create the console account and first branch",
6414
+ description: "Signs in (registering the admin on a first install), then ensures an organization, project and branch exist and an endpoint is running, and reports its port. Every run reports a REAL port, so a first attempt that raced the endpoint cannot leave the connection string permanently blank.",
6415
+ command: `set -e; API=http://127.0.0.1:3000/api; J='Content-Type: application/json'; id1() { sed -n 's/.*"id":"\\([^"]*\\)".*/\\1/p' | head -1; }; port1() { sed -n -e 's|.*"connection_string":"[^"]*@[^:"]*:\\([0-9][0-9]*\\)/.*|\\1|p' -e 's/.*"port":\\([0-9][0-9]*\\).*/\\1/p' | head -1; }; get() { curl -fsS -H "$A" "$1" 2>/dev/null || printf ''; }; post() { n=0; while [ $n -lt 20 ]; do r=$(curl -fsS -X POST "$1" -H "$J" -H "$A" -d "$2" 2>/dev/null || printf ''); [ -n "$r" ] && { printf %s "$r"; return 0; }; n=$((n+1)); sleep 2; done; printf ''; }; SETUP=$(curl -fsS "$API/auth/setup" || printf ''); case "$SETUP" in *'"registration_open":true'*) TOKEN=$(curl -fsS -X POST "$API/auth/register" -H "$J" -d "{\\"name\\":\\"Admin\\",\\"email\\":\\"$ADMIN_EMAIL\\",\\"password\\":\\"$ADMIN_PASSWORD\\"}" 2>/dev/null | sed -n 's/.*"token":"\\([^"]*\\)".*/\\1/p') ;; *) TOKEN=$(curl -fsS -X POST "$API/auth/login" -H "$J" -d "{\\"email\\":\\"$ADMIN_EMAIL\\",\\"password\\":\\"$ADMIN_PASSWORD\\"}" 2>/dev/null | sed -n 's/.*"token":"\\([^"]*\\)".*/\\1/p') ;; esac; [ -n "$TOKEN" ] || { echo "pg_port=no-token"; exit 0; }; A="Authorization: Bearer $TOKEN"; ORG=$(get "$API/organizations" | id1); [ -n "$ORG" ] || ORG=$(post "$API/organizations" '{"name":"Default"}' | id1); [ -n "$ORG" ] || { echo "pg_port=no-org"; exit 0; }; P="$API/organizations/$ORG/projects"; PROJ=$(get "$P" | id1); [ -n "$PROJ" ] || PROJ=$(post "$P" '{"name":"main"}' | id1); [ -n "$PROJ" ] || { echo "pg_port=no-project"; exit 0; }; B="$P/$PROJ/branches"; BRJSON=$(get "$B"); BR=$(printf %s "$BRJSON" | id1); if [ -z "$BR" ]; then BR=$(post "$B" '{"name":"production"}' | id1); [ -n "$BR" ] || { echo "pg_port=branch-failed"; exit 0; }; curl -fsS -X PUT "$B/$BR/password" -H "$J" -H "$A" -d "{\\"password\\":\\"$PG_PASSWORD\\"}" >/dev/null 2>&1 || true; fi; PORT=$(printf %s "$BRJSON" | port1); [ -n "$PORT" ] || PORT=$(post "$B/$BR/endpoint" '' | port1); echo "pg_port=\${PORT:-unstarted}"`,
6416
+ capture: "pgPort",
6417
+ capturePattern: "pg_port=([0-9]+)",
6418
+ persistAs: {
6419
+ key: "NEOND_PG_PORT"
6420
+ },
6421
+ once: true,
5985
6422
  phase: "post-ready",
5986
6423
  readiness: {
5987
- test: 'mc alias set local http://127.0.0.1:9000 "$MINIO_ROOT_USER" "$MINIO_ROOT_PASSWORD"',
5988
- interval: 1e3,
5989
- retries: 30
5990
- },
5991
- once: true
6424
+ test: "curl -fsS http://127.0.0.1:3000/api/auth/setup",
6425
+ interval: 5e3,
6426
+ retries: 60
6427
+ }
5992
6428
  }
5993
6429
  ],
5994
6430
  connection: {
5995
- title: "Connect to Neon",
5996
- description: "Point a Postgres driver at the compute node. The compute serves the Postgres wire protocol on port 55433 as user cloud_admin.",
6431
+ title: "Open the Neon console",
6432
+ description: "Sign in to the console to manage projects and branches. The install pre-creates the admin account plus a `production` branch with a running endpoint, so the Postgres URL below works immediately.",
5997
6433
  guide: {
5998
- intro: "Your project gets a Postgres connection served by the Neon compute node.",
5999
- useHint: "Read process.env.DATABASE_URL in your code \u2014 it's set the next time your project deploys.",
6000
- defaultMode: "internal"
6434
+ intro: "You get a Neon console for branching plus a normal Postgres connection string for the first branch.",
6435
+ useHint: "Each branch gets its own endpoint on its own port. Create a branch in the console, press Start endpoint, and copy that branch's connection string \u2014 six host ports (55432-55437) are published for endpoints, which is three concurrent branches.",
6436
+ defaultMode: "public"
6001
6437
  },
6002
6438
  outputs: [
6439
+ {
6440
+ id: "console",
6441
+ label: "Neon console",
6442
+ source: "publicUrl:neond",
6443
+ kind: "url",
6444
+ recommended: true,
6445
+ help: "Sign in with the admin email and password below."
6446
+ },
6447
+ {
6448
+ id: "adminEmail",
6449
+ label: "Admin email",
6450
+ source: "env:neond:ADMIN_EMAIL",
6451
+ help: "The instance admin. Sign-up is closed once this account exists."
6452
+ },
6453
+ {
6454
+ id: "adminPassword",
6455
+ label: "Admin password",
6456
+ source: "env:neond:ADMIN_PASSWORD",
6457
+ secret: true
6458
+ },
6003
6459
  {
6004
6460
  id: "dbUrl",
6005
6461
  label: "Database URL",
6006
- source: "template:postgresql://cloud_admin:cloud_admin@{{host}}:55433/postgres",
6007
- sourceLabel: "Public",
6008
- variants: [
6009
- {
6010
- id: "internal",
6011
- label: "Internal",
6012
- source: "template:postgresql://cloud_admin:cloud_admin@compute:55433/postgres"
6013
- }
6014
- ],
6462
+ source: "template:postgresql://postgres:{{env:neond:PG_PASSWORD}}@{{host}}:{{env:neond:NEOND_PG_PORT}}/postgres?sslmode=require",
6015
6463
  secret: true,
6016
6464
  envKey: "DATABASE_URL",
6017
- recommended: true,
6018
- help: "Postgres connection (cloud_admin). Published on port 55433. Switch to Internal for apps on the same project network."
6019
- }
6020
- ]
6021
- },
6022
- endpoints: [
6023
- {
6024
- service: "compute",
6025
- port: 55433,
6026
- label: "Postgres",
6027
- kind: "tcp"
6028
- }
6029
- ],
6030
- files: [
6031
- {
6032
- service: "pageserver",
6033
- path: "/data/.neon/pageserver.toml",
6034
- content: `broker_endpoint='http://storage_broker:50051'
6035
- pg_distrib_dir='/usr/local/'
6036
- listen_pg_addr='0.0.0.0:6400'
6037
- listen_http_addr='0.0.0.0:9898'
6038
- remote_storage={ endpoint='http://minio:9000', bucket_name='neon', bucket_region='eu-north-1', prefix_in_bucket='/pageserver' }
6039
- control_plane_api='http://0.0.0.0:6666'
6040
- control_plane_emergency_mode=true
6041
- virtual_file_io_mode="buffered"
6042
- `
6043
- },
6044
- {
6045
- service: "pageserver",
6046
- path: "/data/.neon/identity.toml",
6047
- content: "id=1234\n"
6048
- },
6049
- {
6050
- service: "compute",
6051
- path: "/var/db/postgres/configs/config.json",
6052
- content: '{\n "spec": {\n "format_version": 1.0,\n\n "timestamp": "2022-10-12T18:00:00.000Z",\n "operation_uuid": "0f657b36-4b0f-4a2d-9c2e-1dcd615e7d8c",\n "suspend_timeout_seconds": -1,\n\n "cluster": {\n "cluster_id": "docker_compose",\n "name": "docker_compose_test",\n "state": "restarted",\n "roles": [\n {\n "name": "cloud_admin",\n "encrypted_password": "b093c0d3b281ba6da1eacc608620abd8",\n "options": null\n }\n ],\n "databases": [\n ],\n "settings": [\n {\n "name": "fsync",\n "value": "off",\n "vartype": "bool"\n },\n {\n "name": "wal_level",\n "value": "logical",\n "vartype": "enum"\n },\n {\n "name": "wal_log_hints",\n "value": "on",\n "vartype": "bool"\n },\n {\n "name": "log_connections",\n "value": "on",\n "vartype": "bool"\n },\n {\n "name": "port",\n "value": "55433",\n "vartype": "integer"\n },\n {\n "name": "shared_buffers",\n "value": "1MB",\n "vartype": "string"\n },\n {\n "name": "max_connections",\n "value": "100",\n "vartype": "integer"\n },\n {\n "name": "listen_addresses",\n "value": "0.0.0.0",\n "vartype": "string"\n },\n {\n "name": "max_wal_senders",\n "value": "10",\n "vartype": "integer"\n },\n {\n "name": "max_replication_slots",\n "value": "10",\n "vartype": "integer"\n },\n {\n "name": "wal_sender_timeout",\n "value": "5s",\n "vartype": "string"\n },\n {\n "name": "wal_keep_size",\n "value": "0",\n "vartype": "integer"\n },\n {\n "name": "password_encryption",\n "value": "md5",\n "vartype": "enum"\n },\n {\n "name": "restart_after_crash",\n "value": "off",\n "vartype": "bool"\n },\n {\n "name": "synchronous_standby_names",\n "value": "walproposer",\n "vartype": "string"\n },\n {\n "name": "shared_preload_libraries",\n "value": "neon,pg_cron,timescaledb,pg_stat_statements",\n "vartype": "string"\n },\n {\n "name": "neon.safekeepers",\n "value": "safekeeper1:5454,safekeeper2:5454,safekeeper3:5454",\n "vartype": "string"\n },\n {\n "name": "neon.timeline_id",\n "value": "TIMELINE_ID",\n "vartype": "string"\n },\n {\n "name": "neon.tenant_id",\n "value": "TENANT_ID",\n "vartype": "string"\n },\n {\n "name": "neon.pageserver_connstring",\n "value": "host=pageserver port=6400",\n "vartype": "string"\n },\n {\n "name": "max_replication_write_lag",\n "value": "500MB",\n "vartype": "string"\n },\n {\n "name": "max_replication_flush_lag",\n "value": "10GB",\n "vartype": "string"\n },\n {\n "name": "cron.database",\n "value": "postgres",\n "vartype": "string"\n }\n ]\n },\n\n "delta_operations": [\n ]\n },\n "compute_ctl_config": {\n "jwks": {\n "keys": [\n {\n "use": "sig",\n "key_ops": [\n "verify"\n ],\n "alg": "EdDSA",\n "kid": "ZGIxMzAzOGY0YWQwODk2ODU1MTk1NzMxMDFkYmUyOWU2NzZkOWNjNjMyMGRkZGJjOWY0MjdjYWVmNzE1MjUyOAo=",\n "kty": "OKP",\n "crv": "Ed25519",\n "x": "MGQ4ZDFhOTdmNTM0NmUwMDc3ZmJkN2Q0MWE0ZmI3M2NhNWE3YjFjOTNkM2IyYzRkZTQzOGM3MjBkZTk3N2E5ZAo="\n }\n ]\n }\n }\n}\n'
6465
+ help: "The `production` branch endpoint. Neon assigns each endpoint its own port, so if this shows no port the endpoint has not started yet \u2014 open the console and press Start endpoint, then read the connection string there."
6466
+ }
6467
+ ],
6468
+ firstLogin: {
6469
+ note: `The admin account is created during install \u2014 use the email and password above. First boot initialises two embedded Postgres instances and can take a couple of minutes after a ~1.2 GB image pull.
6470
+
6471
+ Two things worth knowing, both upstream behaviour: if the console shows a branch as running but connections fail, stop and start that endpoint from the console \u2014 restarting the container can leave a stale compute lock behind while the API still reports it healthy. And if the container is killed hard (out of memory, power loss) it can refuse to boot with "lease already held"; delete neon_daemon_data/.lock inside the app's volume while nothing is running. An ordinary redeploy is safe \u2014 it shuts down gracefully and releases the lock.`
6053
6472
  }
6054
- ],
6055
- provides: [
6473
+ },
6474
+ endpoints: [
6056
6475
  {
6057
- id: "postgres",
6058
- outputRefs: [
6059
- "dbUrl"
6060
- ],
6061
- category: "database"
6476
+ service: "neond",
6477
+ port: 3e3,
6478
+ label: "Console",
6479
+ kind: "http",
6480
+ defaultMode: "domain"
6062
6481
  }
6063
6482
  ]
6064
6483
  },
6065
6484
  {
6066
6485
  available: true,
6067
6486
  verified: false,
6068
- hosting: "experimental",
6069
6487
  minResources: {
6070
- memoryMb: 8192,
6488
+ memoryMb: 16384,
6071
6489
  cpuCores: 4
6072
6490
  },
6073
6491
  id: "posthog",
6074
6492
  name: "PostHog",
6075
- description: "Self-hosted PostHog \u2014 product analytics, session replay, and feature flags. EXPERIMENTAL and heavy: the hobby stack runs the PostHog app plus Postgres, Redis, ClickHouse, Zookeeper, a Kafka-compatible broker (Redpanda), and MinIO (~9 containers) and wants roughly 4 vCPU / 8\u201316 GB RAM. Not for production.",
6493
+ description: "Self-hosted PostHog \u2014 product analytics, session replay, and feature flags, with real event ingestion. This mirrors PostHog's own hobby topology: a Caddy path router in front of the Django app, the Rust capture/flags/hypercache/persons services, the Node ingestion consumers, ClickHouse + Kafka + Postgres + Redis + Valkey + S3. Heavy: 21 containers, roughly 4 vCPU / 16 GB RAM and 30+ GB disk. PostHog ships no tagged releases for self-hosting and rebuilds `latest` hourly, so this tracks a moving upstream.",
6076
6494
  repository: "https://github.com/PostHog/posthog",
6077
6495
  kind: "template",
6078
6496
  logo: "posthog",
@@ -6086,6 +6504,37 @@ virtual_file_io_mode="buffered"
6086
6504
  ],
6087
6505
  framework: "docker-compose",
6088
6506
  services: [
6507
+ {
6508
+ name: "proxy",
6509
+ image: "caddy:2.10-alpine",
6510
+ exposed: true,
6511
+ exposedPort: 80,
6512
+ routes: [
6513
+ {
6514
+ port: 80
6515
+ }
6516
+ ],
6517
+ dependsOn: [
6518
+ "web",
6519
+ "capture",
6520
+ "replay-capture",
6521
+ "feature-flags",
6522
+ "hypercache-server",
6523
+ "plugins",
6524
+ "objectstorage"
6525
+ ],
6526
+ healthcheck: {
6527
+ test: [
6528
+ "CMD-SHELL",
6529
+ "wget --no-verbose --tries=1 --spider http://127.0.0.1:80/openship-health || exit 1"
6530
+ ],
6531
+ interval: "10s",
6532
+ timeout: "5s",
6533
+ retries: 12,
6534
+ startPeriod: "60s"
6535
+ },
6536
+ restart: "unless-stopped"
6537
+ },
6089
6538
  {
6090
6539
  name: "db",
6091
6540
  image: "postgres:15.12-alpine",
@@ -6105,16 +6554,22 @@ virtual_file_io_mode="buffered"
6105
6554
  "pg_isready -U posthog"
6106
6555
  ],
6107
6556
  interval: "5s",
6108
- timeout: "5s",
6109
- retries: 10,
6557
+ timeout: "30s",
6558
+ retries: 30,
6110
6559
  startPeriod: "10s"
6111
6560
  },
6112
6561
  restart: "unless-stopped"
6113
6562
  },
6114
6563
  {
6115
- name: "redis",
6564
+ name: "redis7",
6116
6565
  image: "redis:7.2-alpine",
6117
- command: "redis-server --maxmemory-policy allkeys-lru --maxmemory 200mb",
6566
+ commandArgv: [
6567
+ "redis-server",
6568
+ "--maxmemory-policy",
6569
+ "allkeys-lru",
6570
+ "--maxmemory",
6571
+ "200mb"
6572
+ ],
6118
6573
  volumes: [
6119
6574
  "posthog_redis:/data"
6120
6575
  ],
@@ -6124,8 +6579,30 @@ virtual_file_io_mode="buffered"
6124
6579
  "redis-cli",
6125
6580
  "ping"
6126
6581
  ],
6127
- interval: "5s",
6128
- timeout: "5s",
6582
+ interval: "3s",
6583
+ timeout: "10s",
6584
+ retries: 10
6585
+ },
6586
+ restart: "unless-stopped"
6587
+ },
6588
+ {
6589
+ name: "valkey",
6590
+ image: "valkey/valkey:8.1-alpine",
6591
+ commandArgv: [
6592
+ "valkey-server",
6593
+ "--maxmemory-policy",
6594
+ "allkeys-lru",
6595
+ "--maxmemory",
6596
+ "200mb"
6597
+ ],
6598
+ healthcheck: {
6599
+ test: [
6600
+ "CMD",
6601
+ "valkey-cli",
6602
+ "ping"
6603
+ ],
6604
+ interval: "3s",
6605
+ timeout: "10s",
6129
6606
  retries: 10
6130
6607
  },
6131
6608
  restart: "unless-stopped"
@@ -6139,41 +6616,90 @@ virtual_file_io_mode="buffered"
6139
6616
  },
6140
6617
  volumes: [
6141
6618
  "posthog_zk_data:/data",
6142
- "posthog_zk_datalog:/datalog"
6619
+ "posthog_zk_datalog:/datalog",
6620
+ "posthog_zk_logs:/logs"
6143
6621
  ],
6622
+ healthcheck: {
6623
+ test: [
6624
+ "CMD-SHELL",
6625
+ "echo ruok | nc -w 2 localhost 2181 | grep -q imok"
6626
+ ],
6627
+ interval: "5s",
6628
+ timeout: "10s",
6629
+ retries: 20,
6630
+ startPeriod: "10s"
6631
+ },
6144
6632
  restart: "unless-stopped"
6145
6633
  },
6146
6634
  {
6147
6635
  name: "kafka",
6148
- image: "docker.io/redpandadata/redpanda:v25.1.9",
6149
- command: "redpanda start --kafka-addr internal://0.0.0.0:9092 --advertise-kafka-addr internal://kafka:9092 --mode dev-container --smp 1 --memory 1G",
6636
+ image: "apache/kafka:4.1.0",
6637
+ environment: {
6638
+ KAFKA_NODE_ID: "1",
6639
+ KAFKA_PROCESS_ROLES: "broker,controller",
6640
+ KAFKA_LISTENERS: "PLAINTEXT://0.0.0.0:9092,CONTROLLER://0.0.0.0:9093",
6641
+ KAFKA_ADVERTISED_LISTENERS: "PLAINTEXT://kafka:9092",
6642
+ KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: "CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT",
6643
+ KAFKA_CONTROLLER_LISTENER_NAMES: "CONTROLLER",
6644
+ KAFKA_INTER_BROKER_LISTENER_NAME: "PLAINTEXT",
6645
+ KAFKA_CONTROLLER_QUORUM_VOTERS: "1@kafka:9093",
6646
+ KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: "1",
6647
+ KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: "1",
6648
+ KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: "1",
6649
+ KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: "0",
6650
+ KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true",
6651
+ KAFKA_NUM_PARTITIONS: "1",
6652
+ KAFKA_DEFAULT_REPLICATION_FACTOR: "1",
6653
+ KAFKA_LOG_RETENTION_HOURS: "1",
6654
+ KAFKA_LOG_DIRS: "/var/lib/kafka/data"
6655
+ },
6150
6656
  volumes: [
6151
- "posthog_kafka:/var/lib/redpanda/data"
6657
+ "posthog_kafka:/var/lib/kafka/data"
6152
6658
  ],
6659
+ healthcheck: {
6660
+ test: [
6661
+ "CMD-SHELL",
6662
+ "/opt/kafka/bin/kafka-broker-api-versions.sh --bootstrap-server localhost:9092 >/dev/null 2>&1 || exit 1"
6663
+ ],
6664
+ interval: "5s",
6665
+ timeout: "10s",
6666
+ retries: 30,
6667
+ startPeriod: "20s"
6668
+ },
6153
6669
  restart: "unless-stopped"
6154
6670
  },
6155
6671
  {
6156
6672
  name: "clickhouse",
6157
- image: "clickhouse/clickhouse-server:24.12",
6673
+ image: "clickhouse/clickhouse-server:26.6.2.158",
6158
6674
  dependsOn: [
6159
- "kafka",
6160
- "zookeeper"
6675
+ "zookeeper",
6676
+ "kafka"
6161
6677
  ],
6162
6678
  environment: {
6163
- CLICKHOUSE_SKIP_USER_SETUP: "1"
6679
+ CLICKHOUSE_SKIP_USER_SETUP: "1",
6680
+ KAFKA_HOSTS: "kafka:9092"
6164
6681
  },
6165
6682
  volumes: [
6166
6683
  "posthog_clickhouse:/var/lib/clickhouse"
6167
6684
  ],
6685
+ healthcheck: {
6686
+ test: [
6687
+ "CMD-SHELL",
6688
+ "wget --no-verbose --tries=1 --spider http://localhost:8123/ping || exit 1"
6689
+ ],
6690
+ interval: "3s",
6691
+ timeout: "10s",
6692
+ retries: 30,
6693
+ startPeriod: "20s"
6694
+ },
6168
6695
  restart: "unless-stopped"
6169
6696
  },
6170
6697
  {
6171
6698
  name: "objectstorage",
6172
- image: "minio/minio:RELEASE.2025-04-22T22-12-26Z",
6173
- command: "server /data --console-address :9001",
6174
- secretEnv: [
6175
- "MINIO_ROOT_PASSWORD"
6176
- ],
6699
+ image: "chrislusf/seaweedfs:4.29",
6700
+ environment: {
6701
+ S3_BUCKET: "posthog,ducklake-dev,ai-blobs"
6702
+ },
6177
6703
  volumes: [
6178
6704
  "posthog_objectstorage:/data"
6179
6705
  ],
@@ -6182,116 +6708,468 @@ virtual_file_io_mode="buffered"
6182
6708
  {
6183
6709
  name: "web",
6184
6710
  image: "posthog/posthog:latest",
6185
- command: "./bin/docker-server",
6711
+ commandArgv: [
6712
+ "sh",
6713
+ "-c",
6714
+ "./bin/migrate && exec ./bin/docker-server"
6715
+ ],
6186
6716
  dependsOn: [
6187
6717
  "db",
6188
- "redis",
6718
+ "redis7",
6189
6719
  "clickhouse",
6190
6720
  "kafka",
6191
- "objectstorage"
6192
- ],
6193
- exposedPort: 8e3,
6194
- exposed: true,
6195
- routes: [
6196
- {
6197
- port: 8e3
6198
- }
6721
+ "objectstorage",
6722
+ "personhog-router"
6199
6723
  ],
6200
6724
  environment: {
6201
6725
  DATABASE_URL: "postgres://posthog:{{config:POSTGRES_PASSWORD}}@db:5432/posthog",
6726
+ PERSONS_DB_WRITER_URL: "postgres://posthog:{{config:POSTGRES_PASSWORD}}@db:5432/posthog",
6727
+ PERSONS_DB_READER_URL: "postgres://posthog:{{config:POSTGRES_PASSWORD}}@db:5432/posthog",
6728
+ PGHOST: "db",
6729
+ PGUSER: "posthog",
6730
+ PGPASSWORD: "{{config:POSTGRES_PASSWORD}}",
6202
6731
  CLICKHOUSE_HOST: "clickhouse",
6203
6732
  CLICKHOUSE_DATABASE: "posthog",
6204
6733
  CLICKHOUSE_SECURE: "false",
6205
6734
  CLICKHOUSE_VERIFY: "false",
6206
- KAFKA_HOSTS: "kafka:9092",
6207
- KAFKA_URL: "kafka://kafka:9092",
6208
- REDIS_URL: "redis://redis:6379/",
6735
+ CLICKHOUSE_API_USER: "api",
6736
+ CLICKHOUSE_API_PASSWORD: "apipass",
6737
+ CLICKHOUSE_APP_USER: "app",
6738
+ CLICKHOUSE_APP_PASSWORD: "apppass",
6739
+ CLICKHOUSE_BILLING_USER: "billing",
6740
+ CLICKHOUSE_BILLING_PASSWORD: "billingpass",
6741
+ CLICKHOUSE_DICT_READER_USER: "dict_reader",
6742
+ CLICKHOUSE_DICT_READER_PASSWORD: "dictreaderpass",
6743
+ CLICKHOUSE_LOGS_CLUSTER_HOST: "clickhouse",
6744
+ CLICKHOUSE_LOGS_CLUSTER_SECURE: "false",
6745
+ REDIS_URL: "redis://redis7:6379/",
6746
+ KAFKA_HOSTS: "kafka",
6747
+ DEPLOYMENT: "hobby",
6209
6748
  SECRET_KEY: "{{config:POSTHOG_SECRET}}",
6210
- OBJECT_STORAGE_ENABLED: "True",
6211
- OBJECT_STORAGE_ENDPOINT: "http://objectstorage:9000",
6212
- OBJECT_STORAGE_ACCESS_KEY_ID: "{{config:MINIO_ROOT_USER}}",
6213
- OBJECT_STORAGE_SECRET_ACCESS_KEY: "{{config:MINIO_ROOT_PASSWORD}}",
6214
- OBJECT_STORAGE_BUCKET: "posthog",
6215
- SITE_URL: "{{publicUrl:web}}",
6216
- DISABLE_SECURE_SSL_REDIRECT: "true",
6749
+ SITE_URL: "{{publicUrl:proxy}}",
6217
6750
  IS_BEHIND_PROXY: "true",
6218
- TRUST_ALL_PROXIES: "true"
6751
+ DISABLE_SECURE_SSL_REDIRECT: "true",
6752
+ OTEL_SDK_DISABLED: "true",
6753
+ OPT_OUT_CAPTURE: "false",
6754
+ FLAGS_REDIS_ENABLED: "false",
6755
+ FEATURE_FLAGS_SERVICE_URL: "http://feature-flags:3001",
6756
+ CDP_API_URL: "http://plugins:6738",
6757
+ RECORDING_API_URL: "http://recording-api:6738",
6758
+ PERSONHOG_ADDR: "personhog-router:50052",
6759
+ PERSONHOG_ENABLED: "true",
6760
+ OBJECT_STORAGE_ENABLED: "true",
6761
+ OBJECT_STORAGE_ENDPOINT: "http://objectstorage:8333",
6762
+ OBJECT_STORAGE_PUBLIC_ENDPOINT: "{{publicUrl:proxy}}",
6763
+ OBJECT_STORAGE_FORCE_PATH_STYLE: "true",
6764
+ OBJECT_STORAGE_BUCKET: "posthog",
6765
+ OBJECT_STORAGE_ACCESS_KEY_ID: "posthog",
6766
+ OBJECT_STORAGE_SECRET_ACCESS_KEY: "posthog",
6767
+ SESSION_RECORDING_V2_S3_ENDPOINT: "http://objectstorage:8333",
6768
+ SESSION_RECORDING_V2_S3_ACCESS_KEY_ID: "any",
6769
+ SESSION_RECORDING_V2_S3_SECRET_ACCESS_KEY: "any"
6219
6770
  },
6220
6771
  secretEnv: [
6221
6772
  "DATABASE_URL",
6222
- "SECRET_KEY",
6223
- "OBJECT_STORAGE_SECRET_ACCESS_KEY"
6773
+ "PERSONS_DB_WRITER_URL",
6774
+ "PERSONS_DB_READER_URL",
6775
+ "PGPASSWORD",
6776
+ "SECRET_KEY"
6224
6777
  ],
6225
6778
  restart: "unless-stopped"
6226
6779
  },
6227
6780
  {
6228
6781
  name: "worker",
6229
6782
  image: "posthog/posthog:latest",
6230
- command: "./bin/docker-worker-celery --with-scheduler",
6783
+ commandArgv: [
6784
+ "./bin/docker-worker-celery",
6785
+ "--with-scheduler"
6786
+ ],
6787
+ dependsOn: [
6788
+ "db",
6789
+ "redis7",
6790
+ "clickhouse",
6791
+ "kafka",
6792
+ "objectstorage",
6793
+ "web",
6794
+ "personhog-router"
6795
+ ],
6796
+ environment: {
6797
+ DATABASE_URL: "postgres://posthog:{{config:POSTGRES_PASSWORD}}@db:5432/posthog",
6798
+ PERSONS_DB_WRITER_URL: "postgres://posthog:{{config:POSTGRES_PASSWORD}}@db:5432/posthog",
6799
+ PERSONS_DB_READER_URL: "postgres://posthog:{{config:POSTGRES_PASSWORD}}@db:5432/posthog",
6800
+ PGHOST: "db",
6801
+ PGUSER: "posthog",
6802
+ PGPASSWORD: "{{config:POSTGRES_PASSWORD}}",
6803
+ CLICKHOUSE_HOST: "clickhouse",
6804
+ CLICKHOUSE_DATABASE: "posthog",
6805
+ CLICKHOUSE_SECURE: "false",
6806
+ CLICKHOUSE_VERIFY: "false",
6807
+ CLICKHOUSE_API_USER: "api",
6808
+ CLICKHOUSE_API_PASSWORD: "apipass",
6809
+ CLICKHOUSE_APP_USER: "app",
6810
+ CLICKHOUSE_APP_PASSWORD: "apppass",
6811
+ CLICKHOUSE_BILLING_USER: "billing",
6812
+ CLICKHOUSE_BILLING_PASSWORD: "billingpass",
6813
+ CLICKHOUSE_DICT_READER_USER: "dict_reader",
6814
+ CLICKHOUSE_DICT_READER_PASSWORD: "dictreaderpass",
6815
+ CLICKHOUSE_LOGS_CLUSTER_HOST: "clickhouse",
6816
+ CLICKHOUSE_LOGS_CLUSTER_SECURE: "false",
6817
+ REDIS_URL: "redis://redis7:6379/",
6818
+ KAFKA_HOSTS: "kafka",
6819
+ DEPLOYMENT: "hobby",
6820
+ POSTHOG_SKIP_MIGRATION_CHECKS: "1",
6821
+ SECRET_KEY: "{{config:POSTHOG_SECRET}}",
6822
+ SITE_URL: "{{publicUrl:proxy}}",
6823
+ IS_BEHIND_PROXY: "true",
6824
+ DISABLE_SECURE_SSL_REDIRECT: "true",
6825
+ OTEL_SDK_DISABLED: "true",
6826
+ FLAGS_REDIS_ENABLED: "false",
6827
+ FEATURE_FLAGS_SERVICE_URL: "http://feature-flags:3001",
6828
+ CDP_API_URL: "http://plugins:6738",
6829
+ RECORDING_API_URL: "http://recording-api:6738",
6830
+ PERSONHOG_ADDR: "personhog-router:50052",
6831
+ PERSONHOG_ENABLED: "true",
6832
+ OBJECT_STORAGE_ENABLED: "true",
6833
+ OBJECT_STORAGE_ENDPOINT: "http://objectstorage:8333",
6834
+ OBJECT_STORAGE_PUBLIC_ENDPOINT: "{{publicUrl:proxy}}",
6835
+ OBJECT_STORAGE_FORCE_PATH_STYLE: "true",
6836
+ OBJECT_STORAGE_BUCKET: "posthog",
6837
+ OBJECT_STORAGE_ACCESS_KEY_ID: "posthog",
6838
+ OBJECT_STORAGE_SECRET_ACCESS_KEY: "posthog",
6839
+ SESSION_RECORDING_V2_S3_ENDPOINT: "http://objectstorage:8333",
6840
+ SESSION_RECORDING_V2_S3_ACCESS_KEY_ID: "any",
6841
+ SESSION_RECORDING_V2_S3_SECRET_ACCESS_KEY: "any"
6842
+ },
6843
+ secretEnv: [
6844
+ "DATABASE_URL",
6845
+ "PERSONS_DB_WRITER_URL",
6846
+ "PERSONS_DB_READER_URL",
6847
+ "PGPASSWORD",
6848
+ "SECRET_KEY"
6849
+ ],
6850
+ restart: "unless-stopped"
6851
+ },
6852
+ {
6853
+ name: "capture",
6854
+ image: "ghcr.io/posthog/posthog/capture:master",
6855
+ dependsOn: [
6856
+ "kafka",
6857
+ "redis7"
6858
+ ],
6859
+ environment: {
6860
+ ADDRESS: "0.0.0.0:3000",
6861
+ CAPTURE_MODE: "events",
6862
+ KAFKA_TOPIC: "events_plugin_ingestion",
6863
+ KAFKA_HOSTS: "kafka:9092",
6864
+ REDIS_URL: "redis://redis7:6379/",
6865
+ RUST_LOG: "info,rdkafka=warn",
6866
+ CAPTURE_V1_SINKS: "msk",
6867
+ CAPTURE_V1_SINK_MSK_KAFKA_HOSTS: "kafka:9092",
6868
+ CAPTURE_V1_SINK_MSK_KAFKA_TOPIC_MAIN: "events_plugin_ingestion",
6869
+ CAPTURE_V1_SINK_MSK_KAFKA_TOPIC_HISTORICAL: "events_plugin_ingestion_historical",
6870
+ CAPTURE_V1_SINK_MSK_KAFKA_TOPIC_OVERFLOW: "events_plugin_ingestion_overflow",
6871
+ CAPTURE_V1_SINK_MSK_KAFKA_TOPIC_DLQ: "events_plugin_ingestion_dlq",
6872
+ CAPTURE_V1_SINK_MSK_KAFKA_TOPIC_EXCEPTION: "ingestion-errortracking-main",
6873
+ CAPTURE_V1_SINK_MSK_KAFKA_TOPIC_HEATMAP: "heatmaps_ingestion",
6874
+ CAPTURE_V1_SINK_MSK_KAFKA_TOPIC_CLIENT_INGESTION_WARNING: "ingestion-clientwarnings-main-1"
6875
+ },
6876
+ restart: "unless-stopped"
6877
+ },
6878
+ {
6879
+ name: "replay-capture",
6880
+ image: "ghcr.io/posthog/posthog/capture:master",
6881
+ dependsOn: [
6882
+ "kafka",
6883
+ "redis7"
6884
+ ],
6885
+ environment: {
6886
+ ADDRESS: "0.0.0.0:3000",
6887
+ CAPTURE_MODE: "recordings",
6888
+ KAFKA_TOPIC: "session_recording_snapshot_item_events",
6889
+ KAFKA_HOSTS: "kafka:9092",
6890
+ REDIS_URL: "redis://redis7:6379/",
6891
+ RUST_LOG: "info,rdkafka=warn"
6892
+ },
6893
+ restart: "unless-stopped"
6894
+ },
6895
+ {
6896
+ name: "ingestion-general",
6897
+ image: "posthog/posthog-node:latest",
6898
+ commandArgv: [
6899
+ "node",
6900
+ "nodejs/dist/index.js"
6901
+ ],
6902
+ dependsOn: [
6903
+ "db",
6904
+ "redis7",
6905
+ "clickhouse",
6906
+ "kafka",
6907
+ "objectstorage",
6908
+ "personhog-router"
6909
+ ],
6910
+ environment: {
6911
+ PLUGIN_SERVER_MODE: "ingestion-v2-combined",
6912
+ DATABASE_URL: "postgres://posthog:{{config:POSTGRES_PASSWORD}}@db:5432/posthog",
6913
+ PERSONS_DATABASE_URL: "postgres://posthog:{{config:POSTGRES_PASSWORD}}@db:5432/posthog",
6914
+ BEHAVIORAL_COHORTS_DATABASE_URL: "postgres://posthog:{{config:POSTGRES_PASSWORD}}@db:5432/posthog",
6915
+ KAFKA_HOSTS: "kafka:9092",
6916
+ REDIS_URL: "redis://redis7:6379/",
6917
+ CLICKHOUSE_HOST: "clickhouse",
6918
+ CLICKHOUSE_DATABASE: "posthog",
6919
+ CLICKHOUSE_SECURE: "false",
6920
+ CLICKHOUSE_VERIFY: "false",
6921
+ COOKIELESS_REDIS_HOST: "redis7",
6922
+ COOKIELESS_REDIS_PORT: "6379",
6923
+ CDP_REDIS_HOST: "redis7",
6924
+ CDP_REDIS_PORT: "6379",
6925
+ CDP_VALKEY_HOST: "valkey",
6926
+ CDP_VALKEY_PORT: "6379",
6927
+ PERSONHOG_ADDR: "personhog-router:50052",
6928
+ PERSONHOG_ENABLED: "true",
6929
+ AI_BLOB_S3_BUCKET: "ai-blobs",
6930
+ AI_BLOB_S3_PREFIX: "aio/",
6931
+ AI_BLOB_S3_ENDPOINT: "http://objectstorage:8333",
6932
+ AI_BLOB_S3_REGION: "us-east-1",
6933
+ AI_BLOB_S3_ACCESS_KEY_ID: "any",
6934
+ AI_BLOB_S3_SECRET_ACCESS_KEY: "any",
6935
+ AI_BLOB_OFFLOAD_TEAMS: "*"
6936
+ },
6937
+ secretEnv: [
6938
+ "DATABASE_URL",
6939
+ "PERSONS_DATABASE_URL",
6940
+ "BEHAVIORAL_COHORTS_DATABASE_URL"
6941
+ ],
6942
+ restart: "unless-stopped"
6943
+ },
6944
+ {
6945
+ name: "ingestion-sessionreplay",
6946
+ image: "posthog/posthog-node:latest",
6947
+ commandArgv: [
6948
+ "node",
6949
+ "nodejs/dist/index.js"
6950
+ ],
6951
+ dependsOn: [
6952
+ "db",
6953
+ "redis7",
6954
+ "kafka",
6955
+ "objectstorage"
6956
+ ],
6957
+ environment: {
6958
+ PLUGIN_SERVER_MODE: "recordings-blob-ingestion-v2",
6959
+ DATABASE_URL: "postgres://posthog:{{config:POSTGRES_PASSWORD}}@db:5432/posthog",
6960
+ KAFKA_HOSTS: "kafka:9092",
6961
+ REDIS_URL: "redis://redis7:6379/",
6962
+ SESSION_RECORDING_V2_S3_ENDPOINT: "http://objectstorage:8333",
6963
+ SESSION_RECORDING_V2_S3_ACCESS_KEY_ID: "any",
6964
+ SESSION_RECORDING_V2_S3_SECRET_ACCESS_KEY: "any",
6965
+ SESSION_RECORDING_V2_S3_TIMEOUT_MS: "120000",
6966
+ CDP_REDIS_HOST: "redis7",
6967
+ CDP_REDIS_PORT: "6379",
6968
+ CDP_VALKEY_HOST: "valkey",
6969
+ CDP_VALKEY_PORT: "6379"
6970
+ },
6971
+ secretEnv: [
6972
+ "DATABASE_URL"
6973
+ ],
6974
+ restart: "unless-stopped"
6975
+ },
6976
+ {
6977
+ name: "recording-api",
6978
+ image: "posthog/posthog-node:latest",
6979
+ commandArgv: [
6980
+ "node",
6981
+ "nodejs/dist/index.js"
6982
+ ],
6983
+ dependsOn: [
6984
+ "db",
6985
+ "redis7",
6986
+ "clickhouse"
6987
+ ],
6988
+ environment: {
6989
+ PLUGIN_SERVER_MODE: "recording-api",
6990
+ DATABASE_URL: "postgres://posthog:{{config:POSTGRES_PASSWORD}}@db:5432/posthog",
6991
+ REDIS_URL: "redis://redis7:6379/",
6992
+ CLICKHOUSE_HOST: "clickhouse",
6993
+ CLICKHOUSE_DATABASE: "posthog",
6994
+ CLICKHOUSE_SECURE: "false",
6995
+ CLICKHOUSE_VERIFY: "false",
6996
+ SESSION_RECORDING_API_REDIS_HOST: "redis7",
6997
+ SESSION_RECORDING_API_REDIS_PORT: "6379",
6998
+ SESSION_RECORDING_V2_S3_ENDPOINT: "http://objectstorage:8333",
6999
+ SESSION_RECORDING_V2_S3_ACCESS_KEY_ID: "any",
7000
+ SESSION_RECORDING_V2_S3_SECRET_ACCESS_KEY: "any",
7001
+ CDP_REDIS_HOST: "redis7",
7002
+ CDP_REDIS_PORT: "6379",
7003
+ CDP_VALKEY_HOST: "valkey",
7004
+ CDP_VALKEY_PORT: "6379"
7005
+ },
7006
+ secretEnv: [
7007
+ "DATABASE_URL"
7008
+ ],
7009
+ restart: "unless-stopped"
7010
+ },
7011
+ {
7012
+ name: "feature-flags",
7013
+ image: "ghcr.io/posthog/posthog/feature-flags:master",
7014
+ dependsOn: [
7015
+ "db",
7016
+ "redis7"
7017
+ ],
7018
+ environment: {
7019
+ ADDRESS: "0.0.0.0:3001",
7020
+ WRITE_DATABASE_URL: "postgres://posthog:{{config:POSTGRES_PASSWORD}}@db:5432/posthog",
7021
+ READ_DATABASE_URL: "postgres://posthog:{{config:POSTGRES_PASSWORD}}@db:5432/posthog",
7022
+ PERSONS_WRITE_DATABASE_URL: "postgres://posthog:{{config:POSTGRES_PASSWORD}}@db:5432/posthog",
7023
+ PERSONS_READ_DATABASE_URL: "postgres://posthog:{{config:POSTGRES_PASSWORD}}@db:5432/posthog",
7024
+ REDIS_URL: "redis://redis7:6379/",
7025
+ COOKIELESS_REDIS_HOST: "redis7",
7026
+ COOKIELESS_REDIS_PORT: "6379",
7027
+ MAXMIND_DB_PATH: "/app/share/GeoLite2-City.mmdb",
7028
+ RUST_LOG: "info"
7029
+ },
7030
+ secretEnv: [
7031
+ "WRITE_DATABASE_URL",
7032
+ "READ_DATABASE_URL",
7033
+ "PERSONS_WRITE_DATABASE_URL",
7034
+ "PERSONS_READ_DATABASE_URL"
7035
+ ],
7036
+ healthcheck: {
7037
+ test: [
7038
+ "CMD",
7039
+ "curl",
7040
+ "-f",
7041
+ "http://localhost:3001/_readiness"
7042
+ ],
7043
+ interval: "5s",
7044
+ timeout: "5s",
7045
+ retries: 12,
7046
+ startPeriod: "10s"
7047
+ },
7048
+ restart: "unless-stopped"
7049
+ },
7050
+ {
7051
+ name: "hypercache-server",
7052
+ image: "ghcr.io/posthog/posthog/hypercache-server:master",
7053
+ dependsOn: [
7054
+ "redis7"
7055
+ ],
7056
+ environment: {
7057
+ ADDRESS: "0.0.0.0:3002",
7058
+ REDIS_URL: "redis://redis7:6379/",
7059
+ RUST_LOG: "info"
7060
+ },
7061
+ healthcheck: {
7062
+ test: [
7063
+ "CMD",
7064
+ "curl",
7065
+ "-f",
7066
+ "http://localhost:3002/_readiness"
7067
+ ],
7068
+ interval: "5s",
7069
+ timeout: "5s",
7070
+ retries: 12,
7071
+ startPeriod: "10s"
7072
+ },
7073
+ restart: "unless-stopped"
7074
+ },
7075
+ {
7076
+ name: "personhog-replica",
7077
+ image: "ghcr.io/posthog/posthog/personhog-replica:master",
7078
+ dependsOn: [
7079
+ "db"
7080
+ ],
7081
+ environment: {
7082
+ GRPC_ADDRESS: "0.0.0.0:50051",
7083
+ PRIMARY_DATABASE_URL: "postgres://posthog:{{config:POSTGRES_PASSWORD}}@db:5432/posthog",
7084
+ METRICS_PORT: "9100",
7085
+ RUST_LOG: "info"
7086
+ },
7087
+ secretEnv: [
7088
+ "PRIMARY_DATABASE_URL"
7089
+ ],
7090
+ restart: "unless-stopped"
7091
+ },
7092
+ {
7093
+ name: "personhog-router",
7094
+ image: "ghcr.io/posthog/posthog/personhog-router:master",
7095
+ dependsOn: [
7096
+ "personhog-replica"
7097
+ ],
7098
+ environment: {
7099
+ GRPC_ADDRESS: "0.0.0.0:50052",
7100
+ REPLICA_URL: "http://personhog-replica:50051",
7101
+ BACKEND_TIMEOUT_MS: "5000",
7102
+ METRICS_PORT: "9101",
7103
+ RUST_LOG: "info"
7104
+ },
7105
+ restart: "unless-stopped"
7106
+ },
7107
+ {
7108
+ name: "property-defs-rs",
7109
+ image: "ghcr.io/posthog/posthog/property-defs-rs:master",
6231
7110
  dependsOn: [
6232
7111
  "db",
6233
- "redis",
6234
- "clickhouse",
6235
- "kafka",
6236
- "objectstorage",
6237
- "web"
7112
+ "kafka"
6238
7113
  ],
6239
7114
  environment: {
6240
7115
  DATABASE_URL: "postgres://posthog:{{config:POSTGRES_PASSWORD}}@db:5432/posthog",
6241
- CLICKHOUSE_HOST: "clickhouse",
6242
- CLICKHOUSE_DATABASE: "posthog",
6243
- CLICKHOUSE_SECURE: "false",
6244
- CLICKHOUSE_VERIFY: "false",
6245
7116
  KAFKA_HOSTS: "kafka:9092",
6246
- KAFKA_URL: "kafka://kafka:9092",
6247
- REDIS_URL: "redis://redis:6379/",
6248
- SECRET_KEY: "{{config:POSTHOG_SECRET}}",
6249
- OBJECT_STORAGE_ENABLED: "True",
6250
- OBJECT_STORAGE_ENDPOINT: "http://objectstorage:9000",
6251
- OBJECT_STORAGE_ACCESS_KEY_ID: "{{config:MINIO_ROOT_USER}}",
6252
- OBJECT_STORAGE_SECRET_ACCESS_KEY: "{{config:MINIO_ROOT_PASSWORD}}",
6253
- OBJECT_STORAGE_BUCKET: "posthog",
6254
- SITE_URL: "{{publicUrl:web}}"
7117
+ SKIP_WRITES: "false",
7118
+ SKIP_READS: "false",
7119
+ FILTER_MODE: "opt-out",
7120
+ RUST_LOG: "info"
6255
7121
  },
6256
7122
  secretEnv: [
6257
- "DATABASE_URL",
6258
- "SECRET_KEY",
6259
- "OBJECT_STORAGE_SECRET_ACCESS_KEY"
7123
+ "DATABASE_URL"
6260
7124
  ],
6261
7125
  restart: "unless-stopped"
6262
7126
  },
6263
7127
  {
6264
7128
  name: "plugins",
6265
- image: "posthog/posthog:latest",
6266
- command: "./bin/plugin-server --no-restart-loop",
7129
+ image: "posthog/posthog-node:latest",
7130
+ commandArgv: [
7131
+ "node",
7132
+ "nodejs/dist/index.js"
7133
+ ],
6267
7134
  dependsOn: [
6268
7135
  "db",
6269
- "redis",
7136
+ "redis7",
7137
+ "valkey",
6270
7138
  "clickhouse",
6271
7139
  "kafka",
6272
- "objectstorage",
6273
- "web"
7140
+ "objectstorage"
6274
7141
  ],
6275
7142
  environment: {
6276
7143
  DATABASE_URL: "postgres://posthog:{{config:POSTGRES_PASSWORD}}@db:5432/posthog",
7144
+ PERSONS_DATABASE_URL: "postgres://posthog:{{config:POSTGRES_PASSWORD}}@db:5432/posthog",
7145
+ BEHAVIORAL_COHORTS_DATABASE_URL: "postgres://posthog:{{config:POSTGRES_PASSWORD}}@db:5432/posthog",
7146
+ CYCLOTRON_DATABASE_URL: "postgres://posthog:{{config:POSTGRES_PASSWORD}}@db:5432/posthog",
7147
+ KAFKA_HOSTS: "kafka:9092",
7148
+ REDIS_URL: "redis://redis7:6379/",
6277
7149
  CLICKHOUSE_HOST: "clickhouse",
6278
7150
  CLICKHOUSE_DATABASE: "posthog",
6279
7151
  CLICKHOUSE_SECURE: "false",
6280
7152
  CLICKHOUSE_VERIFY: "false",
6281
- KAFKA_HOSTS: "kafka:9092",
6282
- KAFKA_URL: "kafka://kafka:9092",
6283
- REDIS_URL: "redis://redis:6379/",
7153
+ SITE_URL: "{{publicUrl:proxy}}",
6284
7154
  SECRET_KEY: "{{config:POSTHOG_SECRET}}",
6285
- OBJECT_STORAGE_ENABLED: "True",
6286
- OBJECT_STORAGE_ENDPOINT: "http://objectstorage:9000",
6287
- OBJECT_STORAGE_ACCESS_KEY_ID: "{{config:MINIO_ROOT_USER}}",
6288
- OBJECT_STORAGE_SECRET_ACCESS_KEY: "{{config:MINIO_ROOT_PASSWORD}}",
6289
- OBJECT_STORAGE_BUCKET: "posthog"
7155
+ CDP_REDIS_HOST: "redis7",
7156
+ CDP_REDIS_PORT: "6379",
7157
+ CDP_VALKEY_HOST: "valkey",
7158
+ CDP_VALKEY_PORT: "6379",
7159
+ OBJECT_STORAGE_ENABLED: "true",
7160
+ OBJECT_STORAGE_ENDPOINT: "http://objectstorage:8333",
7161
+ OBJECT_STORAGE_PUBLIC_ENDPOINT: "{{publicUrl:proxy}}",
7162
+ OBJECT_STORAGE_FORCE_PATH_STYLE: "true",
7163
+ OBJECT_STORAGE_BUCKET: "posthog",
7164
+ OBJECT_STORAGE_ACCESS_KEY_ID: "any",
7165
+ OBJECT_STORAGE_SECRET_ACCESS_KEY: "any"
6290
7166
  },
6291
7167
  secretEnv: [
6292
7168
  "DATABASE_URL",
6293
- "SECRET_KEY",
6294
- "OBJECT_STORAGE_SECRET_ACCESS_KEY"
7169
+ "PERSONS_DATABASE_URL",
7170
+ "BEHAVIORAL_COHORTS_DATABASE_URL",
7171
+ "CYCLOTRON_DATABASE_URL",
7172
+ "SECRET_KEY"
6295
7173
  ],
6296
7174
  restart: "unless-stopped"
6297
7175
  }
@@ -6301,7 +7179,7 @@ virtual_file_io_mode="buffered"
6301
7179
  key: "POSTGRES_PASSWORD",
6302
7180
  service: "db",
6303
7181
  label: "Database password",
6304
- help: "Auto-generated. The Postgres password PostHog uses.",
7182
+ help: "Auto-generated. The Postgres password every PostHog service uses.",
6305
7183
  generate: "secret",
6306
7184
  secret: true
6307
7185
  },
@@ -6312,77 +7190,126 @@ virtual_file_io_mode="buffered"
6312
7190
  help: "Auto-generated. Signs sessions and cookies (SECRET_KEY).",
6313
7191
  generate: "secret",
6314
7192
  secret: true
7193
+ }
7194
+ ],
7195
+ management: {
7196
+ kind: "schema"
7197
+ },
7198
+ files: [
7199
+ {
7200
+ service: "proxy",
7201
+ path: "/etc/caddy/Caddyfile",
7202
+ content: "{\n servers {\n trusted_proxies static 127.0.0.1/32 ::1/128 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16\n }\n}\n\n# Host-less site address on purpose. PostHog's own default is\n# `http://localhost:8000`, which compiles to a Host matcher \u2014 behind Openship's\n# edge a foreign Host then returns an empty HTTP 200 instead of a 404, so every\n# ingestion POST would look successful while the events were silently dropped.\n# `:80` also emits no automatic_https and no tls app, so Caddy never tries ACME.\n:80 {\n # Health target for the container healthcheck. Deliberately answered by\n # Caddy itself: proxying /_health through to Django would report unhealthy\n # for the whole first-boot migration window and raise a false incident.\n @openship-health {\n path /openship-health\n }\n\n handle @openship-health {\n respond \"ok\" 200\n }\n\n @replay-capture {\n path /s\n path /s/\n path /s/*\n }\n\n @capture {\n path /e\n path /e/\n path /e/*\n path /i/v0\n path /i/v0/\n path /i/v0/*\n path /i/v1/analytics/events\n path /i/v1/analytics/events/\n path /batch\n path /batch/\n path /batch/*\n path /capture\n path /capture/\n path /capture/*\n }\n\n @flags {\n path /flags\n path /flags/\n path /flags/*\n path /api/feature_flag/local_evaluation\n path /api/feature_flag/local_evaluation/\n path /api/feature_flag/local_evaluation/*\n }\n\n @surveys {\n path /surveys\n path /surveys/\n path /api/surveys\n path /api/surveys/\n }\n\n @remote-config {\n path /array/*\n }\n\n @webhooks {\n path /public/webhooks\n path /public/webhooks/\n path /public/webhooks/*\n path /public/m/\n path /public/m/*\n }\n\n @objectstorage {\n path /posthog\n path /posthog/\n path /posthog/*\n }\n\n handle @capture {\n reverse_proxy capture:3000\n }\n\n handle @replay-capture {\n reverse_proxy replay-capture:3000\n }\n\n handle @flags {\n reverse_proxy feature-flags:3001\n }\n\n handle @surveys {\n reverse_proxy hypercache-server:3002\n }\n\n handle @remote-config {\n reverse_proxy hypercache-server:3002\n }\n\n handle @webhooks {\n reverse_proxy plugins:6738\n }\n\n handle @objectstorage {\n reverse_proxy objectstorage:8333\n }\n\n handle {\n reverse_proxy web:8000\n }\n}\n"
6315
7203
  },
6316
7204
  {
6317
- key: "MINIO_ROOT_USER",
6318
- service: "objectstorage",
6319
- label: "Object-storage access key",
6320
- help: "The S3 access key PostHog uses against its bundled MinIO (session recordings).",
6321
- type: "text",
6322
- default: "posthog",
6323
- required: true
7205
+ service: "clickhouse",
7206
+ path: "/etc/clickhouse-server/config.d/openship-posthog.xml",
7207
+ content: `<clickhouse>
7208
+ <!-- PostHog creates every table as ReplicatedMergeTree ON CLUSTER, so the
7209
+ macros, the clusters and ZooKeeper must all resolve or migrate_clickhouse
7210
+ cannot create a single table. Shipped as a config.d overlay rather than a
7211
+ replacement config.xml so the image's own defaults still apply. -->
7212
+ <zookeeper>
7213
+ <node>
7214
+ <host>zookeeper</host>
7215
+ <port>2181</port>
7216
+ </node>
7217
+ </zookeeper>
7218
+ <macros>
7219
+ <shard>01</shard>
7220
+ <replica>ch1</replica>
7221
+ </macros>
7222
+ <distributed_ddl>
7223
+ <path>/clickhouse/task_queue/ddl</path>
7224
+ </distributed_ddl>
7225
+ <!-- ~95 Kafka-engine tables each hold a background schedule slot. -->
7226
+ <background_schedule_pool_size>256</background_schedule_pool_size>
7227
+ <format_schema_path>/var/lib/clickhouse/format_schemas/</format_schema_path>
7228
+ <remote_servers>
7229
+ <posthog><shard><replica><host>clickhouse</host><port>9000</port></replica></shard></posthog>
7230
+ <posthog_single_shard><shard><replica><host>clickhouse</host><port>9000</port></replica></shard></posthog_single_shard>
7231
+ <posthog_migrations><shard><replica><host>clickhouse</host><port>9000</port></replica></shard></posthog_migrations>
7232
+ <posthog_writable><shard><replica><host>clickhouse</host><port>9000</port></replica></shard></posthog_writable>
7233
+ <posthog_primary_replica><shard><replica><host>clickhouse</host><port>9000</port></replica></shard></posthog_primary_replica>
7234
+ <ai_events><shard><replica><host>clickhouse</host><port>9000</port></replica></shard></ai_events>
7235
+ <aux><shard><replica><host>clickhouse</host><port>9000</port></replica></shard></aux>
7236
+ <ops><shard><replica><host>clickhouse</host><port>9000</port></replica></shard></ops>
7237
+ <sessions><shard><replica><host>clickhouse</host><port>9000</port></replica></shard></sessions>
7238
+ </remote_servers>
7239
+ <!-- PostHog's Kafka tables reference a named collection, not a broker list. -->
7240
+ <named_collections>
7241
+ <msk_cluster><kafka_broker_list from_env="KAFKA_HOSTS"/></msk_cluster>
7242
+ <warpstream_ingestion><kafka_broker_list from_env="KAFKA_HOSTS"/></warpstream_ingestion>
7243
+ <warpstream_calculated_events><kafka_broker_list from_env="KAFKA_HOSTS"/></warpstream_calculated_events>
7244
+ <warpstream_replay><kafka_broker_list from_env="KAFKA_HOSTS"/></warpstream_replay>
7245
+ <warpstream_shared><kafka_broker_list from_env="KAFKA_HOSTS"/></warpstream_shared>
7246
+ <warpstream_cyclotron><kafka_broker_list from_env="KAFKA_HOSTS"/></warpstream_cyclotron>
7247
+ <warpstream_logs><kafka_broker_list from_env="KAFKA_HOSTS"/></warpstream_logs>
7248
+ <warpstream_traces><kafka_broker_list from_env="KAFKA_HOSTS"/></warpstream_traces>
7249
+ </named_collections>
7250
+ </clickhouse>
7251
+ `
6324
7252
  },
6325
7253
  {
6326
- key: "MINIO_ROOT_PASSWORD",
6327
- service: "objectstorage",
6328
- label: "Object-storage secret key",
6329
- help: "Auto-generated. The S3 secret key for the bundled MinIO.",
6330
- generate: "secret",
6331
- secret: true
7254
+ service: "clickhouse",
7255
+ path: "/etc/clickhouse-server/users.d/openship-posthog.xml",
7256
+ content: "<clickhouse>\n <!-- CLICKHOUSE_SKIP_USER_SETUP=1 means the image creates no users, but the\n Django app connects as api/app/billing/dict_reader on different query\n paths. Passwords match the values the app is configured with; ClickHouse\n is never published outside the project network. -->\n <profiles>\n <default>\n <max_memory_usage>10000000000</max_memory_usage>\n <use_uncompressed_cache>0</use_uncompressed_cache>\n <load_balancing>random</load_balancing>\n </default>\n </profiles>\n <users>\n <default>\n <password></password>\n <networks><ip>::/0</ip></networks>\n <profile>default</profile>\n <quota>default</quota>\n <access_management>1</access_management>\n </default>\n <api>\n <password>apipass</password>\n <networks><ip>::/0</ip></networks>\n <profile>default</profile>\n <quota>default</quota>\n </api>\n <app>\n <password>apppass</password>\n <networks><ip>::/0</ip></networks>\n <profile>default</profile>\n <quota>default</quota>\n </app>\n <billing>\n <password>billingpass</password>\n <networks><ip>::/0</ip></networks>\n <profile>default</profile>\n <quota>default</quota>\n </billing>\n <dict_reader>\n <password>dictreaderpass</password>\n <networks><ip>::/0</ip></networks>\n <profile>default</profile>\n <quota>default</quota>\n </dict_reader>\n </users>\n <quotas>\n <default><interval><duration>3600</duration></interval></default>\n </quotas>\n</clickhouse>\n"
6332
7257
  }
6333
7258
  ],
6334
- management: {
6335
- kind: "schema"
6336
- },
6337
7259
  prepare: [
6338
7260
  {
6339
- service: "objectstorage",
6340
- command: 'mc alias set local http://127.0.0.1:9000 "$MINIO_ROOT_USER" "$MINIO_ROOT_PASSWORD" > /dev/null && mc mb --ignore-existing local/posthog > /dev/null && printf %s posthog',
6341
- capture: "bucket",
7261
+ service: "kafka",
7262
+ title: "Create Kafka topics",
7263
+ description: "Pre-creates the ingestion topics the Node consumers verify at startup.",
7264
+ command: 'for t in events_plugin_ingestion events_plugin_ingestion_historical events_plugin_ingestion_overflow events_plugin_ingestion_dlq events_plugin_ingestion_ai events_plugin_ingestion_async session_recording_snapshot_item_events clickhouse_events_json clickhouse_ai_events_json clickhouse_heatmap_events clickhouse_flag_evaluations clickhouse_ingestion_warnings clickhouse_groups clickhouse_person clickhouse_person_distinct_id clickhouse_person_distinct_id2 clickhouse_person_overrides clickhouse_app_metrics2 clickhouse_session_replay_events clickhouse_session_recording_events clickhouse_tophog heatmaps_ingestion ingestion-clientwarnings-main-1 ingestion-errortracking-main log_entries plugin_log_entries events_dead_letter_queue; do /opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --create --if-not-exists --topic "$t" --partitions 1 --replication-factor 1 >/dev/null 2>&1 || true; done; echo done',
7265
+ capture: "topics",
6342
7266
  phase: "post-ready",
6343
7267
  readiness: {
6344
- test: 'mc alias set local http://127.0.0.1:9000 "$MINIO_ROOT_USER" "$MINIO_ROOT_PASSWORD"',
6345
- interval: 1e3,
7268
+ test: "/opt/kafka/bin/kafka-broker-api-versions.sh --bootstrap-server localhost:9092",
7269
+ interval: 3e3,
6346
7270
  retries: 30
6347
- },
6348
- once: true
7271
+ }
6349
7272
  }
6350
7273
  ],
6351
7274
  connection: {
6352
7275
  title: "Open PostHog",
6353
- description: "Open the PostHog UI and create your admin account on first load.",
7276
+ description: "Open the PostHog UI and create your admin account on first load. The same hostname serves the UI and the event-ingestion endpoints, so an SDK pointed at this URL works with no extra configuration.",
6354
7277
  guide: {
6355
- intro: "PostHog runs its own analytics UI \u2014 create the admin account when you first open it.",
7278
+ intro: "PostHog runs its own analytics UI \u2014 create the admin account when you first open it, then use the project API key it gives you in your SDK.",
7279
+ useHint: "Point your SDK's host at this URL. Caddy routes /e, /capture, /batch and /i/* to the capture service, /s/* to session replay, /flags to feature flags, and everything else to the app.",
6356
7280
  defaultMode: "public"
6357
7281
  },
6358
7282
  outputs: [
6359
7283
  {
6360
7284
  id: "ui",
6361
7285
  label: "PostHog",
6362
- source: "publicUrl:web",
7286
+ source: "publicUrl:proxy",
6363
7287
  kind: "url",
6364
- help: "PostHog has no default login \u2014 the first visitor creates the admin account."
7288
+ recommended: true,
7289
+ help: "PostHog has no default login \u2014 the first visitor creates the admin account. This host also receives your events."
6365
7290
  }
6366
7291
  ],
6367
7292
  firstLogin: {
6368
- note: "PostHog ships no default credentials \u2014 open the URL above and sign up to create the first (admin) account."
7293
+ note: "PostHog ships no default credentials \u2014 open the URL above and sign up to create the first (admin) account. First boot runs database migrations and can take several minutes before the UI answers."
6369
7294
  }
6370
7295
  },
6371
7296
  endpoints: [
6372
7297
  {
6373
- service: "web",
6374
- port: 8e3,
7298
+ service: "proxy",
7299
+ port: 80,
6375
7300
  label: "PostHog",
6376
- kind: "http"
7301
+ kind: "http",
7302
+ defaultMode: "domain"
6377
7303
  }
6378
7304
  ]
6379
7305
  },
6380
7306
  {
6381
7307
  available: true,
6382
- verified: true,
7308
+ verified: false,
6383
7309
  id: "redis",
6384
7310
  name: "Valkey (Redis)",
6385
- description: "In-memory data store for caching, sessions, rate limits, and queues \u2014 Valkey, the open-source Redis fork. Drop-in Redis-compatible: point any redis client at it.",
7311
+ description: "In-memory data store for caching, sessions, rate limits, and queues \u2014 Valkey, the open-source Redis fork. Drop-in Redis-compatible: point any redis client at it. Ships with RedisInsight, a browser UI that arrives already connected to this instance.",
7312
+ repository: "https://github.com/valkey-io/valkey",
6386
7313
  kind: "template",
6387
7314
  logo: "valkey",
6388
7315
  category: "database",
@@ -6391,16 +7318,22 @@ virtual_file_io_mode="buffered"
6391
7318
  "redis",
6392
7319
  "valkey",
6393
7320
  "key-value",
6394
- "queue"
7321
+ "queue",
7322
+ "gui"
6395
7323
  ],
6396
7324
  framework: "docker-compose",
6397
7325
  services: [
6398
7326
  {
6399
7327
  name: "valkey",
6400
7328
  image: "valkey/valkey:8.1-alpine",
6401
- command: "valkey-server /etc/valkey/valkey.conf",
6402
- ports: [
6403
- "6379:6379"
7329
+ commandArgv: [
7330
+ "valkey-server"
7331
+ ],
7332
+ environment: {
7333
+ VALKEY_EXTRA_FLAGS: "--requirepass {{config:VALKEY_PASSWORD}} --appendonly yes --appendfsync everysec --save 300 100"
7334
+ },
7335
+ secretEnv: [
7336
+ "VALKEY_EXTRA_FLAGS"
6404
7337
  ],
6405
7338
  volumes: [
6406
7339
  "valkey_data:/data"
@@ -6408,7 +7341,7 @@ virtual_file_io_mode="buffered"
6408
7341
  healthcheck: {
6409
7342
  test: [
6410
7343
  "CMD-SHELL",
6411
- 'valkey-cli --no-auth-warning -a "$VALKEY_PASSWORD" ping | grep -q PONG'
7344
+ 'valkey-cli ping 2>&1 | grep -q NOAUTH && valkey-cli --no-auth-warning -a "$VALKEY_PASSWORD" ping | grep -q PONG'
6412
7345
  ],
6413
7346
  interval: "10s",
6414
7347
  timeout: "5s",
@@ -6416,6 +7349,38 @@ virtual_file_io_mode="buffered"
6416
7349
  startPeriod: "10s"
6417
7350
  },
6418
7351
  restart: "unless-stopped"
7352
+ },
7353
+ {
7354
+ name: "redisinsight",
7355
+ image: "redis/redisinsight:3.8.0",
7356
+ exposedPort: 5540,
7357
+ exposed: true,
7358
+ routes: [
7359
+ {
7360
+ port: 5540
7361
+ }
7362
+ ],
7363
+ dependsOn: [
7364
+ "valkey"
7365
+ ],
7366
+ environment: {
7367
+ RI_ACCEPT_TERMS_AND_CONDITIONS: "true",
7368
+ RI_REDIS_HOST: "valkey",
7369
+ RI_REDIS_PORT: "6379",
7370
+ RI_REDIS_DB: "0",
7371
+ RI_REDIS_ALIAS: "Valkey (this app)",
7372
+ RI_REDIS_PASSWORD: "{{config:VALKEY_PASSWORD}}"
7373
+ },
7374
+ secretEnv: [
7375
+ "RI_REDIS_PASSWORD"
7376
+ ],
7377
+ volumes: [
7378
+ "redisinsight_data:/data"
7379
+ ],
7380
+ restart: "unless-stopped",
7381
+ ports: [
7382
+ "8219:5540"
7383
+ ]
6419
7384
  }
6420
7385
  ],
6421
7386
  configFields: [
@@ -6423,46 +7388,58 @@ virtual_file_io_mode="buffered"
6423
7388
  key: "VALKEY_PASSWORD",
6424
7389
  service: "valkey",
6425
7390
  label: "Password",
6426
- help: "Auto-generated. Required by every client (Valkey's `requirepass` auth).",
7391
+ help: "Auto-generated. Required by every client (Valkey's `requirepass` auth). The bundled browser UI is pre-loaded with it.",
6427
7392
  generate: "secret",
6428
7393
  secret: true
6429
7394
  }
6430
7395
  ],
6431
- files: [
6432
- {
6433
- service: "valkey",
6434
- path: "/etc/valkey/valkey.conf",
6435
- content: "requirepass {{config:VALKEY_PASSWORD}}\nappendonly yes\nappendfsync everysec\nsave 300 100\n"
6436
- }
6437
- ],
6438
7396
  management: {
6439
7397
  kind: "schema"
6440
7398
  },
6441
7399
  connection: {
6442
7400
  title: "Connect to Valkey",
6443
- description: "A Redis-compatible endpoint. Point any redis client at the URL below \u2014 it already carries the password.",
7401
+ description: "A Redis-compatible endpoint on your project's private network. Point any redis client at the URL below \u2014 it already carries the password.",
6444
7402
  guide: {
6445
- intro: "Your project gets a Redis-compatible cache, ready to use.",
7403
+ intro: "Your project gets a Redis-compatible cache plus a browser UI that is already connected to it.",
6446
7404
  useHint: "Read `process.env.REDIS_URL` in your code \u2014 it's set the next time your project deploys.",
6447
7405
  defaultMode: "internal"
6448
7406
  },
6449
7407
  outputs: [
7408
+ {
7409
+ id: "ui",
7410
+ label: "Browser UI",
7411
+ source: "publicUrl:redisinsight",
7412
+ kind: "url",
7413
+ recommended: true,
7414
+ help: "RedisInsight, already pointed at this instance \u2014 no connection details to enter. It has NO login of its own, so anyone who can reach this address can read and write your data: keep it on the published port (reachable through an SSH tunnel) unless you deliberately put it on a domain you protect.",
7415
+ width: "full"
7416
+ },
6450
7417
  {
6451
7418
  id: "url",
6452
7419
  label: "Connection URL",
6453
- source: "template:redis://:{{env:valkey:VALKEY_PASSWORD}}@{{host}}:6379",
6454
- sourceLabel: "Public",
6455
- variants: [
6456
- {
6457
- id: "internal",
6458
- label: "Internal",
6459
- source: "template:redis://:{{env:valkey:VALKEY_PASSWORD}}@valkey:6379"
6460
- }
6461
- ],
7420
+ source: "template:redis://:{{env:valkey:VALKEY_PASSWORD}}@valkey:6379",
7421
+ sourceLabel: "Internal",
7422
+ service: "valkey",
6462
7423
  secret: true,
6463
7424
  envKey: "REDIS_URL",
6464
7425
  recommended: true,
6465
- help: "Redis-protocol URL with the password embedded. Published on port 6379. Switch to Internal for apps on the same project network."
7426
+ help: "Redis-protocol URL with the password embedded, on the project's private network. Port 6379 is not published to the internet \u2014 bind this app into another project to use it, or reach it from the server with `docker exec`."
7427
+ },
7428
+ {
7429
+ id: "host",
7430
+ label: "Host",
7431
+ source: "template:valkey",
7432
+ service: "valkey",
7433
+ envKey: "REDIS_HOST",
7434
+ width: "half"
7435
+ },
7436
+ {
7437
+ id: "port",
7438
+ label: "Port",
7439
+ source: "template:6379",
7440
+ service: "valkey",
7441
+ envKey: "REDIS_PORT",
7442
+ width: "half"
6466
7443
  },
6467
7444
  {
6468
7445
  id: "password",
@@ -6484,11 +7461,23 @@ virtual_file_io_mode="buffered"
6484
7461
  }
6485
7462
  ],
6486
7463
  endpoints: [
7464
+ {
7465
+ service: "redisinsight",
7466
+ port: 5540,
7467
+ label: "Browser UI",
7468
+ kind: "http",
7469
+ scope: "public"
7470
+ },
6487
7471
  {
6488
7472
  service: "valkey",
6489
7473
  port: 6379,
6490
- label: "Redis / Valkey",
6491
- kind: "tcp"
7474
+ label: "Redis / Valkey (private)",
7475
+ kind: "tcp",
7476
+ scope: "internal",
7477
+ defaultMode: "internal",
7478
+ allowedModes: [
7479
+ "internal"
7480
+ ]
6492
7481
  }
6493
7482
  ]
6494
7483
  },
@@ -6514,9 +7503,6 @@ virtual_file_io_mode="buffered"
6514
7503
  image: "ghcr.io/umami-software/umami:postgresql-v2.19.0",
6515
7504
  exposedPort: 3e3,
6516
7505
  exposed: true,
6517
- ports: [
6518
- "3000:3000"
6519
- ],
6520
7506
  routes: [
6521
7507
  {
6522
7508
  port: 3e3
@@ -6542,7 +7528,10 @@ virtual_file_io_mode="buffered"
6542
7528
  retries: 10,
6543
7529
  startPeriod: "30s"
6544
7530
  },
6545
- restart: "unless-stopped"
7531
+ restart: "unless-stopped",
7532
+ ports: [
7533
+ "3011:3000"
7534
+ ]
6546
7535
  },
6547
7536
  {
6548
7537
  name: "umami-db",
@@ -6785,9 +7774,279 @@ virtual_file_io_mode="buffered"
6785
7774
  port: 4080,
6786
7775
  label: "Webmail",
6787
7776
  kind: "http",
6788
- defaultMode: "domain",
7777
+ defaultMode: "domain",
7778
+ allowedModes: [
7779
+ "domain"
7780
+ ]
7781
+ }
7782
+ ]
7783
+ },
7784
+ {
7785
+ available: true,
7786
+ verified: false,
7787
+ id: "clickhouse",
7788
+ name: "ClickHouse",
7789
+ description: "The columnar SQL database for analytics \u2014 billions of rows scanned per second. Ships with the CH-UI console, so you get a SQL editor, schema browser and dashboards the moment it installs: sign in with the ClickHouse user and password below, no extra setup.",
7790
+ repository: "https://github.com/ClickHouse/ClickHouse",
7791
+ kind: "template",
7792
+ logo: "clickhouse",
7793
+ category: "database",
7794
+ tags: [
7795
+ "analytics",
7796
+ "olap",
7797
+ "sql",
7798
+ "columnar",
7799
+ "warehouse",
7800
+ "timeseries"
7801
+ ],
7802
+ framework: "docker-compose",
7803
+ minResources: {
7804
+ memoryMb: 2048
7805
+ },
7806
+ services: [
7807
+ {
7808
+ name: "clickhouse",
7809
+ image: "clickhouse/clickhouse-server:26.3.17.110",
7810
+ exposedPort: 8123,
7811
+ exposed: true,
7812
+ routes: [
7813
+ {
7814
+ port: 8123
7815
+ }
7816
+ ],
7817
+ environment: {
7818
+ CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT: "1"
7819
+ },
7820
+ secretEnv: [
7821
+ "CLICKHOUSE_PASSWORD"
7822
+ ],
7823
+ volumes: [
7824
+ "clickhouse_data:/var/lib/clickhouse"
7825
+ ],
7826
+ healthcheck: {
7827
+ test: [
7828
+ "CMD-SHELL",
7829
+ "wget -q -O /dev/null http://127.0.0.1:8123/ping || exit 1"
7830
+ ],
7831
+ interval: "10s",
7832
+ timeout: "5s",
7833
+ retries: 12,
7834
+ startPeriod: "30s"
7835
+ },
7836
+ restart: "unless-stopped",
7837
+ stopGracePeriod: "60s",
7838
+ ports: [
7839
+ "8218:8123"
7840
+ ]
7841
+ },
7842
+ {
7843
+ name: "ch-ui",
7844
+ image: "ghcr.io/caioricciuti/ch-ui:v2.6.1",
7845
+ exposedPort: 3488,
7846
+ exposed: true,
7847
+ routes: [
7848
+ {
7849
+ port: 3488
7850
+ }
7851
+ ],
7852
+ dependsOn: [
7853
+ "clickhouse"
7854
+ ],
7855
+ environment: {
7856
+ CLICKHOUSE_URL: "http://clickhouse:8123",
7857
+ CONNECTION_NAME: "ClickHouse",
7858
+ APP_URL: "{{publicUrl:ch-ui}}",
7859
+ DATABASE_PATH: "/app/data/ch-ui.db"
7860
+ },
7861
+ secretEnv: [
7862
+ "APP_SECRET_KEY"
7863
+ ],
7864
+ volumes: [
7865
+ "ch_ui_data:/app/data"
7866
+ ],
7867
+ healthcheck: {
7868
+ test: [
7869
+ "CMD-SHELL",
7870
+ "wget -q -O /dev/null http://127.0.0.1:3488/health || exit 1"
7871
+ ],
7872
+ interval: "10s",
7873
+ timeout: "5s",
7874
+ retries: 6,
7875
+ startPeriod: "15s"
7876
+ },
7877
+ restart: "unless-stopped",
7878
+ ports: [
7879
+ "8217:3488"
7880
+ ]
7881
+ }
7882
+ ],
7883
+ configFields: [
7884
+ {
7885
+ key: "CLICKHOUSE_USER",
7886
+ service: "clickhouse",
7887
+ label: "Database user",
7888
+ help: "The account the server creates on its first boot. It owns the data and is the sign-in for the console.",
7889
+ type: "text",
7890
+ default: "default",
7891
+ required: true
7892
+ },
7893
+ {
7894
+ key: "CLICKHOUSE_PASSWORD",
7895
+ service: "clickhouse",
7896
+ label: "Password",
7897
+ help: "Auto-generated. Set on the account at first boot \u2014 it is the console sign-in and the credential every SQL client needs. Without it the server would refuse all non-localhost connections.",
7898
+ generate: "secret",
7899
+ secret: true
7900
+ },
7901
+ {
7902
+ key: "CLICKHOUSE_DB",
7903
+ service: "clickhouse",
7904
+ label: "First database",
7905
+ help: "Created on the first boot so you can write a table immediately. More can be added from the console.",
7906
+ type: "text",
7907
+ default: "analytics",
7908
+ required: true
7909
+ },
7910
+ {
7911
+ key: "APP_SECRET_KEY",
7912
+ service: "ch-ui",
7913
+ label: "Console session key",
7914
+ help: "Auto-generated. Encrypts the ClickHouse credentials the console holds for a signed-in session. Rotating it signs everyone out.",
7915
+ generate: "secret",
7916
+ secret: true
7917
+ }
7918
+ ],
7919
+ management: {
7920
+ kind: "schema"
7921
+ },
7922
+ connection: {
7923
+ title: "Connect to ClickHouse",
7924
+ description: "Open the console and sign in with the user and password below \u2014 the console talks to ClickHouse over the private project network, so the database itself never has to be public. For your own code, use the DSN.",
7925
+ guide: {
7926
+ intro: "Your project gets a ClickHouse server plus a web console for querying it.",
7927
+ useHint: "Read `process.env.CLICKHOUSE_URL` with `CLICKHOUSE_USER` / `CLICKHOUSE_PASSWORD` (or the single `CLICKHOUSE_DSN`) \u2014 set on your next deploy.",
7928
+ defaultMode: "internal"
7929
+ },
7930
+ firstLogin: {
7931
+ username: "default",
7932
+ note: "The console has no account of its own: sign in with the ClickHouse user above and the generated password from this page. Three wrong tries locks that user out for 15 minutes, so paste the password rather than typing it."
7933
+ },
7934
+ outputs: [
7935
+ {
7936
+ id: "console",
7937
+ label: "Console",
7938
+ source: "publicUrl:ch-ui",
7939
+ kind: "url",
7940
+ recommended: true,
7941
+ help: "The CH-UI web console \u2014 SQL editor, schema browser, saved queries and dashboards. Sign in with the user and password below."
7942
+ },
7943
+ {
7944
+ id: "httpUrl",
7945
+ label: "HTTP endpoint",
7946
+ source: "publicUrl:clickhouse",
7947
+ sourceLabel: "Public",
7948
+ variants: [
7949
+ {
7950
+ id: "internal",
7951
+ label: "Internal",
7952
+ source: "template:http://clickhouse:8123"
7953
+ }
7954
+ ],
7955
+ service: "clickhouse",
7956
+ envKey: "CLICKHOUSE_URL",
7957
+ recommended: true,
7958
+ help: "ClickHouse's HTTP interface (port 8123) \u2014 what clickhouse-connect, clickhouse-js, the JDBC/ODBC drivers and plain curl talk to. Send the user and password as basic auth. Switch to Internal for apps on the same project network; Public only resolves if you gave this port a domain."
7959
+ },
7960
+ {
7961
+ id: "httpDsn",
7962
+ label: "HTTP DSN",
7963
+ source: "template:http://{{env:clickhouse:CLICKHOUSE_USER}}:{{env:clickhouse:CLICKHOUSE_PASSWORD}}@clickhouse:8123/{{env:clickhouse:CLICKHOUSE_DB}}",
7964
+ sourceLabel: "Internal",
7965
+ service: "clickhouse",
7966
+ secret: true,
7967
+ envKey: "CLICKHOUSE_HTTP_DSN",
7968
+ recommended: true,
7969
+ help: "One string with the credentials baked in, over the private project network. For a client outside this server, use the public HTTP endpoint above plus the user and password."
7970
+ },
7971
+ {
7972
+ id: "nativeDsn",
7973
+ label: "Native TCP DSN",
7974
+ source: "template:clickhouse://{{env:clickhouse:CLICKHOUSE_USER}}:{{env:clickhouse:CLICKHOUSE_PASSWORD}}@clickhouse:9000/{{env:clickhouse:CLICKHOUSE_DB}}",
7975
+ sourceLabel: "Internal",
7976
+ service: "clickhouse",
7977
+ secret: true,
7978
+ envKey: "CLICKHOUSE_DSN",
7979
+ help: "ClickHouse's native protocol (port 9000) \u2014 faster and what clickhouse-client, clickhouse-go and clickhouse-driver prefer. Reachable from inside the project only; the port is never published to the host."
7980
+ },
7981
+ {
7982
+ id: "user",
7983
+ label: "User",
7984
+ source: "env:clickhouse:CLICKHOUSE_USER",
7985
+ service: "clickhouse",
7986
+ envKey: "CLICKHOUSE_USER",
7987
+ recommended: true,
7988
+ width: "half"
7989
+ },
7990
+ {
7991
+ id: "password",
7992
+ label: "Password",
7993
+ source: "env:clickhouse:CLICKHOUSE_PASSWORD",
7994
+ service: "clickhouse",
7995
+ envKey: "CLICKHOUSE_PASSWORD",
7996
+ secret: true,
7997
+ recommended: true,
7998
+ width: "half"
7999
+ },
8000
+ {
8001
+ id: "database",
8002
+ label: "Database",
8003
+ source: "env:clickhouse:CLICKHOUSE_DB",
8004
+ service: "clickhouse",
8005
+ envKey: "CLICKHOUSE_DATABASE",
8006
+ width: "half"
8007
+ }
8008
+ ]
8009
+ },
8010
+ provides: [
8011
+ {
8012
+ id: "clickhouse",
8013
+ outputRefs: [
8014
+ "httpUrl",
8015
+ "httpDsn",
8016
+ "user",
8017
+ "password",
8018
+ "database"
8019
+ ],
8020
+ category: "database"
8021
+ }
8022
+ ],
8023
+ endpoints: [
8024
+ {
8025
+ service: "ch-ui",
8026
+ port: 3488,
8027
+ label: "Console",
8028
+ kind: "http",
8029
+ required: true,
8030
+ scope: "public",
8031
+ defaultMode: "domain"
8032
+ },
8033
+ {
8034
+ service: "clickhouse",
8035
+ port: 8123,
8036
+ label: "HTTP API",
8037
+ kind: "http",
8038
+ scope: "public",
8039
+ defaultMode: "port"
8040
+ },
8041
+ {
8042
+ service: "clickhouse",
8043
+ port: 9e3,
8044
+ label: "Native protocol",
8045
+ kind: "tcp",
8046
+ scope: "internal",
8047
+ defaultMode: "internal",
6789
8048
  allowedModes: [
6790
- "domain"
8049
+ "internal"
6791
8050
  ]
6792
8051
  }
6793
8052
  ]
@@ -21355,7 +22614,30 @@ var serviceSpec = external_exports.object({
21355
22614
  exposed: external_exports.boolean().optional(),
21356
22615
  healthcheck: external_exports.unknown().optional(),
21357
22616
  restart: external_exports.enum(["no", "always", "on-failure", "unless-stopped"]).optional(),
21358
- command: external_exports.string().optional()
22617
+ command: external_exports.string().optional(),
22618
+ /**
22619
+ * Structured argv, passed through as the container Cmd with NO `sh -c` wrap.
22620
+ * Wins over `command` when both are set.
22621
+ *
22622
+ * `command` is convenient but it is a SHELL string, so the container's argv
22623
+ * becomes ["sh","-c",cmd] — and an image whose entrypoint rewrites argv rather
22624
+ * than `exec "$@"` then sees the wrong thing. MinIO is the worked example: its
22625
+ * entrypoint prepends `minio` unless argv[0] already is, so a `command` turned
22626
+ * into `minio sh -c "server /data"` and the container exited with "'sh' is not
22627
+ * a minio sub-command" on every boot. There is no command string that fixes
22628
+ * that; the argv has to arrive unwrapped. Use `command` when you need a shell
22629
+ * (`a && b`), `commandArgv` when the image needs exact argv.
22630
+ */
22631
+ commandArgv: external_exports.array(external_exports.string()).optional(),
22632
+ /**
22633
+ * How long Docker waits after SIGTERM before SIGKILL (compose duration, e.g.
22634
+ * "10m"). The engine has always honored `advanced.stopGracePeriod`; only the
22635
+ * template could not ask for it, which silently capped every app at Docker's
22636
+ * 10s default. That is not a tuning knob for an app whose clean shutdown does
22637
+ * a final checkpoint and whose boot lease is a lockfile — being killed
22638
+ * mid-checkpoint leaves the lock behind and the next boot refuses to start.
22639
+ */
22640
+ stopGracePeriod: external_exports.string().optional()
21359
22641
  });
21360
22642
  var configField = external_exports.object({
21361
22643
  key: external_exports.string(),
@@ -21532,6 +22814,20 @@ var appTemplateSchema = external_exports.object({
21532
22814
  memoryMb: external_exports.number().positive().optional(),
21533
22815
  cpuCores: external_exports.number().positive().optional()
21534
22816
  }).optional(),
22817
+ /**
22818
+ * Trust mark: official open-source image plus a reviewable pipeline.
22819
+ *
22820
+ * A claim about PROVENANCE, not about whether we got the app running. Booting a
22821
+ * template proves it works; it does not make its publisher vouched for. Set it
22822
+ * only when EVERY image is published by the project the app actually is, or is
22823
+ * a composition upstream itself documents (Ghost + MySQL, Supabase's own
22824
+ * compose, Convex's backend/dashboard pair).
22825
+ *
22826
+ * Leave it off when WE picked a third-party companion the upstream project
22827
+ * neither ships nor endorses — ClickHouse + ch-ui, Kafka + kafbat-ui,
22828
+ * MongoDB + mongo-express, Valkey + RedisInsight — or when the publisher is a
22829
+ * single maintainer whose build pipeline cannot be reviewed (neond).
22830
+ */
21535
22831
  verified: external_exports.boolean().optional(),
21536
22832
  // Hidden from the browsable catalog while staying fully installable — for an app
21537
22833
  // reached through another app's wizard rather than the grid. NOT `available:
@@ -21606,6 +22902,1436 @@ var appTemplateSchema = external_exports.object({
21606
22902
  });
21607
22903
  });
21608
22904
 
22905
+ // ../../packages/core/src/deployment-class.ts
22906
+ var WORKLOAD_TYPES = ["web", "worker", "static"];
22907
+
22908
+ // ../../packages/core/src/pricing/schema.ts
22909
+ var MAX_SUPPORTED_PRICING_SCHEMA = 1;
22910
+ var limitNumber = external_exports.number().int().nonnegative().nullable();
22911
+ var planLimitsSchema = external_exports.object({
22912
+ /** Which deployment workloads the tier may run. Free ships `["static"]`. */
22913
+ workloads: external_exports.array(external_exports.enum(WORKLOAD_TYPES)).min(1),
22914
+ /** May the tier run multi-service stacks — Compose, catalog apps, managed DBs? */
22915
+ services: external_exports.boolean(),
22916
+ /**
22917
+ * Concurrently running services — one Oblien workspace each. This is the
22918
+ * ceiling customers feel: an app + Postgres + Redis is 3, a static site is 0
22919
+ * at rest. Drives `max_workspaces` (plus build headroom).
22920
+ */
22921
+ runningServices: limitNumber,
22922
+ /** Projects (project groups). Oblien has no project concept — Openship gates it. */
22923
+ maxProjects: limitNumber,
22924
+ /**
22925
+ * The largest per-service machine this tier may select, named in the deploy
22926
+ * wizard's OWN vocabulary (`RESOURCE_TIER_SPECS`). Stating a raw vCPU number
22927
+ * here is what made the pricing page advertise sizes no picker offered.
22928
+ * `null` = uncapped (enterprise).
22929
+ */
22930
+ maxResourceTier: external_exports.enum(RESOURCE_TIER_ORDER).nullable(),
22931
+ buildMinutesPerMonth: limitNumber,
22932
+ freeSubdomains: limitNumber,
22933
+ customDomains: limitNumber,
22934
+ seats: limitNumber
22935
+ });
22936
+ var planSchema = external_exports.object({
22937
+ id: external_exports.string().min(1),
22938
+ price: external_exports.object({
22939
+ /** USD cents, or null for "contact sales". */
22940
+ monthly: external_exports.number().int().nonnegative().nullable(),
22941
+ annual: external_exports.number().int().nonnegative().nullable()
22942
+ }),
22943
+ stripePriceEnv: external_exports.object({
22944
+ monthly: external_exports.string().min(1).nullable(),
22945
+ annual: external_exports.string().min(1).nullable()
22946
+ }),
22947
+ popular: external_exports.boolean(),
22948
+ contactSales: external_exports.string().min(1).nullable(),
22949
+ support: external_exports.enum(["community", "email", "priority", "dedicated"]),
22950
+ /** Tier whose features this one builds on — drives the "Everything in X" line. */
22951
+ inherits: external_exports.string().min(1).optional(),
22952
+ credits: external_exports.object({
22953
+ /** Milli-credits granted per period; null = granted by hand (enterprise). */
22954
+ monthlyMilli: external_exports.number().int().positive().nullable()
22955
+ }),
22956
+ limits: planLimitsSchema,
22957
+ /** Copy keys, in display order. Each must exist in `locales/en.json#features`. */
22958
+ features: external_exports.array(external_exports.string().min(1)).min(1)
22959
+ });
22960
+ var isoInstant = external_exports.string().regex(
22961
+ /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$/,
22962
+ 'must be a full ISO-8601 instant with an offset, e.g. "2026-09-30T23:59:59Z" \u2014 a bare date is timezone-ambiguous and ends a day early'
22963
+ ).refine((s) => !Number.isNaN(Date.parse(s)), "must be a parseable date");
22964
+ var campaignSchema = external_exports.object({
22965
+ id: external_exports.string().min(1),
22966
+ /** Percent off the monthly price, 1–100. */
22967
+ percentOff: external_exports.number().int().min(1).max(100),
22968
+ /** Plan ids the campaign applies to, or `"all"` for every purchasable tier. */
22969
+ appliesTo: external_exports.union([external_exports.literal("all"), external_exports.array(external_exports.string().min(1)).min(1)]),
22970
+ startsAt: isoInstant,
22971
+ endsAt: isoInstant,
22972
+ /** How many months the discount lasts per customer; null = for the whole
22973
+ * subscription lifetime. Must match the Stripe coupon's duration. */
22974
+ durationMonths: external_exports.number().int().positive().nullable(),
22975
+ /** Env var NAMING the Stripe coupon id — same indirection as price ids, so
22976
+ * no live Stripe id is committed or shipped to a browser bundle. */
22977
+ stripeCouponEnv: external_exports.string().min(1)
22978
+ }).superRefine((c, ctx) => {
22979
+ if (Date.parse(c.endsAt) <= Date.parse(c.startsAt)) {
22980
+ ctx.addIssue({ code: "custom", path: ["endsAt"], message: `campaign "${c.id}" ends before it starts` });
22981
+ }
22982
+ });
22983
+ var creditPackSchema = external_exports.object({
22984
+ id: external_exports.string().min(1),
22985
+ creditsMilli: external_exports.number().int().positive(),
22986
+ priceCents: external_exports.number().int().positive(),
22987
+ stripePriceEnv: external_exports.string().min(1),
22988
+ sortOrder: external_exports.number().int()
22989
+ });
22990
+ var pricingCatalogSchema = external_exports.object({
22991
+ schemaVersion: external_exports.number().int().positive(),
22992
+ currency: external_exports.string().length(3),
22993
+ /** Suffix of the managed free domain, e.g. `.opsh.io`. Copy interpolates it. */
22994
+ freeDomainSuffix: external_exports.string().min(1),
22995
+ annual: external_exports.object({
22996
+ /** False hides every annual affordance; annual prices may stay null. */
22997
+ enabled: external_exports.boolean(),
22998
+ monthsFree: external_exports.number().int().nonnegative()
22999
+ }),
23000
+ /**
23001
+ * How customer-facing limits translate into Oblien's per-namespace ceilings.
23002
+ *
23003
+ * `buildResources` MIRRORS `DEFAULT_BUILD_RESOURCE_CONFIG` in adapters — a
23004
+ * build runs in its own Oblien workspace, and since `max_vcpus`/`max_ram_mb`/
23005
+ * `max_disk_gb` are per-workspace caps, a tier whose ceiling sits below the
23006
+ * build machine cannot build AT ALL (Oblien 409s the create). That is not
23007
+ * hypothetical: free published 2 vCPU / 2 GB against a 4 vCPU / 8 GB build,
23008
+ * so the one workload free is allowed to run would have failed the moment
23009
+ * ceilings went live. The mirror is asserted by test.
23010
+ *
23011
+ * `buildWorkspaceHeadroom` is how many transient build workspaces a tier may
23012
+ * hold ON TOP of its running services, because Oblien counts build
23013
+ * workspaces in `max_workspaces` too. Without it a free user with one
23014
+ * service could never deploy — the build would have nowhere to go.
23015
+ */
23016
+ oblien: external_exports.object({
23017
+ buildWorkspaceHeadroom: external_exports.number().int().nonnegative(),
23018
+ buildResources: external_exports.object({
23019
+ cpuCores: external_exports.number().positive(),
23020
+ memoryMb: external_exports.number().int().positive(),
23021
+ diskGb: external_exports.number().int().positive()
23022
+ })
23023
+ }),
23024
+ /** Time-bounded automatic discounts. Empty = list price. */
23025
+ campaigns: external_exports.array(campaignSchema),
23026
+ plans: external_exports.array(planSchema).min(1),
23027
+ creditPacks: external_exports.array(creditPackSchema),
23028
+ selfHosted: external_exports.object({ features: external_exports.array(external_exports.string().min(1)).min(1) })
23029
+ }).superRefine((data, ctx) => {
23030
+ if (data.schemaVersion > MAX_SUPPORTED_PRICING_SCHEMA) {
23031
+ ctx.addIssue({
23032
+ code: "custom",
23033
+ path: ["schemaVersion"],
23034
+ message: `pricing.json schemaVersion ${data.schemaVersion} is newer than this build supports (${MAX_SUPPORTED_PRICING_SCHEMA})`
23035
+ });
23036
+ }
23037
+ const ids = /* @__PURE__ */ new Set();
23038
+ data.plans.forEach((plan, i) => {
23039
+ if (ids.has(plan.id)) {
23040
+ ctx.addIssue({ code: "custom", path: ["plans", i, "id"], message: `duplicate plan id "${plan.id}"` });
23041
+ }
23042
+ ids.add(plan.id);
23043
+ });
23044
+ data.plans.forEach((plan, i) => {
23045
+ if (plan.inherits && !ids.has(plan.inherits)) {
23046
+ ctx.addIssue({ code: "custom", path: ["plans", i, "inherits"], message: `plan "${plan.id}" inherits unknown plan "${plan.inherits}"` });
23047
+ }
23048
+ if (plan.inherits === plan.id) {
23049
+ ctx.addIssue({ code: "custom", path: ["plans", i, "inherits"], message: `plan "${plan.id}" inherits itself` });
23050
+ }
23051
+ const monthlyPurchasable = plan.price.monthly !== null && plan.price.monthly > 0;
23052
+ if (monthlyPurchasable && !plan.stripePriceEnv.monthly) {
23053
+ ctx.addIssue({ code: "custom", path: ["plans", i, "stripePriceEnv", "monthly"], message: `plan "${plan.id}" has a monthly price but no Stripe price env name` });
23054
+ }
23055
+ const annualPurchasable = plan.price.annual !== null && plan.price.annual > 0;
23056
+ if (annualPurchasable && !plan.stripePriceEnv.annual) {
23057
+ ctx.addIssue({ code: "custom", path: ["plans", i, "stripePriceEnv", "annual"], message: `plan "${plan.id}" has an annual price but no Stripe price env name` });
23058
+ }
23059
+ if (plan.contactSales && plan.price.monthly !== null) {
23060
+ ctx.addIssue({ code: "custom", path: ["plans", i, "contactSales"], message: `plan "${plan.id}" sets contactSales but also a monthly price` });
23061
+ }
23062
+ if (!plan.contactSales && plan.price.monthly === null) {
23063
+ ctx.addIssue({ code: "custom", path: ["plans", i, "price", "monthly"], message: `plan "${plan.id}" has no monthly price and no contactSales \u2014 it would render as an unbuyable blank` });
23064
+ }
23065
+ if (plan.features.includes("everythingIn") && !plan.inherits) {
23066
+ ctx.addIssue({ code: "custom", path: ["plans", i, "features"], message: `plan "${plan.id}" uses the "everythingIn" feature key without an "inherits" tier` });
23067
+ }
23068
+ });
23069
+ const campaignIds = /* @__PURE__ */ new Set();
23070
+ data.campaigns.forEach((c, i) => {
23071
+ if (campaignIds.has(c.id)) {
23072
+ ctx.addIssue({ code: "custom", path: ["campaigns", i, "id"], message: `duplicate campaign id "${c.id}"` });
23073
+ }
23074
+ campaignIds.add(c.id);
23075
+ if (c.appliesTo !== "all") {
23076
+ for (const target of c.appliesTo) {
23077
+ if (!ids.has(target)) {
23078
+ ctx.addIssue({ code: "custom", path: ["campaigns", i, "appliesTo"], message: `campaign "${c.id}" targets unknown plan "${target}"` });
23079
+ }
23080
+ }
23081
+ }
23082
+ });
23083
+ for (let i = 0; i < data.campaigns.length; i++) {
23084
+ for (let j = i + 1; j < data.campaigns.length; j++) {
23085
+ const a = data.campaigns[i];
23086
+ const b = data.campaigns[j];
23087
+ const overlapsInTime = Date.parse(a.startsAt) < Date.parse(b.endsAt) && Date.parse(b.startsAt) < Date.parse(a.endsAt);
23088
+ if (!overlapsInTime) continue;
23089
+ const sharesAPlan = a.appliesTo === "all" || b.appliesTo === "all" || a.appliesTo.some((p) => b.appliesTo.includes(p));
23090
+ if (sharesAPlan) {
23091
+ ctx.addIssue({
23092
+ code: "custom",
23093
+ path: ["campaigns", j],
23094
+ message: `campaigns "${a.id}" and "${b.id}" overlap in time on the same plan \u2014 only one discount can apply`
23095
+ });
23096
+ }
23097
+ }
23098
+ }
23099
+ const packIds = /* @__PURE__ */ new Set();
23100
+ data.creditPacks.forEach((pack, i) => {
23101
+ if (packIds.has(pack.id)) {
23102
+ ctx.addIssue({ code: "custom", path: ["creditPacks", i, "id"], message: `duplicate credit pack id "${pack.id}"` });
23103
+ }
23104
+ packIds.add(pack.id);
23105
+ });
23106
+ });
23107
+ var pricingCopySchema = external_exports.object({
23108
+ plans: external_exports.record(external_exports.string(), external_exports.object({ name: external_exports.string(), tagline: external_exports.string() }).partial()).optional(),
23109
+ features: external_exports.record(external_exports.string(), external_exports.string()).optional(),
23110
+ selfHosted: external_exports.object({ name: external_exports.string(), tagline: external_exports.string(), priceLabel: external_exports.string(), priceNote: external_exports.string(), cta: external_exports.string() }).partial().optional(),
23111
+ creditPacks: external_exports.record(external_exports.string(), external_exports.string()).optional(),
23112
+ ui: external_exports.object({
23113
+ perMonth: external_exports.string(),
23114
+ perYear: external_exports.string(),
23115
+ custom: external_exports.string(),
23116
+ free: external_exports.string(),
23117
+ unlimited: external_exports.string(),
23118
+ mostPopular: external_exports.string(),
23119
+ monthsFree: external_exports.string(),
23120
+ ctaStart: external_exports.string(),
23121
+ ctaChoose: external_exports.string(),
23122
+ ctaContact: external_exports.string(),
23123
+ billedMonthly: external_exports.string(),
23124
+ campaignBadge: external_exports.string(),
23125
+ campaignEnds: external_exports.string(),
23126
+ wasPrice: external_exports.string()
23127
+ }).partial().optional()
23128
+ });
23129
+
23130
+ // ../../packages/core/src/pricing/pricing.json
23131
+ var pricing_default = {
23132
+ schemaVersion: 1,
23133
+ currency: "usd",
23134
+ freeDomainSuffix: ".opsh.io",
23135
+ annual: {
23136
+ enabled: false,
23137
+ monthsFree: 2
23138
+ },
23139
+ oblien: {
23140
+ buildWorkspaceHeadroom: 2,
23141
+ buildResources: {
23142
+ cpuCores: 4,
23143
+ memoryMb: 8192,
23144
+ diskGb: 10
23145
+ }
23146
+ },
23147
+ campaigns: [],
23148
+ plans: [
23149
+ {
23150
+ id: "free",
23151
+ price: {
23152
+ monthly: 0,
23153
+ annual: null
23154
+ },
23155
+ stripePriceEnv: {
23156
+ monthly: null,
23157
+ annual: null
23158
+ },
23159
+ popular: false,
23160
+ contactSales: null,
23161
+ support: "community",
23162
+ credits: {
23163
+ monthlyMilli: 5e5
23164
+ },
23165
+ limits: {
23166
+ workloads: [
23167
+ "static"
23168
+ ],
23169
+ services: false,
23170
+ runningServices: 0,
23171
+ maxProjects: 3,
23172
+ maxResourceTier: "low",
23173
+ buildMinutesPerMonth: 15,
23174
+ freeSubdomains: 10,
23175
+ customDomains: null,
23176
+ seats: null
23177
+ },
23178
+ features: [
23179
+ "staticOnly",
23180
+ "buildMinutes",
23181
+ "freeSubdomains",
23182
+ "customDomains",
23183
+ "projects",
23184
+ "unlimitedSeats",
23185
+ "communitySupport"
23186
+ ]
23187
+ },
23188
+ {
23189
+ id: "starter",
23190
+ price: {
23191
+ monthly: 1e3,
23192
+ annual: null
23193
+ },
23194
+ stripePriceEnv: {
23195
+ monthly: "STRIPE_PRICE_STARTER_MONTHLY",
23196
+ annual: "STRIPE_PRICE_STARTER_ANNUAL"
23197
+ },
23198
+ popular: false,
23199
+ contactSales: null,
23200
+ support: "email",
23201
+ inherits: "free",
23202
+ credits: {
23203
+ monthlyMilli: 2e6
23204
+ },
23205
+ limits: {
23206
+ workloads: [
23207
+ "static",
23208
+ "web",
23209
+ "worker"
23210
+ ],
23211
+ services: true,
23212
+ runningServices: 3,
23213
+ maxProjects: 5,
23214
+ maxResourceTier: "medium",
23215
+ buildMinutesPerMonth: 400,
23216
+ freeSubdomains: 25,
23217
+ customDomains: null,
23218
+ seats: null
23219
+ },
23220
+ features: [
23221
+ "everythingIn",
23222
+ "anyWorkload",
23223
+ "managedServices",
23224
+ "runningServices",
23225
+ "power",
23226
+ "projects",
23227
+ "buildMinutes",
23228
+ "credits",
23229
+ "meteredCredits",
23230
+ "freeSubdomains",
23231
+ "previewDeploys",
23232
+ "emailSupport"
23233
+ ]
23234
+ },
23235
+ {
23236
+ id: "pro",
23237
+ price: {
23238
+ monthly: 3900,
23239
+ annual: null
23240
+ },
23241
+ stripePriceEnv: {
23242
+ monthly: "STRIPE_PRICE_PRO_MONTHLY",
23243
+ annual: "STRIPE_PRICE_PRO_ANNUAL"
23244
+ },
23245
+ popular: true,
23246
+ contactSales: null,
23247
+ support: "priority",
23248
+ inherits: "starter",
23249
+ credits: {
23250
+ monthlyMilli: 1e7
23251
+ },
23252
+ limits: {
23253
+ workloads: [
23254
+ "static",
23255
+ "web",
23256
+ "worker"
23257
+ ],
23258
+ services: true,
23259
+ runningServices: 10,
23260
+ maxProjects: 25,
23261
+ maxResourceTier: "high",
23262
+ buildMinutesPerMonth: 2e3,
23263
+ freeSubdomains: 100,
23264
+ customDomains: null,
23265
+ seats: null
23266
+ },
23267
+ features: [
23268
+ "everythingIn",
23269
+ "runningServices",
23270
+ "power",
23271
+ "projects",
23272
+ "buildMinutes",
23273
+ "credits",
23274
+ "meteredCredits",
23275
+ "freeSubdomains",
23276
+ "backups",
23277
+ "observability",
23278
+ "mailServer",
23279
+ "prioritySupport"
23280
+ ]
23281
+ },
23282
+ {
23283
+ id: "team",
23284
+ price: {
23285
+ monthly: 9900,
23286
+ annual: null
23287
+ },
23288
+ stripePriceEnv: {
23289
+ monthly: "STRIPE_PRICE_TEAM_MONTHLY",
23290
+ annual: "STRIPE_PRICE_TEAM_ANNUAL"
23291
+ },
23292
+ popular: false,
23293
+ contactSales: null,
23294
+ support: "priority",
23295
+ inherits: "pro",
23296
+ credits: {
23297
+ monthlyMilli: 6e7
23298
+ },
23299
+ limits: {
23300
+ workloads: [
23301
+ "static",
23302
+ "web",
23303
+ "worker"
23304
+ ],
23305
+ services: true,
23306
+ runningServices: 50,
23307
+ maxProjects: null,
23308
+ maxResourceTier: "xlarge",
23309
+ buildMinutesPerMonth: 6e3,
23310
+ freeSubdomains: null,
23311
+ customDomains: null,
23312
+ seats: null
23313
+ },
23314
+ features: [
23315
+ "everythingIn",
23316
+ "runningServices",
23317
+ "power",
23318
+ "projectsUnlimited",
23319
+ "buildMinutes",
23320
+ "credits",
23321
+ "meteredCredits",
23322
+ "freeSubdomainsUnlimited",
23323
+ "auditLog",
23324
+ "sharedChannel",
23325
+ "prioritySupport"
23326
+ ]
23327
+ },
23328
+ {
23329
+ id: "enterprise",
23330
+ price: {
23331
+ monthly: null,
23332
+ annual: null
23333
+ },
23334
+ stripePriceEnv: {
23335
+ monthly: null,
23336
+ annual: null
23337
+ },
23338
+ popular: false,
23339
+ contactSales: "mailto:sales@openship.io",
23340
+ support: "dedicated",
23341
+ inherits: "team",
23342
+ credits: {
23343
+ monthlyMilli: null
23344
+ },
23345
+ limits: {
23346
+ workloads: [
23347
+ "static",
23348
+ "web",
23349
+ "worker"
23350
+ ],
23351
+ services: true,
23352
+ runningServices: null,
23353
+ maxProjects: null,
23354
+ maxResourceTier: null,
23355
+ buildMinutesPerMonth: null,
23356
+ freeSubdomains: null,
23357
+ customDomains: null,
23358
+ seats: null
23359
+ },
23360
+ features: [
23361
+ "everythingIn",
23362
+ "customLimits",
23363
+ "sso",
23364
+ "dedicatedInfra",
23365
+ "onboarding",
23366
+ "dedicatedSupport"
23367
+ ]
23368
+ }
23369
+ ],
23370
+ creditPacks: [
23371
+ {
23372
+ id: "pack_5k",
23373
+ creditsMilli: 5e6,
23374
+ priceCents: 500,
23375
+ stripePriceEnv: "STRIPE_PRICE_PACK_5K",
23376
+ sortOrder: 10
23377
+ },
23378
+ {
23379
+ id: "pack_25k",
23380
+ creditsMilli: 25e6,
23381
+ priceCents: 2e3,
23382
+ stripePriceEnv: "STRIPE_PRICE_PACK_25K",
23383
+ sortOrder: 20
23384
+ },
23385
+ {
23386
+ id: "pack_100k",
23387
+ creditsMilli: 1e8,
23388
+ priceCents: 7e3,
23389
+ stripePriceEnv: "STRIPE_PRICE_PACK_100K",
23390
+ sortOrder: 30
23391
+ }
23392
+ ],
23393
+ selfHosted: {
23394
+ features: [
23395
+ "selfHostedOpenSource",
23396
+ "selfHostedUnlimited",
23397
+ "selfHostedServices",
23398
+ "selfHostedClients",
23399
+ "selfHostedFreeSubdomains",
23400
+ "communitySupport"
23401
+ ]
23402
+ }
23403
+ };
23404
+
23405
+ // ../../packages/core/src/pricing/locales/en.json
23406
+ var en_default2 = {
23407
+ plans: {
23408
+ free: {
23409
+ name: "Free",
23410
+ tagline: "Ship static sites on Openship Cloud at no cost."
23411
+ },
23412
+ starter: {
23413
+ name: "Starter",
23414
+ tagline: "For your first real app \u2014 containers, databases, the works."
23415
+ },
23416
+ pro: {
23417
+ name: "Pro",
23418
+ tagline: "For production apps that need room to grow."
23419
+ },
23420
+ team: {
23421
+ name: "Scale",
23422
+ tagline: "For teams running serious traffic on shared infrastructure."
23423
+ },
23424
+ enterprise: {
23425
+ name: "Enterprise",
23426
+ tagline: "Custom limits, dedicated infrastructure, and an SLA."
23427
+ }
23428
+ },
23429
+ features: {
23430
+ everythingIn: "Everything in {inherited}, plus:",
23431
+ staticOnly: "Static sites only \u2014 SSG, SPA, and plain HTML",
23432
+ anyWorkload: "Run anything \u2014 containers, Compose stacks, and workers",
23433
+ managedServices: "Managed Postgres, Redis, and the full app catalog",
23434
+ buildMinutes: "{buildMinutes} build minutes per month",
23435
+ credits: "{credits} compute credits per month",
23436
+ freeSubdomains: "{freeSubdomains} free {freeDomainSuffix} subdomains",
23437
+ freeSubdomainsUnlimited: "Unlimited free {freeDomainSuffix} subdomains",
23438
+ customDomains: "Unlimited custom domains, TLS included",
23439
+ unlimitedSeats: "Unlimited team members \u2014 never pay per seat",
23440
+ previewDeploys: "Preview deploys on every push",
23441
+ backups: "Daily backups with point-in-time recovery",
23442
+ observability: "Live logs, metrics, and health alerts",
23443
+ mailServer: "Built-in mail server \u2014 SMTP, IMAP, and webmail",
23444
+ auditLog: "Audit log with extended retention",
23445
+ sharedChannel: "Shared Slack or Discord channel",
23446
+ sso: "SSO and SAML",
23447
+ customLimits: "Custom limits, negotiated per contract",
23448
+ dedicatedInfra: "Dedicated infrastructure and regions",
23449
+ onboarding: "White-glove onboarding and migration",
23450
+ communitySupport: "Community support",
23451
+ emailSupport: "Email support",
23452
+ prioritySupport: "Priority support",
23453
+ dedicatedSupport: "Dedicated support with an SLA",
23454
+ selfHostedOpenSource: "The whole platform, open source (Apache 2.0)",
23455
+ selfHostedUnlimited: "Unlimited deploys, projects, servers, and domains",
23456
+ selfHostedServices: "Every managed service \u2014 Postgres, Redis, mail",
23457
+ selfHostedClients: "CLI, web, and desktop \u2014 one backend",
23458
+ selfHostedFreeSubdomains: "{freeSubdomains} free {freeDomainSuffix} subdomains when you connect to Cloud",
23459
+ runningServices: "{runningServices} running services \u2014 apps, databases, workers",
23460
+ power: "Up to {powerCpu} vCPU and {powerRamGb} GB RAM per service",
23461
+ projects: "{maxProjects} projects",
23462
+ projectsUnlimited: "Unlimited projects",
23463
+ meteredCredits: "Compute, bandwidth and storage all draw from your monthly credits"
23464
+ },
23465
+ selfHosted: {
23466
+ name: "Self-hosted",
23467
+ tagline: "Run the whole platform on machines you own. Any Linux box, any provider, any region. No metering, no seat caps, no telemetry.",
23468
+ priceLabel: "Free",
23469
+ priceNote: "Apache 2.0 \u2014 free forever, no credit card",
23470
+ cta: "Start self-hosting"
23471
+ },
23472
+ creditPacks: {
23473
+ pack_5k: "{credits} credits",
23474
+ pack_25k: "{credits} credits",
23475
+ pack_100k: "{credits} credits"
23476
+ },
23477
+ ui: {
23478
+ perMonth: "/mo",
23479
+ perYear: "/yr",
23480
+ custom: "Custom",
23481
+ free: "Free",
23482
+ unlimited: "Unlimited",
23483
+ mostPopular: "Most popular",
23484
+ monthsFree: "{months} months free",
23485
+ ctaStart: "Start for free",
23486
+ ctaChoose: "Choose {name}",
23487
+ ctaContact: "Talk to sales",
23488
+ billedMonthly: "billed monthly, cancel anytime",
23489
+ campaignBadge: "{percentOff}% off",
23490
+ campaignEnds: "Offer ends {date}",
23491
+ wasPrice: "was {price}"
23492
+ }
23493
+ };
23494
+
23495
+ // ../../packages/core/src/pricing/locales/ar.json
23496
+ var ar_default2 = {
23497
+ plans: {
23498
+ free: {
23499
+ name: "\u0645\u062C\u0627\u0646\u064A",
23500
+ tagline: "\u0627\u0646\u0634\u0631 \u0627\u0644\u0645\u0648\u0627\u0642\u0639 \u0627\u0644\u062B\u0627\u0628\u062A\u0629 \u0639\u0644\u0649 Openship Cloud \u062F\u0648\u0646 \u0623\u064A \u062A\u0643\u0644\u0641\u0629."
23501
+ },
23502
+ starter: {
23503
+ name: "\u0627\u0644\u0628\u062F\u0627\u064A\u0629",
23504
+ tagline: "\u0644\u0623\u0648\u0644 \u062A\u0637\u0628\u064A\u0642 \u062D\u0642\u064A\u0642\u064A \u0644\u0643 \u2014 \u062D\u0627\u0648\u064A\u0627\u062A \u0648\u0642\u0648\u0627\u0639\u062F \u0628\u064A\u0627\u0646\u0627\u062A \u0648\u0643\u0644 \u0645\u0627 \u062A\u062D\u062A\u0627\u062C\u0647."
23505
+ },
23506
+ pro: {
23507
+ name: "\u0627\u062D\u062A\u0631\u0627\u0641\u064A",
23508
+ tagline: "\u0644\u062A\u0637\u0628\u064A\u0642\u0627\u062A \u0627\u0644\u0625\u0646\u062A\u0627\u062C \u0627\u0644\u062A\u064A \u062A\u062D\u062A\u0627\u062C \u0645\u0633\u0627\u062D\u0629 \u0644\u0644\u0646\u0645\u0648."
23509
+ },
23510
+ team: {
23511
+ name: "\u0627\u0644\u062A\u0648\u0633\u0651\u0639",
23512
+ tagline: "\u0644\u0644\u0641\u0631\u0642 \u0627\u0644\u062A\u064A \u062A\u062F\u064A\u0631 \u062D\u0631\u0643\u0629 \u0645\u0631\u0648\u0631 \u0643\u062B\u064A\u0641\u0629 \u0639\u0644\u0649 \u0628\u0646\u064A\u0629 \u062A\u062D\u062A\u064A\u0629 \u0645\u0634\u062A\u0631\u0643\u0629."
23513
+ },
23514
+ enterprise: {
23515
+ name: "\u0627\u0644\u0645\u0624\u0633\u0633\u0627\u062A",
23516
+ tagline: "\u062D\u062F\u0648\u062F \u0645\u062E\u0635\u0651\u0635\u0629 \u0648\u0628\u0646\u064A\u0629 \u062A\u062D\u062A\u064A\u0629 \u0645\u062E\u0635\u0651\u0635\u0629 \u0648\u0627\u062A\u0641\u0627\u0642\u064A\u0629 SLA."
23517
+ }
23518
+ },
23519
+ features: {
23520
+ everythingIn: "\u0643\u0644 \u0645\u0627 \u0641\u064A \u062E\u0637\u0629 \xAB{inherited}\xBB\u060C \u0628\u0627\u0644\u0625\u0636\u0627\u0641\u0629 \u0625\u0644\u0649:",
23521
+ staticOnly: "\u0627\u0644\u0645\u0648\u0627\u0642\u0639 \u0627\u0644\u062B\u0627\u0628\u062A\u0629 \u0641\u0642\u0637 \u2014 SSG \u0648SPA \u0648HTML \u0639\u0627\u062F\u064A",
23522
+ anyWorkload: "\u0634\u063A\u0651\u0644 \u0623\u064A \u0634\u064A\u0621 \u2014 \u062D\u0627\u0648\u064A\u0627\u062A \u0648\u062D\u0650\u0632\u0645 Compose \u0648\u0639\u0645\u0644\u064A\u0627\u062A \u062E\u0644\u0641\u064A\u0629",
23523
+ managedServices: "\u062E\u062F\u0645\u0627\u062A Postgres \u0648Redis \u0645\u064F\u062F\u0627\u0631\u0629\u060C \u0645\u0639 \u0643\u062A\u0627\u0644\u0648\u062C \u0627\u0644\u062A\u0637\u0628\u064A\u0642\u0627\u062A \u0627\u0644\u0643\u0627\u0645\u0644",
23524
+ buildMinutes: "{buildMinutes} \u062F\u0642\u064A\u0642\u0629 \u0628\u0646\u0627\u0621 \u0634\u0647\u0631\u064A\u064B\u0627",
23525
+ credits: "{credits} \u0648\u062D\u062F\u0629 \u0631\u0635\u064A\u062F \u062D\u0648\u0633\u0628\u0629 \u0634\u0647\u0631\u064A\u064B\u0627",
23526
+ freeSubdomains: "{freeSubdomains} \u0646\u0637\u0627\u0642 \u0641\u0631\u0639\u064A \u0645\u062C\u0627\u0646\u064A \u0639\u0644\u0649 {freeDomainSuffix}",
23527
+ freeSubdomainsUnlimited: "\u0646\u0637\u0627\u0642\u0627\u062A \u0641\u0631\u0639\u064A\u0629 \u0645\u062C\u0627\u0646\u064A\u0629 \u063A\u064A\u0631 \u0645\u062D\u062F\u0648\u062F\u0629 \u0639\u0644\u0649 {freeDomainSuffix}",
23528
+ customDomains: "\u0646\u0637\u0627\u0642\u0627\u062A \u0645\u062E\u0635\u0651\u0635\u0629 \u063A\u064A\u0631 \u0645\u062D\u062F\u0648\u062F\u0629\u060C \u0645\u0639 TLS",
23529
+ unlimitedSeats: "\u0623\u0639\u0636\u0627\u0621 \u0641\u0631\u064A\u0642 \u063A\u064A\u0631 \u0645\u062D\u062F\u0648\u062F\u064A\u0646 \u2014 \u0628\u0644\u0627 \u0631\u0633\u0648\u0645 \u0639\u0644\u0649 \u0643\u0644 \u0645\u0633\u062A\u062E\u062F\u0645",
23530
+ previewDeploys: "\u0646\u0634\u0631 \u0645\u0639\u0627\u064A\u0646\u0629 \u0639\u0646\u062F \u0643\u0644 \u062F\u0641\u0639 \u0644\u0644\u0643\u0648\u062F",
23531
+ backups: "\u0646\u0633\u062E \u0627\u062D\u062A\u064A\u0627\u0637\u064A \u064A\u0648\u0645\u064A \u0645\u0639 \u0627\u0633\u062A\u0639\u0627\u062F\u0629 \u0625\u0644\u0649 \u0644\u062D\u0638\u0629 \u0632\u0645\u0646\u064A\u0629 \u0645\u062D\u062F\u0651\u062F\u0629",
23532
+ observability: "\u0633\u062C\u0644\u0627\u062A \u062D\u064A\u0651\u0629 \u0648\u0645\u0642\u0627\u064A\u064A\u0633 \u0648\u062A\u0646\u0628\u064A\u0647\u0627\u062A \u0644\u0635\u062D\u0629 \u0627\u0644\u062E\u062F\u0645\u0629",
23533
+ mailServer: "\u062E\u0627\u062F\u0645 \u0628\u0631\u064A\u062F \u0645\u062F\u0645\u062C \u2014 SMTP \u0648IMAP \u0648\u0628\u0631\u064A\u062F \u0639\u0644\u0649 \u0627\u0644\u0648\u064A\u0628",
23534
+ auditLog: "\u0633\u062C\u0644 \u062A\u062F\u0642\u064A\u0642 \u0628\u0645\u062F\u0629 \u062D\u0641\u0638 \u0645\u0645\u062A\u062F\u0629",
23535
+ sharedChannel: "\u0642\u0646\u0627\u0629 \u0645\u0634\u062A\u0631\u0643\u0629 \u0639\u0644\u0649 Slack \u0623\u0648 Discord",
23536
+ sso: "SSO \u0648SAML",
23537
+ customLimits: "\u062D\u062F\u0648\u062F \u0645\u062E\u0635\u0651\u0635\u0629 \u064A\u064F\u062A\u0641\u0642 \u0639\u0644\u064A\u0647\u0627 \u0641\u064A \u0627\u0644\u0639\u0642\u062F",
23538
+ dedicatedInfra: "\u0628\u0646\u064A\u0629 \u062A\u062D\u062A\u064A\u0629 \u0648\u0645\u0646\u0627\u0637\u0642 \u0645\u062E\u0635\u0651\u0635\u0629",
23539
+ onboarding: "\u0625\u0639\u062F\u0627\u062F \u0648\u062A\u0631\u062D\u064A\u0644 \u0628\u0645\u062A\u0627\u0628\u0639\u0629 \u0643\u0627\u0645\u0644\u0629 \u0645\u0646 \u0641\u0631\u064A\u0642\u0646\u0627",
23540
+ communitySupport: "\u062F\u0639\u0645 \u0645\u0646 \u0627\u0644\u0645\u062C\u062A\u0645\u0639",
23541
+ emailSupport: "\u062F\u0639\u0645 \u0639\u0628\u0631 \u0627\u0644\u0628\u0631\u064A\u062F \u0627\u0644\u0625\u0644\u0643\u062A\u0631\u0648\u0646\u064A",
23542
+ prioritySupport: "\u062F\u0639\u0645 \u0630\u0648 \u0623\u0648\u0644\u0648\u064A\u0629",
23543
+ dedicatedSupport: "\u062F\u0639\u0645 \u0645\u062E\u0635\u0651\u0635 \u0645\u0639 \u0627\u062A\u0641\u0627\u0642\u064A\u0629 SLA",
23544
+ selfHostedOpenSource: "\u0627\u0644\u0645\u0646\u0635\u0651\u0629 \u0643\u0627\u0645\u0644\u0629\u060C \u0645\u0641\u062A\u0648\u062D\u0629 \u0627\u0644\u0645\u0635\u062F\u0631 (Apache 2.0)",
23545
+ selfHostedUnlimited: "\u0639\u0645\u0644\u064A\u0627\u062A \u0646\u0634\u0631 \u0648\u0645\u0634\u0627\u0631\u064A\u0639 \u0648\u062E\u0648\u0627\u062F\u0645 \u0648\u0646\u0637\u0627\u0642\u0627\u062A \u063A\u064A\u0631 \u0645\u062D\u062F\u0648\u062F\u0629",
23546
+ selfHostedServices: "\u0643\u0644 \u0627\u0644\u062E\u062F\u0645\u0627\u062A \u0627\u0644\u0645\u064F\u062F\u0627\u0631\u0629 \u2014 Postgres \u0648Redis \u0648\u0627\u0644\u0628\u0631\u064A\u062F",
23547
+ selfHostedClients: "CLI \u0648\u0648\u0627\u062C\u0647\u0629 \u0648\u064A\u0628 \u0648\u062A\u0637\u0628\u064A\u0642 \u0633\u0637\u062D \u0645\u0643\u062A\u0628 \u2014 \u062E\u062F\u0645\u0629 \u062E\u0644\u0641\u064A\u0629 \u0648\u0627\u062D\u062F\u0629",
23548
+ selfHostedFreeSubdomains: "{freeSubdomains} \u0646\u0637\u0627\u0642 \u0641\u0631\u0639\u064A \u0645\u062C\u0627\u0646\u064A \u0639\u0644\u0649 {freeDomainSuffix} \u0639\u0646\u062F \u0627\u0644\u0631\u0628\u0637 \u0645\u0639 Openship Cloud",
23549
+ runningServices: "{runningServices} \u062E\u062F\u0645\u0629 \u0642\u064A\u062F \u0627\u0644\u062A\u0634\u063A\u064A\u0644 \u2014 \u062A\u0637\u0628\u064A\u0642\u0627\u062A \u0648\u0642\u0648\u0627\u0639\u062F \u0628\u064A\u0627\u0646\u0627\u062A \u0648\u0639\u0645\u0644\u064A\u0627\u062A \u062E\u0644\u0641\u064A\u0629",
23550
+ power: "\u062D\u062A\u0649 {powerCpu} vCPU \u0648{powerRamGb} GB RAM \u0644\u0643\u0644 \u062E\u062F\u0645\u0629",
23551
+ projects: "{maxProjects} \u0645\u0634\u0631\u0648\u0639",
23552
+ projectsUnlimited: "\u0645\u0634\u0627\u0631\u064A\u0639 \u063A\u064A\u0631 \u0645\u062D\u062F\u0648\u062F\u0629",
23553
+ meteredCredits: "\u0627\u0644\u062D\u0648\u0633\u0628\u0629 \u0648\u0627\u0644\u0646\u0637\u0627\u0642 \u0627\u0644\u062A\u0631\u062F\u062F\u064A \u0648\u0627\u0644\u062A\u062E\u0632\u064A\u0646 \u062A\u064F\u062E\u0635\u0645 \u0643\u0644\u0647\u0627 \u0645\u0646 \u0631\u0635\u064A\u062F\u0643 \u0627\u0644\u0634\u0647\u0631\u064A"
23554
+ },
23555
+ selfHosted: {
23556
+ name: "\u0627\u0633\u062A\u0636\u0627\u0641\u0629 \u0630\u0627\u062A\u064A\u0629",
23557
+ tagline: "\u0634\u063A\u0651\u0644 \u0627\u0644\u0645\u0646\u0635\u0651\u0629 \u0643\u0627\u0645\u0644\u0629 \u0639\u0644\u0649 \u0623\u062C\u0647\u0632\u0629 \u062A\u0645\u0644\u0643\u0647\u0627. \u0623\u064A \u062C\u0647\u0627\u0632 Linux\u060C \u0648\u0623\u064A \u0645\u0632\u0648\u0651\u062F\u060C \u0648\u0623\u064A \u0645\u0646\u0637\u0642\u0629. \u0628\u0644\u0627 \u0642\u064A\u0627\u0633 \u0627\u0633\u062A\u0647\u0644\u0627\u0643\u060C \u0648\u0628\u0644\u0627 \u062D\u062F \u0644\u0639\u062F\u062F \u0627\u0644\u0645\u0633\u062A\u062E\u062F\u0645\u064A\u0646\u060C \u0648\u0628\u0644\u0627 \u062A\u062A\u0628\u0651\u0639.",
23558
+ priceLabel: "\u0645\u062C\u0627\u0646\u064A",
23559
+ priceNote: "Apache 2.0 \u2014 \u0645\u062C\u0627\u0646\u064A \u062F\u0627\u0626\u0645\u064B\u0627\u060C \u062F\u0648\u0646 \u0628\u0637\u0627\u0642\u0629 \u0627\u0626\u062A\u0645\u0627\u0646",
23560
+ cta: "\u0627\u0628\u062F\u0623 \u0627\u0644\u0627\u0633\u062A\u0636\u0627\u0641\u0629 \u0627\u0644\u0630\u0627\u062A\u064A\u0629"
23561
+ },
23562
+ creditPacks: {
23563
+ pack_5k: "{credits} \u0648\u062D\u062F\u0629 \u0631\u0635\u064A\u062F",
23564
+ pack_25k: "{credits} \u0648\u062D\u062F\u0629 \u0631\u0635\u064A\u062F",
23565
+ pack_100k: "{credits} \u0648\u062D\u062F\u0629 \u0631\u0635\u064A\u062F"
23566
+ },
23567
+ ui: {
23568
+ perMonth: "/\u0634\u0647\u0631",
23569
+ perYear: "/\u0633\u0646\u0629",
23570
+ custom: "\u0645\u062E\u0635\u0651\u0635",
23571
+ free: "\u0645\u062C\u0627\u0646\u064B\u0627",
23572
+ unlimited: "\u063A\u064A\u0631 \u0645\u062D\u062F\u0648\u062F",
23573
+ mostPopular: "\u0627\u0644\u0623\u0643\u062B\u0631 \u0634\u064A\u0648\u0639\u064B\u0627",
23574
+ monthsFree: "{months} \u0634\u0647\u0631 \u0645\u062C\u0627\u0646\u064B\u0627",
23575
+ ctaStart: "\u0627\u0628\u062F\u0623 \u0645\u062C\u0627\u0646\u064B\u0627",
23576
+ ctaChoose: "\u0627\u062E\u062A\u0631 \u062E\u0637\u0629 {name}",
23577
+ ctaContact: "\u062A\u0648\u0627\u0635\u0644 \u0645\u0639 \u0641\u0631\u064A\u0642 \u0627\u0644\u0645\u0628\u064A\u0639\u0627\u062A",
23578
+ billedMonthly: "\u0641\u0627\u062A\u0648\u0631\u0629 \u0634\u0647\u0631\u064A\u0629\u060C \u0648\u0627\u0644\u0625\u0644\u063A\u0627\u0621 \u0645\u062A\u0627\u062D \u0641\u064A \u0623\u064A \u0648\u0642\u062A",
23579
+ campaignBadge: "\u062E\u0635\u0645 {percentOff}%",
23580
+ campaignEnds: "\u064A\u0646\u062A\u0647\u064A \u0627\u0644\u0639\u0631\u0636 \u0641\u064A {date}",
23581
+ wasPrice: "\u0633\u0627\u0628\u0642\u064B\u0627 {price}"
23582
+ }
23583
+ };
23584
+
23585
+ // ../../packages/core/src/pricing/locales/de.json
23586
+ var de_default2 = {
23587
+ plans: {
23588
+ free: {
23589
+ name: "Free",
23590
+ tagline: "Statische Websites kostenlos auf Openship Cloud deployen."
23591
+ },
23592
+ starter: {
23593
+ name: "Starter",
23594
+ tagline: "F\xFCr die erste echte App \u2013 Container, Datenbanken, alles dabei."
23595
+ },
23596
+ pro: {
23597
+ name: "Pro",
23598
+ tagline: "F\xFCr Produktions-Apps, die Platz zum Wachsen brauchen."
23599
+ },
23600
+ team: {
23601
+ name: "Scale",
23602
+ tagline: "F\xFCr Teams mit hohem Traffic auf gemeinsamer Infrastruktur."
23603
+ },
23604
+ enterprise: {
23605
+ name: "Enterprise",
23606
+ tagline: "Individuelle Limits, dedizierte Infrastruktur und ein SLA."
23607
+ }
23608
+ },
23609
+ features: {
23610
+ everythingIn: "Alles aus {inherited}, plus:",
23611
+ staticOnly: "Nur statische Websites \u2013 SSG, SPA und reines HTML",
23612
+ anyWorkload: "Beliebige Workloads \u2013 Container, Compose-Stacks und Worker",
23613
+ managedServices: "Managed Postgres, Redis und der komplette App-Katalog",
23614
+ buildMinutes: "{buildMinutes} Build-Minuten pro Monat",
23615
+ credits: "{credits} Compute-Credits pro Monat",
23616
+ freeSubdomains: "{freeSubdomains} kostenlose Subdomains unter {freeDomainSuffix}",
23617
+ freeSubdomainsUnlimited: "Unbegrenzt viele kostenlose Subdomains unter {freeDomainSuffix}",
23618
+ customDomains: "Unbegrenzt viele eigene Domains, TLS inklusive",
23619
+ unlimitedSeats: "Unbegrenzt viele Teammitglieder \u2013 nie Kosten pro Nutzer",
23620
+ previewDeploys: "Preview-Deploys bei jedem Push",
23621
+ backups: "T\xE4gliche Backups mit Point-in-Time-Recovery",
23622
+ observability: "Live-Logs, Metriken und Health-Alerts",
23623
+ mailServer: "Integrierter Mailserver \u2013 SMTP, IMAP und Webmail",
23624
+ auditLog: "Audit-Log mit verl\xE4ngerter Aufbewahrungsdauer",
23625
+ sharedChannel: "Gemeinsamer Slack- oder Discord-Channel",
23626
+ sso: "SSO und SAML",
23627
+ customLimits: "Individuelle Limits, vertraglich vereinbart",
23628
+ dedicatedInfra: "Dedizierte Infrastruktur und Regionen",
23629
+ onboarding: "Pers\xF6nlich begleitetes Onboarding und Migration",
23630
+ communitySupport: "Community-Support",
23631
+ emailSupport: "E-Mail-Support",
23632
+ prioritySupport: "Priorisierter Support",
23633
+ dedicatedSupport: "Dedizierter Support mit SLA",
23634
+ selfHostedOpenSource: "Die komplette Plattform als Open Source (Apache 2.0)",
23635
+ selfHostedUnlimited: "Unbegrenzt viele Deploys, Projekte, Server und Domains",
23636
+ selfHostedServices: "Alle Managed Services \u2013 Postgres, Redis, Mail",
23637
+ selfHostedClients: "CLI, Web und Desktop \u2013 ein Backend",
23638
+ selfHostedFreeSubdomains: "{freeSubdomains} kostenlose Subdomains unter {freeDomainSuffix} bei Verbindung mit Openship Cloud",
23639
+ runningServices: "{runningServices} laufende Services \u2013 Apps, Datenbanken, Worker",
23640
+ power: "Bis zu {powerCpu} vCPU und {powerRamGb} GB RAM pro Service",
23641
+ projects: "{maxProjects} Projekte",
23642
+ projectsUnlimited: "Unbegrenzt viele Projekte",
23643
+ meteredCredits: "Compute, Daten\xFCbertragung und Speicher werden alle \xFCber die monatlichen Credits abgerechnet"
23644
+ },
23645
+ selfHosted: {
23646
+ name: "Selbst gehostet",
23647
+ tagline: "Die komplette Plattform auf eigenen Maschinen betreiben. Jeder Linux-Server, jeder Anbieter, jede Region. Keine verbrauchsabh\xE4ngige Abrechnung, keine Nutzerlimits, keine Telemetrie.",
23648
+ priceLabel: "Kostenlos",
23649
+ priceNote: "Apache 2.0 \u2013 f\xFCr immer kostenlos, keine Kreditkarte",
23650
+ cta: "Self-Hosting starten"
23651
+ },
23652
+ creditPacks: {
23653
+ pack_5k: "{credits} Credits",
23654
+ pack_25k: "{credits} Credits",
23655
+ pack_100k: "{credits} Credits"
23656
+ },
23657
+ ui: {
23658
+ perMonth: "/Mon.",
23659
+ perYear: "/Jahr",
23660
+ custom: "Individuell",
23661
+ free: "Kostenlos",
23662
+ unlimited: "Unbegrenzt",
23663
+ mostPopular: "Am beliebtesten",
23664
+ monthsFree: "{months} Monate kostenlos",
23665
+ ctaStart: "Kostenlos starten",
23666
+ ctaChoose: "{name} w\xE4hlen",
23667
+ ctaContact: "Vertrieb kontaktieren",
23668
+ billedMonthly: "monatliche Abrechnung, jederzeit k\xFCndbar",
23669
+ campaignBadge: "{percentOff} % Rabatt",
23670
+ campaignEnds: "Angebot endet am {date}",
23671
+ wasPrice: "vorher {price}"
23672
+ }
23673
+ };
23674
+
23675
+ // ../../packages/core/src/pricing/locales/es.json
23676
+ var es_default2 = {
23677
+ plans: {
23678
+ free: {
23679
+ name: "Free",
23680
+ tagline: "Publica sitios est\xE1ticos en Openship Cloud sin costo."
23681
+ },
23682
+ starter: {
23683
+ name: "Starter",
23684
+ tagline: "Para tu primera app en serio: contenedores, bases de datos y todo lo necesario."
23685
+ },
23686
+ pro: {
23687
+ name: "Pro",
23688
+ tagline: "Para apps en producci\xF3n que necesitan espacio para crecer."
23689
+ },
23690
+ team: {
23691
+ name: "Scale",
23692
+ tagline: "Para equipos que mueven tr\xE1fico serio sobre infraestructura compartida."
23693
+ },
23694
+ enterprise: {
23695
+ name: "Enterprise",
23696
+ tagline: "L\xEDmites a medida, infraestructura dedicada y un SLA."
23697
+ }
23698
+ },
23699
+ features: {
23700
+ everythingIn: "Todo lo de {inherited}, y adem\xE1s:",
23701
+ staticOnly: "Solo sitios est\xE1ticos: SSG, SPA y HTML puro",
23702
+ anyWorkload: "Ejecuta lo que sea: contenedores, stacks de Compose y workers",
23703
+ managedServices: "Postgres y Redis gestionados, y el cat\xE1logo completo de apps",
23704
+ buildMinutes: "{buildMinutes} minutos de build al mes",
23705
+ credits: "{credits} cr\xE9ditos de c\xF3mputo al mes",
23706
+ freeSubdomains: "{freeSubdomains} subdominios {freeDomainSuffix} gratis",
23707
+ freeSubdomainsUnlimited: "Subdominios {freeDomainSuffix} gratis e ilimitados",
23708
+ customDomains: "Dominios propios ilimitados, con TLS incluido",
23709
+ unlimitedSeats: "Miembros de equipo ilimitados: nunca pagas por usuario",
23710
+ previewDeploys: "Despliegues de vista previa en cada push",
23711
+ backups: "Copias de seguridad diarias con recuperaci\xF3n a un punto en el tiempo",
23712
+ observability: "Logs en vivo, m\xE9tricas y alertas de estado",
23713
+ mailServer: "Servidor de correo integrado: SMTP, IMAP y webmail",
23714
+ auditLog: "Registro de auditor\xEDa con retenci\xF3n ampliada",
23715
+ sharedChannel: "Canal compartido en Slack o Discord",
23716
+ sso: "SSO y SAML",
23717
+ customLimits: "L\xEDmites a medida, negociados por contrato",
23718
+ dedicatedInfra: "Infraestructura y regiones dedicadas",
23719
+ onboarding: "Puesta en marcha y migraci\xF3n asistidas paso a paso",
23720
+ communitySupport: "Soporte de la comunidad",
23721
+ emailSupport: "Soporte por correo",
23722
+ prioritySupport: "Soporte prioritario",
23723
+ dedicatedSupport: "Soporte dedicado con SLA",
23724
+ selfHostedOpenSource: "La plataforma completa, de c\xF3digo abierto (Apache 2.0)",
23725
+ selfHostedUnlimited: "Despliegues, proyectos, servidores y dominios ilimitados",
23726
+ selfHostedServices: "Todos los servicios gestionados: Postgres, Redis, correo",
23727
+ selfHostedClients: "CLI, web y escritorio: un solo backend",
23728
+ selfHostedFreeSubdomains: "{freeSubdomains} subdominios {freeDomainSuffix} gratis al conectarte a Cloud",
23729
+ runningServices: "{runningServices} servicios en ejecuci\xF3n: apps, bases de datos, workers",
23730
+ power: "Hasta {powerCpu} vCPU y {powerRamGb} GB de RAM por servicio",
23731
+ projects: "{maxProjects} proyectos",
23732
+ projectsUnlimited: "Proyectos ilimitados",
23733
+ meteredCredits: "El c\xF3mputo, el ancho de banda y el almacenamiento se descuentan de tus cr\xE9ditos mensuales"
23734
+ },
23735
+ selfHosted: {
23736
+ name: "Autoalojado",
23737
+ tagline: "Ejecuta la plataforma completa en m\xE1quinas propias. Cualquier equipo Linux, cualquier proveedor, cualquier regi\xF3n. Sin medici\xF3n de consumo, sin l\xEDmite de usuarios, sin telemetr\xEDa.",
23738
+ priceLabel: "Gratis",
23739
+ priceNote: "Apache 2.0: gratis para siempre, sin tarjeta de cr\xE9dito",
23740
+ cta: "Empezar a autoalojar"
23741
+ },
23742
+ creditPacks: {
23743
+ pack_5k: "{credits} cr\xE9ditos",
23744
+ pack_25k: "{credits} cr\xE9ditos",
23745
+ pack_100k: "{credits} cr\xE9ditos"
23746
+ },
23747
+ ui: {
23748
+ perMonth: "/mes",
23749
+ perYear: "/a\xF1o",
23750
+ custom: "A medida",
23751
+ free: "Gratis",
23752
+ unlimited: "Ilimitado",
23753
+ mostPopular: "El m\xE1s popular",
23754
+ monthsFree: "{months} meses gratis",
23755
+ ctaStart: "Empezar gratis",
23756
+ ctaChoose: "Elegir {name}",
23757
+ ctaContact: "Hablar con ventas",
23758
+ billedMonthly: "facturaci\xF3n mensual, cancela cuando quieras",
23759
+ campaignBadge: "{percentOff}% de descuento",
23760
+ campaignEnds: "La oferta termina el {date}",
23761
+ wasPrice: "antes {price}"
23762
+ }
23763
+ };
23764
+
23765
+ // ../../packages/core/src/pricing/locales/fr.json
23766
+ var fr_default2 = {
23767
+ plans: {
23768
+ free: {
23769
+ name: "Free",
23770
+ tagline: "D\xE9ployez des sites statiques sur Openship Cloud, gratuitement."
23771
+ },
23772
+ starter: {
23773
+ name: "Starter",
23774
+ tagline: "Pour votre premi\xE8re vraie application \u2014 conteneurs, bases de donn\xE9es et tout le reste."
23775
+ },
23776
+ pro: {
23777
+ name: "Pro",
23778
+ tagline: "Pour les applications en production qui ont besoin de marge pour grandir."
23779
+ },
23780
+ team: {
23781
+ name: "Scale",
23782
+ tagline: "Pour les \xE9quipes qui encaissent un trafic s\xE9rieux sur une infrastructure partag\xE9e."
23783
+ },
23784
+ enterprise: {
23785
+ name: "Enterprise",
23786
+ tagline: "Limites sur mesure, infrastructure d\xE9di\xE9e et un SLA."
23787
+ }
23788
+ },
23789
+ features: {
23790
+ everythingIn: "Tout ce que contient {inherited}, plus :",
23791
+ staticOnly: "Sites statiques uniquement \u2014 SSG, SPA et HTML brut",
23792
+ anyWorkload: "Ex\xE9cutez ce que vous voulez \u2014 conteneurs, stacks Compose et workers",
23793
+ managedServices: "Postgres et Redis manag\xE9s, et tout le catalogue d'applications",
23794
+ buildMinutes: "{buildMinutes} minutes de build par mois",
23795
+ credits: "{credits} cr\xE9dits de calcul par mois",
23796
+ freeSubdomains: "{freeSubdomains} sous-domaines {freeDomainSuffix} gratuits",
23797
+ freeSubdomainsUnlimited: "Sous-domaines {freeDomainSuffix} gratuits en illimit\xE9",
23798
+ customDomains: "Domaines personnalis\xE9s illimit\xE9s, TLS inclus",
23799
+ unlimitedSeats: "Membres d'\xE9quipe illimit\xE9s \u2014 jamais de facturation par utilisateur",
23800
+ previewDeploys: "D\xE9ploiements de pr\xE9visualisation \xE0 chaque push",
23801
+ backups: "Sauvegardes quotidiennes avec restauration \xE0 un instant pr\xE9cis",
23802
+ observability: "Logs en direct, m\xE9triques et alertes de sant\xE9",
23803
+ mailServer: "Serveur mail int\xE9gr\xE9 \u2014 SMTP, IMAP et webmail",
23804
+ auditLog: "Journal d'audit avec r\xE9tention \xE9tendue",
23805
+ sharedChannel: "Canal Slack ou Discord partag\xE9",
23806
+ sso: "SSO et SAML",
23807
+ customLimits: "Limites sur mesure, n\xE9goci\xE9es par contrat",
23808
+ dedicatedInfra: "Infrastructure et r\xE9gions d\xE9di\xE9es",
23809
+ onboarding: "Mise en route et migration accompagn\xE9es",
23810
+ communitySupport: "Support communautaire",
23811
+ emailSupport: "Support par e-mail",
23812
+ prioritySupport: "Support prioritaire",
23813
+ dedicatedSupport: "Support d\xE9di\xE9 avec SLA",
23814
+ selfHostedOpenSource: "Toute la plateforme, en open source (Apache 2.0)",
23815
+ selfHostedUnlimited: "D\xE9ploiements, projets, serveurs et domaines illimit\xE9s",
23816
+ selfHostedServices: "Tous les services manag\xE9s \u2014 Postgres, Redis, mail",
23817
+ selfHostedClients: "CLI, web et desktop \u2014 un seul backend",
23818
+ selfHostedFreeSubdomains: "{freeSubdomains} sous-domaines {freeDomainSuffix} gratuits en vous connectant au Cloud",
23819
+ runningServices: "{runningServices} services actifs \u2014 applications, bases de donn\xE9es, workers",
23820
+ power: "Jusqu'\xE0 {powerCpu} vCPU et {powerRamGb} GB de RAM par service",
23821
+ projects: "{maxProjects} projets",
23822
+ projectsUnlimited: "Projets illimit\xE9s",
23823
+ meteredCredits: "Le calcul, la bande passante et le stockage sont tous d\xE9duits de vos cr\xE9dits mensuels"
23824
+ },
23825
+ selfHosted: {
23826
+ name: "Auto-h\xE9berg\xE9",
23827
+ tagline: "Faites tourner toute la plateforme sur vos propres machines. N'importe quelle machine Linux, n'importe quel fournisseur, n'importe quelle r\xE9gion. Pas de comptage \xE0 l'usage, pas de limite d'utilisateurs, pas de t\xE9l\xE9m\xE9trie.",
23828
+ priceLabel: "Gratuit",
23829
+ priceNote: "Apache 2.0 \u2014 gratuit \xE0 vie, sans carte bancaire",
23830
+ cta: "Commencer l'auto-h\xE9bergement"
23831
+ },
23832
+ creditPacks: {
23833
+ pack_5k: "{credits} cr\xE9dits",
23834
+ pack_25k: "{credits} cr\xE9dits",
23835
+ pack_100k: "{credits} cr\xE9dits"
23836
+ },
23837
+ ui: {
23838
+ perMonth: "/mois",
23839
+ perYear: "/an",
23840
+ custom: "Sur mesure",
23841
+ free: "Gratuit",
23842
+ unlimited: "Illimit\xE9",
23843
+ mostPopular: "Le plus populaire",
23844
+ monthsFree: "{months} mois offerts",
23845
+ ctaStart: "Commencer gratuitement",
23846
+ ctaChoose: "Choisir {name}",
23847
+ ctaContact: "Contacter l'\xE9quipe commerciale",
23848
+ billedMonthly: "factur\xE9 mensuellement, r\xE9siliable \xE0 tout moment",
23849
+ campaignBadge: "{percentOff} % de remise",
23850
+ campaignEnds: "L'offre se termine le {date}",
23851
+ wasPrice: "avant {price}"
23852
+ }
23853
+ };
23854
+
23855
+ // ../../packages/core/src/pricing/locales/ja.json
23856
+ var ja_default2 = {
23857
+ plans: {
23858
+ free: {
23859
+ name: "Free",
23860
+ tagline: "\u9759\u7684\u30B5\u30A4\u30C8\u3092 Openship Cloud \u306B\u7121\u6599\u3067\u30C7\u30D7\u30ED\u30A4\u3067\u304D\u307E\u3059\u3002"
23861
+ },
23862
+ starter: {
23863
+ name: "Starter",
23864
+ tagline: "\u306F\u3058\u3081\u3066\u306E\u672C\u683C\u7684\u306A\u30A2\u30D7\u30EA\u306B\u3002\u30B3\u30F3\u30C6\u30CA\u3001\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u3001\u5FC5\u8981\u306A\u3082\u306E\u304C\u4E00\u5F0F\u305D\u308D\u3044\u307E\u3059\u3002"
23865
+ },
23866
+ pro: {
23867
+ name: "Pro",
23868
+ tagline: "\u6210\u9577\u306E\u4F59\u5730\u304C\u5FC5\u8981\u306A\u672C\u756A\u30A2\u30D7\u30EA\u5411\u3051\u3002"
23869
+ },
23870
+ team: {
23871
+ name: "Scale",
23872
+ tagline: "\u5171\u6709\u30A4\u30F3\u30D5\u30E9\u3067\u5927\u91CF\u306E\u30C8\u30E9\u30D5\u30A3\u30C3\u30AF\u3092\u3055\u3070\u304F\u30C1\u30FC\u30E0\u5411\u3051\u3002"
23873
+ },
23874
+ enterprise: {
23875
+ name: "Enterprise",
23876
+ tagline: "\u30AB\u30B9\u30BF\u30E0\u306E\u4E0A\u9650\u3001\u5C02\u7528\u30A4\u30F3\u30D5\u30E9\u3001SLA\u3002"
23877
+ }
23878
+ },
23879
+ features: {
23880
+ everythingIn: "{inherited} \u306E\u5168\u6A5F\u80FD\u306B\u52A0\u3048\u3066:",
23881
+ staticOnly: "\u9759\u7684\u30B5\u30A4\u30C8\u306E\u307F \u2014 SSG\u3001SPA\u3001\u7D20\u306E HTML",
23882
+ anyWorkload: "\u4F55\u3067\u3082\u52D5\u304B\u305B\u308B \u2014 \u30B3\u30F3\u30C6\u30CA\u3001Compose \u30B9\u30BF\u30C3\u30AF\u3001\u30EF\u30FC\u30AB\u30FC",
23883
+ managedServices: "\u30DE\u30CD\u30FC\u30B8\u30C9\u306E Postgres \u3068 Redis\u3001\u30A2\u30D7\u30EA\u30AB\u30BF\u30ED\u30B0\u3059\u3079\u3066",
23884
+ buildMinutes: "\u6BCE\u6708 {buildMinutes} \u5206\u306E\u30D3\u30EB\u30C9\u6642\u9593",
23885
+ credits: "\u6BCE\u6708 {credits} \u30B3\u30F3\u30D4\u30E5\u30FC\u30C8\u30AF\u30EC\u30B8\u30C3\u30C8",
23886
+ freeSubdomains: "\u7121\u6599\u306E {freeDomainSuffix} \u30B5\u30D6\u30C9\u30E1\u30A4\u30F3 {freeSubdomains} \u500B",
23887
+ freeSubdomainsUnlimited: "\u7121\u6599\u306E {freeDomainSuffix} \u30B5\u30D6\u30C9\u30E1\u30A4\u30F3\u304C\u7121\u5236\u9650",
23888
+ customDomains: "\u30AB\u30B9\u30BF\u30E0\u30C9\u30E1\u30A4\u30F3\u306F\u7121\u5236\u9650\u3001TLS \u8FBC\u307F",
23889
+ unlimitedSeats: "\u30C1\u30FC\u30E0\u30E1\u30F3\u30D0\u30FC\u306F\u7121\u5236\u9650 \u2014 \u4EBA\u6570\u8AB2\u91D1\u306F\u4E00\u5207\u306A\u3057",
23890
+ previewDeploys: "\u30D7\u30C3\u30B7\u30E5\u3054\u3068\u306B\u30D7\u30EC\u30D3\u30E5\u30FC\u30C7\u30D7\u30ED\u30A4",
23891
+ backups: "\u65E5\u6B21\u30D0\u30C3\u30AF\u30A2\u30C3\u30D7\u3068\u30DD\u30A4\u30F3\u30C8\u30A4\u30F3\u30BF\u30A4\u30E0\u30EA\u30AB\u30D0\u30EA",
23892
+ observability: "\u30E9\u30A4\u30D6\u30ED\u30B0\u3001\u30E1\u30C8\u30EA\u30AF\u30B9\u3001\u30D8\u30EB\u30B9\u30A2\u30E9\u30FC\u30C8",
23893
+ mailServer: "\u30E1\u30FC\u30EB\u30B5\u30FC\u30D0\u30FC\u5185\u8535 \u2014 SMTP\u3001IMAP\u3001\u30A6\u30A7\u30D6\u30E1\u30FC\u30EB",
23894
+ auditLog: "\u4FDD\u6301\u671F\u9593\u3092\u5EF6\u9577\u3057\u305F\u76E3\u67FB\u30ED\u30B0",
23895
+ sharedChannel: "Slack \u307E\u305F\u306F Discord \u306E\u5171\u6709\u30C1\u30E3\u30F3\u30CD\u30EB",
23896
+ sso: "SSO \u3068 SAML",
23897
+ customLimits: "\u5951\u7D04\u3054\u3068\u306B\u8ABF\u6574\u3059\u308B\u30AB\u30B9\u30BF\u30E0\u306E\u4E0A\u9650",
23898
+ dedicatedInfra: "\u5C02\u7528\u30A4\u30F3\u30D5\u30E9\u3068\u5C02\u7528\u30EA\u30FC\u30B8\u30E7\u30F3",
23899
+ onboarding: "\u5C0E\u5165\u3068\u79FB\u884C\u3092\u5C02\u4EFB\u30C1\u30FC\u30E0\u304C\u30D5\u30EB\u30B5\u30DD\u30FC\u30C8",
23900
+ communitySupport: "\u30B3\u30DF\u30E5\u30CB\u30C6\u30A3\u30B5\u30DD\u30FC\u30C8",
23901
+ emailSupport: "\u30E1\u30FC\u30EB\u30B5\u30DD\u30FC\u30C8",
23902
+ prioritySupport: "\u512A\u5148\u30B5\u30DD\u30FC\u30C8",
23903
+ dedicatedSupport: "SLA \u4ED8\u304D\u306E\u5C02\u4EFB\u30B5\u30DD\u30FC\u30C8",
23904
+ selfHostedOpenSource: "\u30D7\u30E9\u30C3\u30C8\u30D5\u30A9\u30FC\u30E0\u5168\u4F53\u304C\u30AA\u30FC\u30D7\u30F3\u30BD\u30FC\u30B9(Apache 2.0)",
23905
+ selfHostedUnlimited: "\u30C7\u30D7\u30ED\u30A4\u3001\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3001\u30B5\u30FC\u30D0\u30FC\u3001\u30C9\u30E1\u30A4\u30F3\u3059\u3079\u3066\u7121\u5236\u9650",
23906
+ selfHostedServices: "\u30DE\u30CD\u30FC\u30B8\u30C9\u30B5\u30FC\u30D3\u30B9\u306F\u3059\u3079\u3066\u5229\u7528\u53EF\u80FD \u2014 Postgres\u3001Redis\u3001\u30E1\u30FC\u30EB",
23907
+ selfHostedClients: "CLI\u3001Web\u3001\u30C7\u30B9\u30AF\u30C8\u30C3\u30D7 \u2014 \u30D0\u30C3\u30AF\u30A8\u30F3\u30C9\u306F\u4E00\u3064",
23908
+ selfHostedFreeSubdomains: "Openship Cloud \u306B\u63A5\u7D9A\u3059\u308B\u3068\u3001\u7121\u6599\u306E {freeDomainSuffix} \u30B5\u30D6\u30C9\u30E1\u30A4\u30F3\u304C {freeSubdomains} \u500B",
23909
+ runningServices: "\u540C\u6642\u306B\u7A3C\u50CD\u3059\u308B\u30B5\u30FC\u30D3\u30B9 {runningServices} \u500B \u2014 \u30A2\u30D7\u30EA\u3001\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u3001\u30EF\u30FC\u30AB\u30FC",
23910
+ power: "\u30B5\u30FC\u30D3\u30B9 1 \u3064\u3042\u305F\u308A\u6700\u5927 {powerCpu} vCPU \u3068 {powerRamGb} GB RAM",
23911
+ projects: "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8 {maxProjects} \u500B",
23912
+ projectsUnlimited: "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u306F\u7121\u5236\u9650",
23913
+ meteredCredits: "\u30B3\u30F3\u30D4\u30E5\u30FC\u30C8\u3001\u5E2F\u57DF\u5E45\u3001\u30B9\u30C8\u30EC\u30FC\u30B8\u306F\u3059\u3079\u3066\u6BCE\u6708\u306E\u30AF\u30EC\u30B8\u30C3\u30C8\u304B\u3089\u6D88\u8CBB\u3055\u308C\u307E\u3059"
23914
+ },
23915
+ selfHosted: {
23916
+ name: "\u30BB\u30EB\u30D5\u30DB\u30B9\u30C8",
23917
+ tagline: "\u30D7\u30E9\u30C3\u30C8\u30D5\u30A9\u30FC\u30E0\u5168\u4F53\u3092\u81EA\u5206\u306E\u30DE\u30B7\u30F3\u3067\u52D5\u304B\u305B\u307E\u3059\u3002Linux \u30DE\u30B7\u30F3\u306A\u3089\u3069\u308C\u3067\u3082\u3001\u3069\u306E\u30D7\u30ED\u30D0\u30A4\u30C0\u30FC\u3067\u3082\u3001\u3069\u306E\u30EA\u30FC\u30B8\u30E7\u30F3\u3067\u3082\u3002\u4F7F\u7528\u91CF\u306E\u8A08\u6E2C\u3082\u30E6\u30FC\u30B6\u30FC\u6570\u306E\u4E0A\u9650\u3082\u30C6\u30EC\u30E1\u30C8\u30EA\u3082\u3042\u308A\u307E\u305B\u3093\u3002",
23918
+ priceLabel: "\u7121\u6599",
23919
+ priceNote: "Apache 2.0 \u2014 \u6C38\u4E45\u7121\u6599\u3001\u30AF\u30EC\u30B8\u30C3\u30C8\u30AB\u30FC\u30C9\u4E0D\u8981",
23920
+ cta: "\u30BB\u30EB\u30D5\u30DB\u30B9\u30C8\u3092\u59CB\u3081\u308B"
23921
+ },
23922
+ creditPacks: {
23923
+ pack_5k: "{credits} \u30AF\u30EC\u30B8\u30C3\u30C8",
23924
+ pack_25k: "{credits} \u30AF\u30EC\u30B8\u30C3\u30C8",
23925
+ pack_100k: "{credits} \u30AF\u30EC\u30B8\u30C3\u30C8"
23926
+ },
23927
+ ui: {
23928
+ perMonth: "/\u6708",
23929
+ perYear: "/\u5E74",
23930
+ custom: "\u30AB\u30B9\u30BF\u30E0",
23931
+ free: "\u7121\u6599",
23932
+ unlimited: "\u7121\u5236\u9650",
23933
+ mostPopular: "\u4E00\u756A\u4EBA\u6C17",
23934
+ monthsFree: "{months} \u304B\u6708\u7121\u6599",
23935
+ ctaStart: "\u7121\u6599\u3067\u59CB\u3081\u308B",
23936
+ ctaChoose: "{name} \u3092\u9078\u629E",
23937
+ ctaContact: "\u55B6\u696D\u306B\u76F8\u8AC7",
23938
+ billedMonthly: "\u6708\u984D\u8ACB\u6C42\u3001\u3044\u3064\u3067\u3082\u30AD\u30E3\u30F3\u30BB\u30EB\u53EF\u80FD",
23939
+ campaignBadge: "{percentOff}% \u30AA\u30D5",
23940
+ campaignEnds: "\u30AD\u30E3\u30F3\u30DA\u30FC\u30F3\u306F {date} \u307E\u3067",
23941
+ wasPrice: "\u901A\u5E38 {price}"
23942
+ }
23943
+ };
23944
+
23945
+ // ../../packages/core/src/pricing/locales/pt.json
23946
+ var pt_default2 = {
23947
+ plans: {
23948
+ free: {
23949
+ name: "Free",
23950
+ tagline: "Publique sites est\xE1ticos na Openship Cloud sem custo."
23951
+ },
23952
+ starter: {
23953
+ name: "Starter",
23954
+ tagline: "Para o seu primeiro app de verdade: cont\xEAineres, bancos de dados e tudo o mais."
23955
+ },
23956
+ pro: {
23957
+ name: "Pro",
23958
+ tagline: "Para apps em produ\xE7\xE3o que precisam de espa\xE7o para crescer."
23959
+ },
23960
+ team: {
23961
+ name: "Scale",
23962
+ tagline: "Para equipes com tr\xE1fego pesado em infraestrutura compartilhada."
23963
+ },
23964
+ enterprise: {
23965
+ name: "Enterprise",
23966
+ tagline: "Limites sob medida, infraestrutura dedicada e um SLA."
23967
+ }
23968
+ },
23969
+ features: {
23970
+ everythingIn: "Tudo do {inherited}, e mais:",
23971
+ staticOnly: "Somente sites est\xE1ticos: SSG, SPA e HTML puro",
23972
+ anyWorkload: "Execute qualquer coisa: cont\xEAineres, stacks do Compose e workers",
23973
+ managedServices: "Postgres e Redis gerenciados, e o cat\xE1logo completo de apps",
23974
+ buildMinutes: "{buildMinutes} minutos de build por m\xEAs",
23975
+ credits: "{credits} cr\xE9ditos de computa\xE7\xE3o por m\xEAs",
23976
+ freeSubdomains: "{freeSubdomains} subdom\xEDnios {freeDomainSuffix} gratuitos",
23977
+ freeSubdomainsUnlimited: "Subdom\xEDnios {freeDomainSuffix} gratuitos e ilimitados",
23978
+ customDomains: "Dom\xEDnios pr\xF3prios ilimitados, com TLS inclu\xEDdo",
23979
+ unlimitedSeats: "Membros de equipe ilimitados: nunca pague por usu\xE1rio",
23980
+ previewDeploys: "Deploys de pr\xE9-visualiza\xE7\xE3o a cada push",
23981
+ backups: "Backups di\xE1rios com recupera\xE7\xE3o para um ponto no tempo",
23982
+ observability: "Logs em tempo real, m\xE9tricas e alertas de sa\xFAde",
23983
+ mailServer: "Servidor de e-mail integrado: SMTP, IMAP e webmail",
23984
+ auditLog: "Log de auditoria com reten\xE7\xE3o estendida",
23985
+ sharedChannel: "Canal compartilhado no Slack ou Discord",
23986
+ sso: "SSO e SAML",
23987
+ customLimits: "Limites sob medida, negociados por contrato",
23988
+ dedicatedInfra: "Infraestrutura e regi\xF5es dedicadas",
23989
+ onboarding: "Configura\xE7\xE3o inicial e migra\xE7\xE3o acompanhadas de ponta a ponta",
23990
+ communitySupport: "Suporte da comunidade",
23991
+ emailSupport: "Suporte por e-mail",
23992
+ prioritySupport: "Suporte priorit\xE1rio",
23993
+ dedicatedSupport: "Suporte dedicado com SLA",
23994
+ selfHostedOpenSource: "A plataforma inteira, de c\xF3digo aberto (Apache 2.0)",
23995
+ selfHostedUnlimited: "Deploys, projetos, servidores e dom\xEDnios ilimitados",
23996
+ selfHostedServices: "Todos os servi\xE7os gerenciados: Postgres, Redis, e-mail",
23997
+ selfHostedClients: "CLI, web e desktop: um \xFAnico backend",
23998
+ selfHostedFreeSubdomains: "{freeSubdomains} subdom\xEDnios {freeDomainSuffix} gratuitos ao conectar \xE0 Cloud",
23999
+ runningServices: "{runningServices} servi\xE7os em execu\xE7\xE3o: apps, bancos de dados, workers",
24000
+ power: "At\xE9 {powerCpu} vCPU e {powerRamGb} GB de RAM por servi\xE7o",
24001
+ projects: "{maxProjects} projetos",
24002
+ projectsUnlimited: "Projetos ilimitados",
24003
+ meteredCredits: "Computa\xE7\xE3o, largura de banda e armazenamento consomem os seus cr\xE9ditos mensais"
24004
+ },
24005
+ selfHosted: {
24006
+ name: "Auto-hospedado",
24007
+ tagline: "Rode a plataforma inteira em m\xE1quinas suas. Qualquer m\xE1quina Linux, qualquer provedor, qualquer regi\xE3o. Sem medi\xE7\xE3o de uso, sem limite de usu\xE1rios, sem telemetria.",
24008
+ priceLabel: "Gr\xE1tis",
24009
+ priceNote: "Apache 2.0: gr\xE1tis para sempre, sem cart\xE3o de cr\xE9dito",
24010
+ cta: "Come\xE7ar a auto-hospedar"
24011
+ },
24012
+ creditPacks: {
24013
+ pack_5k: "{credits} cr\xE9ditos",
24014
+ pack_25k: "{credits} cr\xE9ditos",
24015
+ pack_100k: "{credits} cr\xE9ditos"
24016
+ },
24017
+ ui: {
24018
+ perMonth: "/m\xEAs",
24019
+ perYear: "/ano",
24020
+ custom: "Sob medida",
24021
+ free: "Gr\xE1tis",
24022
+ unlimited: "Ilimitado",
24023
+ mostPopular: "Mais popular",
24024
+ monthsFree: "{months} meses gr\xE1tis",
24025
+ ctaStart: "Come\xE7ar gr\xE1tis",
24026
+ ctaChoose: "Escolher {name}",
24027
+ ctaContact: "Falar com vendas",
24028
+ billedMonthly: "cobran\xE7a mensal, cancele quando quiser",
24029
+ campaignBadge: "{percentOff}% de desconto",
24030
+ campaignEnds: "Oferta termina em {date}",
24031
+ wasPrice: "antes {price}"
24032
+ }
24033
+ };
24034
+
24035
+ // ../../packages/core/src/pricing/locales/tr.json
24036
+ var tr_default2 = {
24037
+ plans: {
24038
+ free: {
24039
+ name: "Free",
24040
+ tagline: "Statik sitelerinizi Openship Cloud'da \xFCcretsiz yay\u0131na al\u0131n."
24041
+ },
24042
+ starter: {
24043
+ name: "Starter",
24044
+ tagline: "\u0130lk ger\xE7ek uygulaman\u0131z i\xE7in \u2014 container'lar, veritabanlar\u0131, hepsi."
24045
+ },
24046
+ pro: {
24047
+ name: "Pro",
24048
+ tagline: "B\xFCy\xFCmek i\xE7in yer isteyen \xFCretim uygulamalar\u0131 i\xE7in."
24049
+ },
24050
+ team: {
24051
+ name: "Scale",
24052
+ tagline: "Payla\u015F\u0131ml\u0131 altyap\u0131da ciddi trafik y\xF6neten ekipler i\xE7in."
24053
+ },
24054
+ enterprise: {
24055
+ name: "Enterprise",
24056
+ tagline: "\xD6zel limitler, size ayr\u0131lm\u0131\u015F altyap\u0131 ve SLA."
24057
+ }
24058
+ },
24059
+ features: {
24060
+ everythingIn: "{inherited} plan\u0131ndaki her \u015Fey, ayr\u0131ca:",
24061
+ staticOnly: "Yaln\u0131zca statik siteler \u2014 SSG, SPA ve d\xFCz HTML",
24062
+ anyWorkload: "Her \u015Feyi \xE7al\u0131\u015Ft\u0131r\u0131n \u2014 container'lar, Compose y\u0131\u011F\u0131nlar\u0131 ve worker'lar",
24063
+ managedServices: "Y\xF6netilen Postgres, Redis ve uygulama katalo\u011Funun tamam\u0131",
24064
+ buildMinutes: "Ayda {buildMinutes} build dakikas\u0131",
24065
+ credits: "Ayda {credits} hesaplama kredisi",
24066
+ freeSubdomains: "{freeSubdomains} \xFCcretsiz {freeDomainSuffix} alt alan ad\u0131",
24067
+ freeSubdomainsUnlimited: "S\u0131n\u0131rs\u0131z \xFCcretsiz {freeDomainSuffix} alt alan ad\u0131",
24068
+ customDomains: "S\u0131n\u0131rs\u0131z \xF6zel alan ad\u0131, TLS dahil",
24069
+ unlimitedSeats: "S\u0131n\u0131rs\u0131z ekip \xFCyesi \u2014 kullan\u0131c\u0131 ba\u015F\u0131na \xF6deme yok",
24070
+ previewDeploys: "Her push'ta \xF6nizleme da\u011F\u0131t\u0131m\u0131",
24071
+ backups: "G\xFCnl\xFCk yedekler ve belirli bir ana geri d\xF6n\xFC\u015F",
24072
+ observability: "Canl\u0131 loglar, metrikler ve sa\u011Fl\u0131k uyar\u0131lar\u0131",
24073
+ mailServer: "Dahili posta sunucusu \u2014 SMTP, IMAP ve webmail",
24074
+ auditLog: "Denetim kayd\u0131, uzat\u0131lm\u0131\u015F saklama s\xFCresiyle",
24075
+ sharedChannel: "Payla\u015F\u0131ml\u0131 Slack veya Discord kanal\u0131",
24076
+ sso: "SSO ve SAML",
24077
+ customLimits: "S\xF6zle\u015Fmeye g\xF6re belirlenen \xF6zel limitler",
24078
+ dedicatedInfra: "Size ayr\u0131lm\u0131\u015F altyap\u0131 ve b\xF6lgeler",
24079
+ onboarding: "U\xE7tan uca kurulum ve ge\xE7i\u015F deste\u011Fi",
24080
+ communitySupport: "Topluluk deste\u011Fi",
24081
+ emailSupport: "E-posta deste\u011Fi",
24082
+ prioritySupport: "\xD6ncelikli destek",
24083
+ dedicatedSupport: "SLA garantili \xF6zel destek",
24084
+ selfHostedOpenSource: "Platformun tamam\u0131, a\xE7\u0131k kaynak (Apache 2.0)",
24085
+ selfHostedUnlimited: "S\u0131n\u0131rs\u0131z da\u011F\u0131t\u0131m, proje, sunucu ve alan ad\u0131",
24086
+ selfHostedServices: "T\xFCm y\xF6netilen servisler \u2014 Postgres, Redis, posta",
24087
+ selfHostedClients: "CLI, web ve masa\xFCst\xFC \u2014 tek backend",
24088
+ selfHostedFreeSubdomains: "Cloud'a ba\u011Fland\u0131\u011F\u0131n\u0131zda {freeSubdomains} \xFCcretsiz {freeDomainSuffix} alt alan ad\u0131",
24089
+ runningServices: "{runningServices} \xE7al\u0131\u015Fan servis \u2014 uygulamalar, veritabanlar\u0131, worker'lar",
24090
+ power: "Servis ba\u015F\u0131na en fazla {powerCpu} vCPU ve {powerRamGb} GB RAM",
24091
+ projects: "{maxProjects} proje",
24092
+ projectsUnlimited: "S\u0131n\u0131rs\u0131z proje",
24093
+ meteredCredits: "Hesaplama, bant geni\u015Fli\u011Fi ve depolaman\u0131n tamam\u0131 ayl\u0131k kredilerinizden d\xFC\u015Fer"
24094
+ },
24095
+ selfHosted: {
24096
+ name: "Kendi sunucunuzda",
24097
+ tagline: "Platformun tamam\u0131n\u0131 kendi makinelerinizde \xE7al\u0131\u015Ft\u0131r\u0131n. Her Linux makinesi, her sa\u011Flay\u0131c\u0131, her b\xF6lge. Kullan\u0131m \xF6l\xE7\xFCm\xFC yok, kullan\u0131c\u0131 s\u0131n\u0131r\u0131 yok, telemetri yok.",
24098
+ priceLabel: "\xDCcretsiz",
24099
+ priceNote: "Apache 2.0 \u2014 her zaman \xFCcretsiz, kredi kart\u0131 gerekmez",
24100
+ cta: "Kendi sunucunuzda ba\u015Flay\u0131n"
24101
+ },
24102
+ creditPacks: {
24103
+ pack_5k: "{credits} kredi",
24104
+ pack_25k: "{credits} kredi",
24105
+ pack_100k: "{credits} kredi"
24106
+ },
24107
+ ui: {
24108
+ perMonth: "/ay",
24109
+ perYear: "/y\u0131l",
24110
+ custom: "\xD6zel",
24111
+ free: "\xDCcretsiz",
24112
+ unlimited: "S\u0131n\u0131rs\u0131z",
24113
+ mostPopular: "En pop\xFCler",
24114
+ monthsFree: "{months} ay \xFCcretsiz",
24115
+ ctaStart: "\xDCcretsiz ba\u015Flay\u0131n",
24116
+ ctaChoose: "{name} plan\u0131n\u0131 se\xE7in",
24117
+ ctaContact: "Sat\u0131\u015F ekibiyle g\xF6r\xFC\u015F\xFCn",
24118
+ billedMonthly: "ayl\u0131k faturalan\u0131r, istedi\u011Finiz zaman iptal edin",
24119
+ campaignBadge: "%{percentOff} indirim",
24120
+ campaignEnds: "Kampanya {date} tarihinde sona eriyor",
24121
+ wasPrice: "eski fiyat {price}"
24122
+ }
24123
+ };
24124
+
24125
+ // ../../packages/core/src/pricing/locales/zh.json
24126
+ var zh_default = {
24127
+ plans: {
24128
+ free: {
24129
+ name: "Free",
24130
+ tagline: "\u5728 Openship Cloud \u4E0A\u514D\u8D39\u90E8\u7F72\u9759\u6001\u7AD9\u70B9\u3002"
24131
+ },
24132
+ starter: {
24133
+ name: "Starter",
24134
+ tagline: "\u9002\u5408\u4F60\u7684\u7B2C\u4E00\u4E2A\u6B63\u5F0F\u5E94\u7528:\u5BB9\u5668\u3001\u6570\u636E\u5E93,\u4E00\u5E94\u4FF1\u5168\u3002"
24135
+ },
24136
+ pro: {
24137
+ name: "Pro",
24138
+ tagline: "\u9002\u5408\u9700\u8981\u589E\u957F\u7A7A\u95F4\u7684\u751F\u4EA7\u5E94\u7528\u3002"
24139
+ },
24140
+ team: {
24141
+ name: "Scale",
24142
+ tagline: "\u9002\u5408\u5728\u5171\u4EAB\u57FA\u7840\u8BBE\u65BD\u4E0A\u627F\u8F7D\u5927\u6D41\u91CF\u7684\u56E2\u961F\u3002"
24143
+ },
24144
+ enterprise: {
24145
+ name: "Enterprise",
24146
+ tagline: "\u81EA\u5B9A\u4E49\u914D\u989D\u3001\u4E13\u5C5E\u57FA\u7840\u8BBE\u65BD,\u5E76\u63D0\u4F9B SLA\u3002"
24147
+ }
24148
+ },
24149
+ features: {
24150
+ everythingIn: "\u5305\u542B {inherited} \u7684\u5168\u90E8\u529F\u80FD,\u53E6\u6709:",
24151
+ staticOnly: "\u4EC5\u652F\u6301\u9759\u6001\u7AD9\u70B9:SSG\u3001SPA \u548C\u7EAF HTML",
24152
+ anyWorkload: "\u4EFB\u610F\u5DE5\u4F5C\u8D1F\u8F7D\u90FD\u80FD\u8FD0\u884C:\u5BB9\u5668\u3001Compose \u7F16\u6392\u548C\u540E\u53F0\u4EFB\u52A1",
24153
+ managedServices: "\u6258\u7BA1\u7684 Postgres\u3001Redis \u548C\u5B8C\u6574\u5E94\u7528\u76EE\u5F55",
24154
+ buildMinutes: "\u6BCF\u6708 {buildMinutes} \u5206\u949F\u6784\u5EFA\u65F6\u957F",
24155
+ credits: "\u6BCF\u6708 {credits} \u8BA1\u7B97\u79EF\u5206",
24156
+ freeSubdomains: "{freeSubdomains} \u4E2A\u514D\u8D39 {freeDomainSuffix} \u5B50\u57DF\u540D",
24157
+ freeSubdomainsUnlimited: "\u514D\u8D39 {freeDomainSuffix} \u5B50\u57DF\u540D\u4E0D\u9650\u6570\u91CF",
24158
+ customDomains: "\u81EA\u5B9A\u4E49\u57DF\u540D\u4E0D\u9650\u6570\u91CF,\u542B TLS",
24159
+ unlimitedSeats: "\u56E2\u961F\u6210\u5458\u4E0D\u9650\u4EBA\u6570,\u6C38\u4E0D\u6309\u5E2D\u4F4D\u8BA1\u8D39",
24160
+ previewDeploys: "\u6BCF\u6B21\u63A8\u9001\u90FD\u751F\u6210\u9884\u89C8\u90E8\u7F72",
24161
+ backups: "\u6BCF\u65E5\u5907\u4EFD,\u652F\u6301\u6309\u65F6\u95F4\u70B9\u6062\u590D",
24162
+ observability: "\u5B9E\u65F6\u65E5\u5FD7\u3001\u6307\u6807\u548C\u5065\u5EB7\u544A\u8B66",
24163
+ mailServer: "\u5185\u7F6E\u90AE\u4EF6\u670D\u52A1\u5668:SMTP\u3001IMAP \u548C\u7F51\u9875\u90AE\u7BB1",
24164
+ auditLog: "\u5BA1\u8BA1\u65E5\u5FD7,\u4FDD\u7559\u671F\u66F4\u957F",
24165
+ sharedChannel: "\u5171\u4EAB\u7684 Slack \u6216 Discord \u9891\u9053",
24166
+ sso: "SSO \u4E0E SAML",
24167
+ customLimits: "\u81EA\u5B9A\u4E49\u914D\u989D,\u6309\u5408\u540C\u5546\u5B9A",
24168
+ dedicatedInfra: "\u4E13\u5C5E\u57FA\u7840\u8BBE\u65BD\u4E0E\u4E13\u5C5E\u533A\u57DF",
24169
+ onboarding: "\u4E13\u4EBA\u5168\u7A0B\u534F\u52A9\u7684\u63A5\u5165\u4E0E\u8FC1\u79FB",
24170
+ communitySupport: "\u793E\u533A\u652F\u6301",
24171
+ emailSupport: "\u90AE\u4EF6\u652F\u6301",
24172
+ prioritySupport: "\u4F18\u5148\u652F\u6301",
24173
+ dedicatedSupport: "\u4E13\u5C5E\u652F\u6301,\u5E76\u63D0\u4F9B SLA",
24174
+ selfHostedOpenSource: "\u6574\u4E2A\u5E73\u53F0\u5F00\u6E90(Apache 2.0)",
24175
+ selfHostedUnlimited: "\u90E8\u7F72\u3001\u9879\u76EE\u3001\u670D\u52A1\u5668\u548C\u57DF\u540D\u5747\u4E0D\u9650\u6570\u91CF",
24176
+ selfHostedServices: "\u5168\u90E8\u6258\u7BA1\u670D\u52A1:Postgres\u3001Redis\u3001\u90AE\u4EF6\u670D\u52A1",
24177
+ selfHostedClients: "CLI\u3001\u7F51\u9875\u7AEF\u548C\u684C\u9762\u7AEF,\u5171\u7528\u4E00\u4E2A\u540E\u7AEF",
24178
+ selfHostedFreeSubdomains: "\u8FDE\u63A5 Cloud \u540E\u53EF\u83B7\u5F97 {freeSubdomains} \u4E2A\u514D\u8D39 {freeDomainSuffix} \u5B50\u57DF\u540D",
24179
+ runningServices: "{runningServices} \u4E2A\u540C\u65F6\u8FD0\u884C\u7684\u670D\u52A1:\u5E94\u7528\u3001\u6570\u636E\u5E93\u3001\u540E\u53F0\u4EFB\u52A1",
24180
+ power: "\u6BCF\u4E2A\u670D\u52A1\u6700\u9AD8\u53EF\u8FBE {powerCpu} vCPU \u548C {powerRamGb} GB RAM",
24181
+ projects: "{maxProjects} \u4E2A\u9879\u76EE",
24182
+ projectsUnlimited: "\u9879\u76EE\u4E0D\u9650\u6570\u91CF",
24183
+ meteredCredits: "\u8BA1\u7B97\u3001\u6D41\u91CF\u548C\u5B58\u50A8\u90FD\u4ECE\u4F60\u7684\u6BCF\u6708\u79EF\u5206\u4E2D\u6263\u9664"
24184
+ },
24185
+ selfHosted: {
24186
+ name: "\u81EA\u6258\u7BA1",
24187
+ tagline: "\u5728\u4F60\u81EA\u5DF1\u7684\u673A\u5668\u4E0A\u8FD0\u884C\u6574\u4E2A\u5E73\u53F0\u3002\u4EFB\u610F Linux \u4E3B\u673A\u3001\u4EFB\u610F\u4E91\u5382\u5546\u3001\u4EFB\u610F\u533A\u57DF\u3002\u4E0D\u8BA1\u91CF\u3001\u4E0D\u9650\u5E2D\u4F4D\u3001\u65E0\u9065\u6D4B\u3002",
24188
+ priceLabel: "\u514D\u8D39",
24189
+ priceNote: "Apache 2.0,\u6C38\u4E45\u514D\u8D39,\u65E0\u9700\u4FE1\u7528\u5361",
24190
+ cta: "\u5F00\u59CB\u81EA\u6258\u7BA1"
24191
+ },
24192
+ creditPacks: {
24193
+ pack_5k: "{credits} \u79EF\u5206",
24194
+ pack_25k: "{credits} \u79EF\u5206",
24195
+ pack_100k: "{credits} \u79EF\u5206"
24196
+ },
24197
+ ui: {
24198
+ perMonth: "/\u6708",
24199
+ perYear: "/\u5E74",
24200
+ custom: "\u5B9A\u5236",
24201
+ free: "\u514D\u8D39",
24202
+ unlimited: "\u65E0\u9650",
24203
+ mostPopular: "\u6700\u53D7\u6B22\u8FCE",
24204
+ monthsFree: "{months} \u4E2A\u6708\u514D\u8D39",
24205
+ ctaStart: "\u514D\u8D39\u5F00\u59CB\u4F7F\u7528",
24206
+ ctaChoose: "\u9009\u62E9 {name}",
24207
+ ctaContact: "\u8054\u7CFB\u9500\u552E",
24208
+ billedMonthly: "\u6309\u6708\u8BA1\u8D39,\u53EF\u968F\u65F6\u53D6\u6D88",
24209
+ campaignBadge: "\u7ACB\u51CF {percentOff}%",
24210
+ campaignEnds: "\u4F18\u60E0\u622A\u6B62 {date}",
24211
+ wasPrice: "\u539F\u4EF7 {price}"
24212
+ }
24213
+ };
24214
+
24215
+ // ../../packages/core/src/pricing/index.ts
24216
+ var PRICING = pricingCatalogSchema.parse(pricing_default);
24217
+ var PLAN_IDS = PRICING.plans.map((p) => p.id);
24218
+ var DEFAULT_PLAN_TIER = "free";
24219
+ var COPY = {
24220
+ en: en_default2,
24221
+ ar: ar_default2,
24222
+ de: de_default2,
24223
+ es: es_default2,
24224
+ fr: fr_default2,
24225
+ ja: ja_default2,
24226
+ pt: pt_default2,
24227
+ tr: tr_default2,
24228
+ zh: zh_default
24229
+ };
24230
+ var NUMBER_LOCALE = { ar: "ar-u-nu-latn" };
24231
+ function formatCount(value, locale) {
24232
+ return new Intl.NumberFormat(NUMBER_LOCALE[locale] ?? locale).format(value);
24233
+ }
24234
+ function formatDecimal(value, locale) {
24235
+ return new Intl.NumberFormat(NUMBER_LOCALE[locale] ?? locale, {
24236
+ maximumFractionDigits: Number.isInteger(value) ? 0 : 2
24237
+ }).format(value);
24238
+ }
24239
+ function fill(template, values) {
24240
+ return template.replace(
24241
+ /\{(\w+)\}/g,
24242
+ (match, key) => Object.prototype.hasOwnProperty.call(values, key) ? values[key] : match
24243
+ );
24244
+ }
24245
+ function copyFor(locale) {
24246
+ return COPY[locale] ?? COPY.en;
24247
+ }
24248
+ function uiString(locale, key) {
24249
+ return copyFor(locale).ui?.[key] ?? en_default2.ui[key];
24250
+ }
24251
+ function planName(planId, locale) {
24252
+ return copyFor(locale).plans?.[planId]?.name ?? en_default2.plans[planId]?.name ?? planId;
24253
+ }
24254
+ function planTagline(planId, locale) {
24255
+ return copyFor(locale).plans?.[planId]?.tagline ?? en_default2.plans[planId]?.tagline ?? "";
24256
+ }
24257
+ function featureTemplate(key, locale) {
24258
+ return copyFor(locale).features?.[key] ?? en_default2.features[key] ?? null;
24259
+ }
24260
+ var PLAN_BY_ID = new Map(PRICING.plans.map((p) => [p.id, p]));
24261
+ function toOblienLimits(limits) {
24262
+ const { runningServices, maxResourceTier } = limits;
24263
+ if (runningServices === null && maxResourceTier === null) return null;
24264
+ const build = PRICING.oblien.buildResources;
24265
+ const svc = maxResourceTier ? RESOURCE_TIER_SPECS[maxResourceTier] : null;
24266
+ return {
24267
+ max_workspaces: runningServices === null ? UNCAPPED_WORKSPACES : runningServices + PRICING.oblien.buildWorkspaceHeadroom,
24268
+ max_vcpus: Math.ceil(Math.max(svc?.cpuCores ?? 0, build.cpuCores)),
24269
+ max_ram_mb: Math.max(svc?.memoryMb ?? 0, build.memoryMb),
24270
+ max_disk_gb: Math.max(svc ? Math.ceil(svc.diskMb / 1024) : 0, build.diskGb)
24271
+ };
24272
+ }
24273
+ var UNCAPPED_WORKSPACES = 1e4;
24274
+ function placeholders(plan, locale) {
24275
+ const unlimited = uiString(locale, "unlimited");
24276
+ const n = (v) => v === null ? unlimited : formatCount(v, locale);
24277
+ const { limits } = plan;
24278
+ const svc = limits.maxResourceTier ? RESOURCE_TIER_SPECS[limits.maxResourceTier] : null;
24279
+ return {
24280
+ buildMinutes: n(limits.buildMinutesPerMonth),
24281
+ freeSubdomains: n(limits.freeSubdomains),
24282
+ customDomains: n(limits.customDomains),
24283
+ seats: n(limits.seats),
24284
+ runningServices: n(limits.runningServices),
24285
+ maxProjects: n(limits.maxProjects),
24286
+ // Per-SERVICE machine size, not a pool — the copy must say so.
24287
+ powerCpu: svc ? formatDecimal(svc.cpuCores, locale) : unlimited,
24288
+ powerRamGb: svc ? formatDecimal(svc.memoryMb / 1024, locale) : unlimited,
24289
+ powerDiskGb: svc ? formatCount(Math.round(svc.diskMb / 1024), locale) : unlimited,
24290
+ credits: plan.credits.monthlyMilli === null ? unlimited : formatCount(plan.credits.monthlyMilli / 1e3, locale),
24291
+ inherited: plan.inherits ? planName(plan.inherits, locale) : "",
24292
+ freeDomainSuffix: PRICING.freeDomainSuffix
24293
+ };
24294
+ }
24295
+ function resolvePlan(planId, locale = "en") {
24296
+ const plan = PLAN_BY_ID.get(planId) ?? PLAN_BY_ID.get(DEFAULT_PLAN_TIER);
24297
+ const values = placeholders(plan, locale);
24298
+ return {
24299
+ id: plan.id,
24300
+ name: planName(plan.id, locale),
24301
+ description: planTagline(plan.id, locale),
24302
+ price: plan.price,
24303
+ monthlyCredits: plan.credits.monthlyMilli,
24304
+ oblienLimits: toOblienLimits(plan.limits),
24305
+ limits: plan.limits,
24306
+ features: plan.features.map((key) => {
24307
+ const template = featureTemplate(key, locale);
24308
+ return template === null ? null : fill(template, values);
24309
+ }).filter((s) => s !== null),
24310
+ popular: plan.popular,
24311
+ support: plan.support,
24312
+ ...plan.contactSales ? { contactSales: plan.contactSales } : {}
24313
+ };
24314
+ }
24315
+ var PLANS = Object.fromEntries(
24316
+ PLAN_IDS.map((id) => [id, resolvePlan(id, "en")])
24317
+ );
24318
+ function resolveCreditPacks(locale = "en") {
24319
+ return [...PRICING.creditPacks].sort((a, b) => a.sortOrder - b.sortOrder).map((pack) => {
24320
+ const template = copyFor(locale).creditPacks?.[pack.id] ?? en_default2.creditPacks[pack.id] ?? "{credits}";
24321
+ return {
24322
+ id: pack.id,
24323
+ name: fill(template, { credits: formatCount(pack.creditsMilli / 1e3, locale) }),
24324
+ credits_milli: pack.creditsMilli,
24325
+ price_cents: pack.priceCents,
24326
+ sortOrder: pack.sortOrder
24327
+ };
24328
+ });
24329
+ }
24330
+ var CREDIT_PACKS = resolveCreditPacks("en");
24331
+ var ANNUAL_ENABLED = PRICING.annual.enabled;
24332
+ var ANNUAL_MONTHS_FREE = PRICING.annual.monthsFree;
24333
+ var FREE_DOMAIN_SUFFIX = PRICING.freeDomainSuffix;
24334
+
21609
24335
  // ../../packages/core/src/updates/types.ts
21610
24336
  var GITHUB_REPO = "oblien/openship";
21611
24337
  var RELEASES_LATEST_API = `https://api.github.com/repos/${GITHUB_REPO}/releases/latest`;
@@ -21647,6 +24373,11 @@ var AUDIT_CATEGORIES = [
21647
24373
  label: "Members & access",
21648
24374
  description: "Who is in this organization and what they are allowed to do."
21649
24375
  },
24376
+ {
24377
+ id: "agent",
24378
+ label: "AI agents",
24379
+ description: "What connected assistants did over MCP \u2014 every tool call, and the scope they hold."
24380
+ },
21650
24381
  {
21651
24382
  id: "security",
21652
24383
  label: "Security",
@@ -21881,6 +24612,38 @@ var AUDIT_EVENTS = {
21881
24612
  tone: "danger",
21882
24613
  description: "Automatic renewal failed \u2014 the certificate will expire unless this is fixed."
21883
24614
  },
24615
+ // Third-party credentials (the generic store: registry logins, DNS tokens, …).
24616
+ // Category `system`: these are instance-wide settings, not a domain operation — unlike
24617
+ // the dns_credential pair below, which stays under `domains` because that is where the
24618
+ // operator was standing when they connected it.
24619
+ "credential.created": {
24620
+ category: "system",
24621
+ action: "added a credential for",
24622
+ label: "Credential added",
24623
+ tone: "info",
24624
+ description: "A credential for a third-party service (a registry login, a DNS token) was stored. The secret itself is never recorded."
24625
+ },
24626
+ "credential.updated": {
24627
+ category: "system",
24628
+ action: "updated the credential for",
24629
+ label: "Credential updated",
24630
+ tone: "info",
24631
+ description: "A stored credential's label, scope or secret was changed. Neither the old nor the new secret is recorded."
24632
+ },
24633
+ "credential.deleted": {
24634
+ category: "system",
24635
+ action: "deleted the credential for",
24636
+ label: "Credential deleted",
24637
+ tone: "warning",
24638
+ description: "A stored credential was removed. Anything relying on it \u2014 a private image pull, a DNS record write \u2014 stops working until another is added."
24639
+ },
24640
+ "credential.verified": {
24641
+ category: "system",
24642
+ action: "verified the credential for",
24643
+ label: "Credential verified",
24644
+ tone: "info",
24645
+ description: "Openship asked the provider whether a stored credential still works."
24646
+ },
21884
24647
  "dns_credential.connected": {
21885
24648
  category: "domains",
21886
24649
  action: "connected the DNS provider",
@@ -21976,6 +24739,16 @@ var AUDIT_EVENTS = {
21976
24739
  label: "Mail server admin action",
21977
24740
  tone: "warning"
21978
24741
  },
24742
+ // Catalogued because the taxonomy scan is deliberately over-inclusive: it greps
24743
+ // apps/api/src for `eventType:` literals, so a notification-only emit that never
24744
+ // writes an audit_event row is caught the same as an audit write. Without this the
24745
+ // suite fails; with it, an operator who DOES surface these sees a real label.
24746
+ "mail.inbound_received": {
24747
+ category: "servers",
24748
+ action: "received mail matching a notification rule on",
24749
+ label: "Mail arrived at a watched address",
24750
+ tone: "info"
24751
+ },
21979
24752
  /* ---------------- Members & access ---------------- */
21980
24753
  "organization.created": {
21981
24754
  category: "members",
@@ -22098,19 +24871,32 @@ var AUDIT_EVENTS = {
22098
24871
  description: "An existing grant was overwritten with a different scope."
22099
24872
  },
22100
24873
  "mcp.authorized": {
22101
- category: "members",
24874
+ category: "agent",
22102
24875
  action: "authorized the MCP client",
22103
24876
  label: "MCP client authorized",
22104
24877
  tone: "info",
22105
24878
  description: "An AI agent was connected and given a scope to act within."
22106
24879
  },
22107
24880
  "mcp.scope_changed": {
22108
- category: "members",
24881
+ category: "agent",
22109
24882
  action: "changed the access of the MCP client",
22110
24883
  label: "MCP access changed",
22111
24884
  tone: "warning",
22112
24885
  description: "A connected agent's scope was edited. It takes effect on the agent's next request \u2014 no reconnect."
22113
24886
  },
24887
+ "mcp.disconnected": {
24888
+ category: "agent",
24889
+ action: "disconnected the MCP client",
24890
+ label: "MCP client disconnected",
24891
+ tone: "warning",
24892
+ description: "A connected agent's tokens, consent and scope were torn down. It stops working immediately and must re-consent to return."
24893
+ },
24894
+ "mcp.tool_called": {
24895
+ category: "agent",
24896
+ action: "ran the MCP tool",
24897
+ label: "Agent tool call",
24898
+ description: "A tool call that left no other trace: a read, or an attempt that was refused or errored. A tool call that successfully changed something is recorded as the change itself, so it is not duplicated here."
24899
+ },
22114
24900
  "grant.materialized": {
22115
24901
  category: "members",
22116
24902
  action: "received their pending permissions,",
@@ -22778,6 +25564,8 @@ export {
22778
25564
  shellQuote,
22779
25565
  shellSplitWords,
22780
25566
  commandToArgv,
25567
+ registryForImage,
25568
+ registryConfigKeys,
22781
25569
  buildEdgeImageRef,
22782
25570
  buildMailImageRef,
22783
25571
  SYSTEM,
@@ -22799,6 +25587,7 @@ export {
22799
25587
  parseOpenshipConfigJson,
22800
25588
  missingOutputDirectoryMessage,
22801
25589
  compareSemver,
25590
+ PRICING,
22802
25591
  resolveCliUpdatePlan,
22803
25592
  cliInstallCommand,
22804
25593
  PROXY_DIRECTIVES,
@@ -22823,6 +25612,11 @@ export {
22823
25612
  HOST_CHANNEL_RECHECK,
22824
25613
  HOST_CHANNEL_NOT_PROVISIONED,
22825
25614
  HOST_CHANNEL_UNPROVISIONED,
25615
+ HOST_CHANNEL_DEFAULT_ACCOUNT,
25616
+ hostChannelAccount,
25617
+ HOST_CHANNEL_AUTH_REJECTED,
25618
+ HOST_CHANNEL_AUTH_REJECTED_SHORT,
25619
+ HOST_CHANNEL_ROW_CREDENTIALS_UNUSED,
22826
25620
  HOST_CHANNEL_OPT_OUT,
22827
25621
  explainHostChannelCause,
22828
25622
  summarizeHostChannelCause,