@wtfalch/foundry 0.34.0 → 0.35.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/bootstrap/bootstrap.d.ts +1 -1
  2. package/dist/bootstrap/bootstrap.js +49 -11
  3. package/dist/bootstrap/declare.d.ts +12 -5
  4. package/dist/bootstrap/declare.js +33 -18
  5. package/dist/bootstrap/versions.d.ts +20 -0
  6. package/dist/bootstrap/versions.js +46 -0
  7. package/dist/calls-sfu.js +3 -3
  8. package/dist/index.d.ts +9 -4
  9. package/dist/index.js +5 -2
  10. package/dist/migrations/0053_heartbeats_provider.sql +9 -0
  11. package/dist/migrations/0053_org_version_pin.sql +6 -0
  12. package/dist/org.d.ts +60 -14
  13. package/dist/org.js +118 -28
  14. package/dist/plan/backup-gate.d.ts +93 -0
  15. package/dist/plan/backup-gate.js +261 -0
  16. package/dist/plan/broker-entries.d.ts +9 -1
  17. package/dist/plan/broker-entries.js +20 -3
  18. package/dist/plan/build-record.d.ts +56 -0
  19. package/dist/plan/build-record.js +109 -0
  20. package/dist/plan/calls-matrix.js +3 -3
  21. package/dist/plan/cloudflared-connector.js +2 -2
  22. package/dist/plan/company-backups.d.ts +31 -0
  23. package/dist/plan/company-backups.js +101 -0
  24. package/dist/plan/deploy-health.d.ts +12 -2
  25. package/dist/plan/deploy-health.js +3 -1
  26. package/dist/plan/destinations.d.ts +73 -0
  27. package/dist/plan/destinations.js +217 -0
  28. package/dist/plan/diff.d.ts +17 -8
  29. package/dist/plan/diff.js +131 -131
  30. package/dist/plan/edge-access.d.ts +15 -0
  31. package/dist/plan/edge-access.js +21 -2
  32. package/dist/plan/executor.d.ts +166 -7
  33. package/dist/plan/executor.js +547 -106
  34. package/dist/plan/health-targets.d.ts +1 -3
  35. package/dist/plan/health-targets.js +36 -36
  36. package/dist/plan/held-key-refs.d.ts +5 -0
  37. package/dist/plan/held-key-refs.js +5 -0
  38. package/dist/plan/hostnames.d.ts +10 -0
  39. package/dist/plan/hostnames.js +14 -0
  40. package/dist/plan/index.d.ts +5 -0
  41. package/dist/plan/index.js +3 -0
  42. package/dist/plan/live.d.ts +6 -0
  43. package/dist/plan/live.js +3 -2
  44. package/dist/plan/locked-hostnames.d.ts +2 -1
  45. package/dist/plan/locked-hostnames.js +16 -3
  46. package/dist/plan/login-host.d.ts +9 -20
  47. package/dist/plan/login-host.js +10 -25
  48. package/dist/plan/mail-relay.d.ts +48 -29
  49. package/dist/plan/mail-relay.js +145 -68
  50. package/dist/plan/network-move.d.ts +20 -0
  51. package/dist/plan/network-move.js +41 -0
  52. package/dist/plan/oikos-ingress.d.ts +111 -0
  53. package/dist/plan/oikos-ingress.js +231 -0
  54. package/dist/plan/org-names.d.ts +8 -2
  55. package/dist/plan/org-names.js +57 -1
  56. package/dist/plan/own-diff.d.ts +9 -8
  57. package/dist/plan/own-diff.js +42 -34
  58. package/dist/plan/own-hostnames.d.ts +27 -0
  59. package/dist/plan/own-hostnames.js +24 -0
  60. package/dist/plan/own-limits.d.ts +198 -7
  61. package/dist/plan/own-limits.js +163 -32
  62. package/dist/plan/own-services.js +3 -1
  63. package/dist/plan/own-sfu.js +3 -2
  64. package/dist/plan/plan-apply.d.ts +47 -7
  65. package/dist/plan/plan-apply.js +184 -38
  66. package/dist/plan/platform-diff.d.ts +133 -17
  67. package/dist/plan/platform-diff.js +231 -68
  68. package/dist/plan/platform-org.d.ts +79 -10
  69. package/dist/plan/platform-org.js +93 -13
  70. package/dist/plan/platform-providers.d.ts +118 -73
  71. package/dist/plan/platform-providers.js +770 -231
  72. package/dist/plan/proxy-labels.d.ts +57 -11
  73. package/dist/plan/proxy-labels.js +94 -14
  74. package/dist/plan/run-options.d.ts +19 -0
  75. package/dist/plan/run-options.js +46 -0
  76. package/dist/plan/zitadel-login-policy.d.ts +1 -1
  77. package/dist/plan/zitadel-login-policy.js +76 -8
  78. package/dist/platform.d.ts +32 -0
  79. package/dist/platform.js +16 -0
  80. package/dist/provision.d.ts +2 -0
  81. package/dist/provision.js +3 -1
  82. package/dist/slug.d.ts +3 -1
  83. package/dist/slug.js +25 -3
  84. package/dist/store/postgres.js +43 -1
  85. package/dist/store/schema.d.ts +36 -0
  86. package/dist/store/schema.js +4 -0
  87. package/package.json +3 -3
  88. package/versions.digests.json +11 -0
@@ -4,8 +4,9 @@ import { SFU_SLOT_MAX, ownsServices } from '../org.js';
4
4
  import { ZITADEL_ORG_SCOPE_KEY } from './agora.js';
5
5
  import { brokerEntryAppliedScopeKey } from './broker-entries.js';
6
6
  import { ZITADEL_FILES_PROJECT_SCOPE_KEY } from './core-files.js';
7
- import { ownAuthApplied } from './login-host.js';
8
- import { OWN_MAIL_SMTP_INTERNAL_SCOPE_KEY } from './mail-relay.js';
7
+ import { OWN_AUTH_APPLIED_HOSTNAME_SCOPE_KEY, ownAuthApplied } from './login-host.js';
8
+ import { OWN_MAIL_SMTP_INTERNAL_SCOPE_KEY, OWN_MAIL_SMTP_PORT_BASE, OWN_MAIL_SMTP_PORT_SCOPE_KEY, isOwnMailSmtpPort, } from './mail-relay.js';
9
+ import { ownHostnames } from './own-hostnames.js';
9
10
  import { OWN_FILES_R2_SCOPE_KEY, OWN_SERVICES_APPLIED_SCOPE_KEY, orgTarget, } from './own-services.js';
10
11
  import { OWN_SFU_APPLIED_HOSTNAME_SCOPE_KEY, OWN_SFU_APPLIED_SLOT_SCOPE_KEY, OWN_SFU_APPLIED_VERSION_SCOPE_KEY, OWN_SFU_DEPLOY_PENDING_SCOPE_KEY, OWN_SFU_KEYS_UNDELIVERED_SCOPE_KEY, SFU_INTERNAL_URL_SCOPE_KEY, SFU_PUBLIC_URL_SCOPE_KEY, } from './own-sfu.js';
11
12
  import { AUTH_ZITADEL_RUN_OPTIONS, JOBS_DEPLOY_PENDING_SCOPE_KEY, JOBS_INTERNAL_URL_SCOPE_KEY, PLATFORM_AUTH_ADMIN_UNDELIVERED_SCOPE_KEY, PLATFORM_AUTH_ADMIN_USER_SCOPE_KEY, PLATFORM_AUTH_BROKER_SCOPE_KEY, PLATFORM_AUTH_CONNECTOR_APP_SCOPE_KEY, PLATFORM_AUTH_INSTANCE_SCOPE_KEY, PLATFORM_AUTH_INSTANCE_UNDELIVERED_SCOPE_KEY, PLATFORM_AUTH_LOGIN_CLIENT_UNDELIVERED_SCOPE_KEY, PLATFORM_AUTH_LOGIN_CLIENT_USER_SCOPE_KEY, PLATFORM_AUTH_SMTP_SCOPE_KEY, PLATFORM_AUTH_ZITADEL_RUN_OPTIONS_SCOPE_KEY, PLATFORM_FILES_APPLIED_GIT_REF_SCOPE_KEY, PLATFORM_FILES_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_FILES_INTERNAL_URL_SCOPE_KEY, PLATFORM_FILES_INTROSPECTION_SCOPE_KEY, PLATFORM_FILES_INTROSPECTION_UNDELIVERED_SCOPE_KEY, PLATFORM_FILES_MANAGEMENT_KEY_SCOPE_KEY, PLATFORM_FILES_MANAGEMENT_UNDELIVERED_SCOPE_KEY, PLATFORM_MAIL_ADMIN_KEY_SCOPE_KEY, PLATFORM_MAIL_ADMIN_UNDELIVERED_SCOPE_KEY, PLATFORM_MAIL_APPLIED_GIT_REF_SCOPE_KEY, PLATFORM_MAIL_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_MAIL_INTERNAL_URL_SCOPE_KEY, platformMailMxHost, } from './platform-diff.js';
@@ -21,13 +22,10 @@ export const OWN_JOBS_BLOCKED_KEY = 'own-jobs';
21
22
  * blocked line is fixed text: it is part of the stale-plan comparison.
22
23
  */
23
24
  export function planOwnJobs(options) {
24
- const { org, recordedIds, readiness, isPlatformOrg, ownServices } = options;
25
+ const { org, recordedIds, readiness, ownServices } = options;
25
26
  if (!ownsServices(org))
26
27
  return { step: null, blocked: null };
27
28
  const blocked = (line) => ({ step: null, blocked: line });
28
- if (isPlatformOrg) {
29
- return blocked(`${org.slug} cannot run its own services: it is the platform's own org`);
30
- }
31
29
  const undelivered = recordedIds[JOBS_ADMIN_TOKEN_UNDELIVERED_SCOPE_KEY];
32
30
  if (undelivered && !options.tokenHeld) {
33
31
  return blocked(`${org.slug}'s own jobs scheduler admin token for app ${undelivered} was minted but not confirmed delivered; remove the recorded key ${JOBS_ADMIN_TOKEN_UNDELIVERED_SCOPE_KEY} to mint a new one`);
@@ -66,8 +64,7 @@ export function planOwnJobs(options) {
66
64
  const BROKER_ENTRY_APPLIED_PREFIX = brokerEntryAppliedScopeKey('');
67
65
  /** `unmanaged` key of the one line `planOwnAuth` reports. */
68
66
  export const OWN_AUTH_BLOCKED_KEY = 'own-auth';
69
- /** The hostname an own org's sign-in last applied at, so a changed domain plans the step again. */
70
- export const OWN_AUTH_APPLIED_HOSTNAME_SCOPE_KEY = 'own_auth_applied_hostname';
67
+ export { OWN_AUTH_APPLIED_HOSTNAME_SCOPE_KEY };
71
68
  /** The auth git ref an own org's sign-in last deployed, so a changed declared ref plans the step again. */
72
69
  export const OWN_AUTH_APPLIED_GIT_REF_SCOPE_KEY = 'own_auth_applied_git_ref';
73
70
  /**
@@ -80,14 +77,18 @@ export const OWN_AUTH_APPLIED_GIT_REF_SCOPE_KEY = 'own_auth_applied_git_ref';
80
77
  * text: it is part of the stale-plan comparison.
81
78
  */
82
79
  export function planOwnAuth(options) {
83
- const { org, recordedIds, readiness, isPlatformOrg, ownServices } = options;
84
- // `planOwnJobs` reports the platform's own org once.
85
- if (!ownsServices(org) || isPlatformOrg || !org.domain)
80
+ const { org, recordedIds, readiness, ownServices } = options;
81
+ if (!ownsServices(org))
86
82
  return { step: null, blocked: null };
87
83
  const blocked = (reason) => ({
88
84
  step: null,
89
85
  blocked: `${org.slug}'s own sign-in is blocked, not planned: ${reason}`,
90
86
  });
87
+ // The owner's sign-in takes the apex's `auth.` name. An old shared sign-in still recorded there
88
+ // would be repointed at a new empty one, and every operator locked out.
89
+ if (readiness.platformAuthRecordedHost && readiness.platformOwnerOrgId === org.id) {
90
+ return blocked(`the platform's shared sign-in is still recorded at ${readiness.platformAuthRecordedHost}; this build is for a platform built from empty`);
91
+ }
91
92
  if (recordedIds.zitadel_org && !recordedIds[OWN_SERVICES_APPLIED_SCOPE_KEY]) {
92
93
  return {
93
94
  step: null,
@@ -113,8 +114,11 @@ export function planOwnAuth(options) {
113
114
  const gitRef = readiness.serviceGitRefs?.auth;
114
115
  if (!gitRef)
115
116
  return blocked('the platform declares no auth git ref');
116
- if (!options.zone?.active)
117
- return blocked(`the zone for ${org.domain} is not active yet`);
117
+ const names = ownHostnames(org);
118
+ // A company without a domain has no zone of its own: it waits for the `oikos.host` zone at the platform's edge.
119
+ const zoneActive = org.domain ? Boolean(options.zone?.active) : Boolean(options.oikosZoneReady);
120
+ if (!zoneActive)
121
+ return blocked(`the zone for ${names.zone} is not active yet`);
118
122
  const ownEdge = readiness.ownEdgePorts;
119
123
  if (readiness.dnsBlocked && !(ownEdge?.dns && ownEdge.tunnels)) {
120
124
  return blocked(readiness.dnsBlocked);
@@ -125,10 +129,14 @@ export function planOwnAuth(options) {
125
129
  if (!options.mailKeyHeld && !options.mailKeyMintable) {
126
130
  return blocked(`the held key ${orgTarget(org).refs.brokerMailKey} is not held yet`);
127
131
  }
128
- const hostname = `auth.${org.domain}`;
132
+ const hostname = names.auth;
129
133
  // The broker starts only on a valid `AUTH_APPS`, and the login client is minted only once it does:
130
134
  // the first broker entry is what lets this step finish, as the platform's first registration does.
131
- const brokerHoldsAnEntry = Object.keys(recordedIds).some((key) => key.startsWith(BROKER_ENTRY_APPLIED_PREFIX));
135
+ // Archon's registration is an entry too; the platform records it, not this org, so it comes in
136
+ // through the readiness.
137
+ const brokerAppId = recordedIds[PLATFORM_AUTH_BROKER_SCOPE_KEY];
138
+ const brokerHoldsAnEntry = Object.keys(recordedIds).some((key) => key.startsWith(BROKER_ENTRY_APPLIED_PREFIX)) ||
139
+ (Boolean(brokerAppId) && readiness.archonBrokerAppId === brokerAppId);
132
140
  const applied = Boolean(recordedIds[`coolify:own-auth:${org.id}`]) &&
133
141
  Boolean(recordedIds[OWN_SERVICES_APPLIED_SCOPE_KEY]);
134
142
  if (applied &&
@@ -171,8 +179,8 @@ export const OWN_MAIL_BLOCKED_KEY = 'own-mail';
171
179
  * stale-plan comparison.
172
180
  */
173
181
  export function planOwnMail(options) {
174
- const { org, recordedIds, readiness, isPlatformOrg, ownServices, mailRelayApplied, zoneActive } = options;
175
- if (!ownsServices(org) || org.mail.mode === 'off' || isPlatformOrg) {
182
+ const { org, recordedIds, readiness, ownServices, mailRelayApplied, zoneActive } = options;
183
+ if (!ownsServices(org) || org.mail.mode === 'off') {
176
184
  return { step: null, blocked: null };
177
185
  }
178
186
  const blocked = (reason) => ({
@@ -191,8 +199,6 @@ export function planOwnMail(options) {
191
199
  const floating = floatingTagBlocked('Stalwart', gitRef, 'v0.16.24');
192
200
  if (floating)
193
201
  return blocked(`${floating} as the mail gitRef`);
194
- if (!org.domain)
195
- return blocked('the org declares no domain');
196
202
  if (!ownAuthApplied(org, recordedIds)) {
197
203
  return blocked('its own sign-in is not applied yet');
198
204
  }
@@ -200,10 +206,14 @@ export function planOwnMail(options) {
200
206
  return blocked('the shared mail relay is not applied yet');
201
207
  }
202
208
  if (!zoneActive)
203
- return blocked(`the zone for ${org.domain} is not active yet`);
209
+ return blocked(`the zone for ${ownHostnames(org).zone} is not active yet`);
204
210
  const platformApex = readiness.platformApex;
205
- const hostname = `mail.${org.domain}`;
211
+ const hostname = ownHostnames(org).mail;
212
+ const smtpPort = options.smtpPort ?? OWN_MAIL_SMTP_PORT_BASE;
213
+ if (!isOwnMailSmtpPort(smtpPort))
214
+ return blocked('its mail port is outside the range foundry uses');
206
215
  if (recordedIds[`coolify:own-mail:${org.id}`] &&
216
+ recordedIds[OWN_MAIL_SMTP_PORT_SCOPE_KEY] === String(smtpPort) &&
207
217
  recordedIds[PLATFORM_MAIL_APPLIED_HOSTNAME_SCOPE_KEY] === hostname &&
208
218
  recordedIds[PLATFORM_MAIL_APPLIED_GIT_REF_SCOPE_KEY] === gitRef &&
209
219
  recordedIds[PLATFORM_MAIL_INTERNAL_URL_SCOPE_KEY] &&
@@ -217,7 +227,7 @@ export function planOwnMail(options) {
217
227
  system: 'coolify',
218
228
  kind: 'own-mail',
219
229
  key: org.id,
220
- desired: { hostname, gitRef, mailHost: platformMailMxHost(platformApex) },
230
+ desired: { hostname, gitRef, mailHost: platformMailMxHost(platformApex), smtpPort },
221
231
  },
222
232
  step: {
223
233
  description: `provision ${org.slug}'s own mail server`,
@@ -240,10 +250,9 @@ export const OWN_SFU_FIREWALL_KEY = 'own-sfu:firewall';
240
250
  * Every line is fixed text: it is part of the stale-plan comparison.
241
251
  */
242
252
  export function planOwnSfu(options) {
243
- const { org, recordedIds, isPlatformOrg, ownServices, zoneActive, sfuPortsOpen, readiness } = options;
253
+ const { org, recordedIds, ownServices, zoneActive, sfuPortsOpen, readiness } = options;
244
254
  const none = { step: null, blocked: null, firewall: null };
245
- // The platform's own org is reported once, by `planOwnJobs`.
246
- if (!ownsServices(org) || !org.calls.enabled || isPlatformOrg || !org.domain)
255
+ if (!ownsServices(org) || !org.calls.enabled)
247
256
  return none;
248
257
  const blocked = (line) => ({ ...none, blocked: line });
249
258
  const slot = org.services?.sfuSlot;
@@ -270,7 +279,7 @@ export function planOwnSfu(options) {
270
279
  if (!ownAuthApplied(org, recordedIds))
271
280
  return waiting('its own sign-in is not applied yet');
272
281
  if (!zoneActive)
273
- return waiting(`the zone for ${org.domain} is not active yet`);
282
+ return waiting(`the zone for ${ownHostnames(org).zone} is not active yet`);
274
283
  const ownEdge = readiness.ownEdgePorts;
275
284
  if (readiness.dnsBlocked && !(ownEdge?.dns && ownEdge.tunnels)) {
276
285
  return waiting(readiness.dnsBlocked);
@@ -278,8 +287,8 @@ export function planOwnSfu(options) {
278
287
  if (!ownEdge?.tunnels && !readiness.edgeReady) {
279
288
  return waiting('the host supplies no tunnel port and edge is not ready');
280
289
  }
281
- const hostname = `sfu.${org.domain}`;
282
- const version = VERSIONS.livekit;
290
+ const hostname = ownHostnames(org).sfu;
291
+ const version = org.versionPin?.sfu ?? VERSIONS.livekit;
283
292
  const applied = Boolean(recordedIds[`coolify:own-sfu:${org.id}`]) &&
284
293
  recordedIds[OWN_SFU_APPLIED_VERSION_SCOPE_KEY] === version &&
285
294
  recordedIds[OWN_SFU_APPLIED_SLOT_SCOPE_KEY] === String(slot) &&
@@ -315,9 +324,8 @@ export const OWN_FILES_BLOCKED_KEY = 'own-files';
315
324
  * comparison.
316
325
  */
317
326
  export function planOwnFiles(options) {
318
- const { org, recordedIds, readiness, isPlatformOrg, ownServices, filesDeps } = options;
319
- // `planOwnJobs` reports the platform's own org once.
320
- if (!ownsServices(org) || isPlatformOrg || !org.domain)
327
+ const { org, recordedIds, readiness, ownServices, filesDeps } = options;
328
+ if (!ownsServices(org))
321
329
  return { step: null, blocked: null };
322
330
  const blocked = (reason) => ({
323
331
  step: null,
@@ -345,7 +353,7 @@ export function planOwnFiles(options) {
345
353
  }
346
354
  if (!filesDeps.bucket)
347
355
  return blocked('the host supplies no bucket port');
348
- const hostname = `files.${org.domain}`;
356
+ const hostname = ownHostnames(org).files;
349
357
  const applied = Boolean(recordedIds[`coolify:own-files:${org.id}`]);
350
358
  if (applied &&
351
359
  recordedIds[PLATFORM_FILES_APPLIED_HOSTNAME_SCOPE_KEY] === hostname &&
@@ -393,8 +401,8 @@ export const OWN_FILES_R2_BLOCKED_KEY = 'own-files-r2';
393
401
  * does not block the step: the next run deletes it first. The blocked line is fixed text.
394
402
  */
395
403
  export function planOwnFilesR2(options) {
396
- const { org, recordedIds, isPlatformOrg, ownServices, ports } = options;
397
- if (!ownsServices(org) || isPlatformOrg || !org.domain || !ownServices) {
404
+ const { org, recordedIds, ownServices, ports } = options;
405
+ if (!ownsServices(org) || !ownServices) {
398
406
  return { step: null, blocked: null };
399
407
  }
400
408
  if (!recordedIds[`coolify:own-files:${org.id}`])
@@ -0,0 +1,27 @@
1
+ import type { DeclaredOrg } from '../org.js';
2
+ /** The zone a company without a domain of its own runs its services on. */
3
+ export declare const NO_DOMAIN_ZONE = "oikos.host";
4
+ /** Every hostname an own company's services answer on. */
5
+ export interface OwnHostnames {
6
+ /** `org.domain`, or {@link NO_DOMAIN_ZONE} for a company without one. */
7
+ zone: string;
8
+ /** `auth.<domain>`, or `<slug>-auth.oikos.host`. */
9
+ auth: string;
10
+ /** `mail.<domain>`, or `<slug>-mail.oikos.host`. */
11
+ mail: string;
12
+ /** `files.<domain>`, or `<slug>-files.oikos.host`. */
13
+ files: string;
14
+ /** `sfu.<domain>`, or `<slug>-sfu.oikos.host`. */
15
+ sfu: string;
16
+ /** `https://<auth>`. */
17
+ issuerUrl: string;
18
+ /** The mail domain: `<domain>`, or `<slug>.oikos.host` (names are `name@<slug>.oikos.host`). */
19
+ mailDomain: string;
20
+ }
21
+ /**
22
+ * The one place that decides every own-service hostname. A company with a
23
+ * domain (and the matching `usesDomain` for the issuer and for mail) runs on
24
+ * it; a company without one runs on `oikos.host`. `auth` is
25
+ * {@link loginHostnameFor}, so the two cannot disagree.
26
+ */
27
+ export declare function ownHostnames(org: Pick<DeclaredOrg, 'slug' | 'domain' | 'issuer' | 'mail'>): OwnHostnames;
@@ -0,0 +1,24 @@
1
+ import { loginHostnameFor } from './login-host.js';
2
+ /** The zone a company without a domain of its own runs its services on. */
3
+ export const NO_DOMAIN_ZONE = 'oikos.host';
4
+ /**
5
+ * The one place that decides every own-service hostname. A company with a
6
+ * domain (and the matching `usesDomain` for the issuer and for mail) runs on
7
+ * it; a company without one runs on `oikos.host`. `auth` is
8
+ * {@link loginHostnameFor}, so the two cannot disagree.
9
+ */
10
+ export function ownHostnames(org) {
11
+ const { slug, domain } = org;
12
+ const auth = loginHostnameFor(org);
13
+ const name = (label) => domain ? `${label}.${domain}` : `${slug}-${label}.${NO_DOMAIN_ZONE}`;
14
+ const mailOnDomain = Boolean(domain) && org.mail.usesDomain;
15
+ return {
16
+ zone: domain ?? NO_DOMAIN_ZONE,
17
+ auth,
18
+ mail: mailOnDomain ? `mail.${domain}` : `${slug}-mail.${NO_DOMAIN_ZONE}`,
19
+ files: name('files'),
20
+ sfu: name('sfu'),
21
+ issuerUrl: `https://${auth}`,
22
+ mailDomain: mailOnDomain && domain ? domain : `${slug}.${NO_DOMAIN_ZONE}`,
23
+ };
24
+ }
@@ -1,22 +1,213 @@
1
1
  import type { CoolifyPort } from './executor.js';
2
+ import { type CapabilityTier } from './run-options.js';
2
3
  /**
3
- * The hard limits every container of a company that runs its own services
4
- * gets: one flat default. Field names are Coolify's (`PATCH /applications/{uuid}`
4
+ * The hard limits of every container foundry creates, per step (`LIMITS`). Field names are Coolify's (`PATCH /applications/{uuid}`
5
5
  * and `PATCH /databases/{uuid}`: `limits_memory`, `limits_cpus`). Coolify has
6
6
  * no field for a process limit, and its `custom_docker_run_options` parser drops
7
7
  * `--pids-limit`, so 512 processes cannot be set through Coolify.
8
8
  */
9
9
  export declare const OWN_LIMITS_MEMORY = "1g";
10
10
  export declare const OWN_LIMITS_CPUS = "1";
11
+ /**
12
+ * What a step's containers get. `memory` is for applications, `dbMemory` for
13
+ * databases. `autoDeploy: false` makes every git application the step creates
14
+ * or updates deploy only when foundry deploys it; `'declared'` leaves it to
15
+ * the company's own hosting entry (`HostingEntry.autoDeploy`).
16
+ */
17
+ export interface LimitProfile {
18
+ memory: string;
19
+ dbMemory: string;
20
+ autoDeploy: false | 'declared';
21
+ /** The capability options sent in `custom_docker_run_options` (`plan/run-options.ts`). */
22
+ capabilities: CapabilityTier;
23
+ }
24
+ /**
25
+ * The one table of memory limits, per step. Every container foundry creates
26
+ * is in a step named here. The five `own*` rows are the flat 1g the own
27
+ * services already ran with. A Node or Next app gets 1g, a small Postgres
28
+ * 512m, the Go-sized helpers (relay, boule, edge, hc) less.
29
+ */
30
+ export declare const LIMITS: {
31
+ readonly ownAuth: {
32
+ memory: string;
33
+ dbMemory: string;
34
+ autoDeploy: false | "declared";
35
+ capabilities: CapabilityTier;
36
+ };
37
+ readonly ownJobs: {
38
+ memory: string;
39
+ dbMemory: string;
40
+ autoDeploy: false | "declared";
41
+ capabilities: CapabilityTier;
42
+ };
43
+ readonly ownFiles: {
44
+ memory: string;
45
+ dbMemory: string;
46
+ autoDeploy: false | "declared";
47
+ capabilities: CapabilityTier;
48
+ };
49
+ readonly ownMail: {
50
+ memory: string;
51
+ dbMemory: string;
52
+ autoDeploy: false | "declared";
53
+ capabilities: CapabilityTier;
54
+ };
55
+ readonly ownSfu: {
56
+ memory: string;
57
+ dbMemory: string;
58
+ autoDeploy: false | "declared";
59
+ capabilities: CapabilityTier;
60
+ };
61
+ /** A customer's hosting entry: capability tier B, unless the entry declares `capabilities: 'standard'`. */
62
+ readonly companyApp: {
63
+ memory: string;
64
+ dbMemory: string;
65
+ autoDeploy: false | "declared";
66
+ capabilities: CapabilityTier;
67
+ };
68
+ readonly companyAppStandard: {
69
+ memory: string;
70
+ dbMemory: string;
71
+ autoDeploy: false | "declared";
72
+ capabilities: CapabilityTier;
73
+ };
74
+ readonly brokerEntry: {
75
+ memory: string;
76
+ dbMemory: string;
77
+ autoDeploy: false | "declared";
78
+ capabilities: CapabilityTier;
79
+ };
80
+ readonly cms: {
81
+ memory: string;
82
+ dbMemory: string;
83
+ autoDeploy: false | "declared";
84
+ capabilities: CapabilityTier;
85
+ };
86
+ readonly chat: {
87
+ memory: string;
88
+ dbMemory: string;
89
+ autoDeploy: false | "declared";
90
+ capabilities: CapabilityTier;
91
+ };
92
+ readonly calls: {
93
+ memory: string;
94
+ dbMemory: string;
95
+ autoDeploy: false | "declared";
96
+ capabilities: CapabilityTier;
97
+ };
98
+ readonly agora: {
99
+ memory: string;
100
+ dbMemory: string;
101
+ autoDeploy: false | "declared";
102
+ capabilities: CapabilityTier;
103
+ };
104
+ readonly boule: {
105
+ memory: string;
106
+ dbMemory: string;
107
+ autoDeploy: false | "declared";
108
+ capabilities: CapabilityTier;
109
+ };
110
+ readonly companyOther: {
111
+ memory: string;
112
+ dbMemory: string;
113
+ autoDeploy: false | "declared";
114
+ capabilities: CapabilityTier;
115
+ };
116
+ readonly platformArchon: {
117
+ memory: string;
118
+ dbMemory: string;
119
+ autoDeploy: false | "declared";
120
+ capabilities: CapabilityTier;
121
+ };
122
+ readonly platformAi: {
123
+ memory: string;
124
+ dbMemory: string;
125
+ autoDeploy: false | "declared";
126
+ capabilities: CapabilityTier;
127
+ };
128
+ readonly platformIntegrations: {
129
+ memory: string;
130
+ dbMemory: string;
131
+ autoDeploy: false | "declared";
132
+ capabilities: CapabilityTier;
133
+ };
134
+ readonly platformMailRelay: {
135
+ memory: string;
136
+ dbMemory: string;
137
+ autoDeploy: false | "declared";
138
+ capabilities: CapabilityTier;
139
+ };
140
+ readonly platformIngress: {
141
+ memory: string;
142
+ dbMemory: string;
143
+ autoDeploy: false | "declared";
144
+ capabilities: CapabilityTier;
145
+ };
146
+ readonly connector: {
147
+ memory: string;
148
+ dbMemory: string;
149
+ autoDeploy: false | "declared";
150
+ capabilities: CapabilityTier;
151
+ };
152
+ readonly platformFiles: {
153
+ memory: string;
154
+ dbMemory: string;
155
+ autoDeploy: false | "declared";
156
+ capabilities: CapabilityTier;
157
+ };
158
+ readonly platformEdge: {
159
+ memory: string;
160
+ dbMemory: string;
161
+ autoDeploy: false | "declared";
162
+ capabilities: CapabilityTier;
163
+ };
164
+ readonly platformHc: {
165
+ memory: string;
166
+ dbMemory: string;
167
+ autoDeploy: false | "declared";
168
+ capabilities: CapabilityTier;
169
+ };
170
+ readonly platformMail: {
171
+ memory: string;
172
+ dbMemory: string;
173
+ autoDeploy: false | "declared";
174
+ capabilities: CapabilityTier;
175
+ };
176
+ readonly platformAuth: {
177
+ memory: string;
178
+ dbMemory: string;
179
+ autoDeploy: false | "declared";
180
+ capabilities: CapabilityTier;
181
+ };
182
+ readonly platformJobs: {
183
+ memory: string;
184
+ dbMemory: string;
185
+ autoDeploy: false | "declared";
186
+ capabilities: CapabilityTier;
187
+ };
188
+ };
11
189
  /** The same bound the neighbouring Coolify calls in this package use for a call that may hang. */
12
190
  export declare const OWN_LIMITS_TIMEOUT_MS = 60000;
13
191
  type LimitsPort = Partial<Pick<CoolifyPort, 'createGithubAppApplication' | 'createDockerImageApplication' | 'createDockerfileApplication' | 'createPostgresDatabase' | 'findApplicationByName' | 'getApplication' | 'getDatabase' | 'updateApplication' | 'raw'>>;
14
192
  /**
15
- * The Coolify port an own-services step uses, with the limits set on every
193
+ * The Coolify port a step uses, with the profile's memory limit set on every
16
194
  * application and database it creates, and set again on one it finds without
17
- * them, so a company provisioned before the limits existed converges on its
18
- * next apply. A failed limits call fails the calling step before it records
19
- * its applied marker; the next run finds the resource and sets them.
195
+ * it, so a company provisioned before the limits existed converges on its
196
+ * next apply. With `autoDeploy: false` a git application is created with
197
+ * auto-deploy off, and one found or updated with it on is switched off. A
198
+ * failed limits call fails the calling step before it records its applied
199
+ * marker; the next run finds the resource and sets them.
200
+ *
201
+ * `scope` is a profile, or a function that gives the profile of the step now
202
+ * running (none: the port is used as it is).
203
+ *
204
+ * Every limits call has a 60 second timeout. A step that creates a resource
205
+ * and then fails on the limits call leaves a resource without limits; the
206
+ * next run finds it by name and sets them.
20
207
  */
21
- export declare function limited<C extends LimitsPort>(coolify: C): C;
208
+ export declare function limited<C extends LimitsPort>(coolify: C, scope?: LimitProfile | (() => LimitProfile | undefined)): C;
209
+ /** The deps of a step with their Coolify port limited by `profile`. */
210
+ export declare function limitedDeps<D extends {
211
+ coolify: LimitsPort;
212
+ }>(deps: D, profile: LimitProfile): D;
22
213
  export {};