@wtfalch/foundry 0.16.3 → 0.16.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -237,6 +237,8 @@ export interface CoolifyPort {
237
237
  name?: string;
238
238
  description?: string;
239
239
  instant_deploy?: boolean;
240
+ /** Extra `docker run` options (`--entrypoint "..."` becomes the compose entrypoint). */
241
+ custom_docker_run_options?: string;
240
242
  /**
241
243
  * `host:container`, comma separated -- publishes host ports directly
242
244
  * (`@wtfalch/providers`' `CoolifyClient.createDockerImageApplication`
@@ -46,6 +46,15 @@ export declare const PLATFORM_AUTH_KEY = "platform:auth";
46
46
  * that was never actually applied.
47
47
  */
48
48
  export declare const PLATFORM_AUTH_ZITADEL_APPLIED_IMAGE_TAG_SCOPE_KEY = "auth_zitadel_applied_image_tag";
49
+ /**
50
+ * The image has `Entrypoint /app/zitadel` and no `Cmd`, so without this it
51
+ * runs zitadel with no subcommand, exits, and restarts forever. Coolify turns
52
+ * `--entrypoint` in `custom_docker_run_options` into the compose `entrypoint`
53
+ * (its PATCH takes this field; it refuses `dockerfile`).
54
+ */
55
+ export declare const AUTH_ZITADEL_RUN_OPTIONS = "--entrypoint \"/app/zitadel start-from-init --masterkeyFromEnv --tlsMode external\"";
56
+ /** The run options last applied to the ZITADEL app (value: the options string), so a changed value forces one redeploy. */
57
+ export declare const PLATFORM_AUTH_ZITADEL_RUN_OPTIONS_SCOPE_KEY = "zitadel_platform_auth_run_options_applied";
49
58
  /**
50
59
  * #251: the Synapse/MAS image tags an org's calls Matrix stack last
51
60
  * successful apply actually set live. `coolifyCalls` (`executor.ts`)
@@ -41,6 +41,15 @@ export const PLATFORM_AUTH_KEY = 'platform:auth';
41
41
  * that was never actually applied.
42
42
  */
43
43
  export const PLATFORM_AUTH_ZITADEL_APPLIED_IMAGE_TAG_SCOPE_KEY = 'auth_zitadel_applied_image_tag';
44
+ /**
45
+ * The image has `Entrypoint /app/zitadel` and no `Cmd`, so without this it
46
+ * runs zitadel with no subcommand, exits, and restarts forever. Coolify turns
47
+ * `--entrypoint` in `custom_docker_run_options` into the compose `entrypoint`
48
+ * (its PATCH takes this field; it refuses `dockerfile`).
49
+ */
50
+ export const AUTH_ZITADEL_RUN_OPTIONS = '--entrypoint "/app/zitadel start-from-init --masterkeyFromEnv --tlsMode external"';
51
+ /** The run options last applied to the ZITADEL app (value: the options string), so a changed value forces one redeploy. */
52
+ export const PLATFORM_AUTH_ZITADEL_RUN_OPTIONS_SCOPE_KEY = 'zitadel_platform_auth_run_options_applied';
44
53
  /**
45
54
  * #251: the Synapse/MAS image tags an org's calls Matrix stack last
46
55
  * successful apply actually set live. `coolifyCalls` (`executor.ts`)
@@ -753,8 +762,15 @@ function planJobs(platform, byKey, claimed, observable) {
753
762
  claimed.add(key);
754
763
  if (!platform.jobs.enabled)
755
764
  return { step: null, blocked: null };
756
- if (live)
757
- return { step: null, blocked: null };
765
+ if (live) {
766
+ // The step also starts the scheduler's database (0.16.3), so a live app
767
+ // whose database the reader saw stopped plans it again. A status the
768
+ // reader could not read is absent, and plans nothing.
769
+ const databaseStatus = live.config.databaseStatus;
770
+ if (typeof databaseStatus !== 'string' || databaseStatus.startsWith('running')) {
771
+ return { step: null, blocked: null };
772
+ }
773
+ }
758
774
  // No Coolify reader means the host wired no Coolify at all for the
759
775
  // platform: the step's executor would throw every apply, and the scheduler
760
776
  // could never be seen live, so it would re-plan forever. A fixed text: it
@@ -1103,7 +1119,10 @@ function planAuth(platform, recordedIds, executorBlocked, executorMissing, authD
1103
1119
  connectorApplied &&
1104
1120
  brokerApplied &&
1105
1121
  brokerDeployed &&
1106
- loginClientApplied) {
1122
+ loginClientApplied &&
1123
+ // The ZITADEL run options reach the container only through the auth
1124
+ // step's redeploy, so a platform recorded before them still plans it.
1125
+ recordedIds[PLATFORM_AUTH_ZITADEL_RUN_OPTIONS_SCOPE_KEY] === AUTH_ZITADEL_RUN_OPTIONS) {
1107
1126
  return { step: null, blocked: null };
1108
1127
  }
1109
1128
  // The admin key was minted but never reached the host. ZITADEL shows a
@@ -770,7 +770,7 @@ export declare class ProvidersPlatformCloudflareReader implements PlatformCloudf
770
770
  export declare class ProvidersPlatformCoolifyReader implements PlatformCoolifyReader {
771
771
  private readonly client;
772
772
  private readonly platformStore;
773
- constructor(client: Pick<CoolifyPort, 'getApplication' | 'listDatabaseBackups'>, platformStore: Pick<PlatformStore, 'getServiceIds'>);
773
+ constructor(client: Pick<CoolifyPort, 'getApplication' | 'getDatabase' | 'listDatabaseBackups'>, platformStore: Pick<PlatformStore, 'getServiceIds'>);
774
774
  listResources(): Promise<LiveResource[]>;
775
775
  }
776
776
  export {};
@@ -6,7 +6,7 @@ import { redactSecrets } from '../calls-sfu-deploy-support.js';
6
6
  import { ensureCloudflaredConnector } from './cloudflared-connector.js';
7
7
  import { ACCESS_POLICY_NAME, getDatabaseStarted, isNotFoundError, isRunningOrStarting, } from './executor.js';
8
8
  import { coolifyDomains } from './hostnames.js';
9
- import { JOBS_SCHEDULER_KEY, PLATFORM_AI_APPLIED_GITREF_SCOPE_KEY, PLATFORM_AI_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_AI_DEPLOY_PENDING_SCOPE_KEY, PLATFORM_AI_INTERNAL_URL_SCOPE_KEY, PLATFORM_AI_KEY, PLATFORM_AI_MANAGEMENT_KEY_CONFLICT_SCOPE_KEY, PLATFORM_AI_MANAGEMENT_KEY_SCOPE_KEY, PLATFORM_AI_MANAGEMENT_KEY_UNDELIVERED_SCOPE_KEY, PLATFORM_AI_MIGRATE_BLOCKED_SCOPE_KEY, PLATFORM_AI_ORGANISATION_SCOPE_KEY, PLATFORM_AI_SIGNING_KEY_SET_SCOPE_KEY, PLATFORM_AI_SIGNING_KEY_UNDELIVERED_SCOPE_KEY, PLATFORM_ARCHON_APPLIED_BROKER_SCOPE_KEY, PLATFORM_ARCHON_APPLIED_GIT_REF_SCOPE_KEY, PLATFORM_ARCHON_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_ARCHON_CONNECTOR_APP_SCOPE_KEY, PLATFORM_ARCHON_GATE_CLOSED_SCOPE_KEY, PLATFORM_ARCHON_KEY, PLATFORM_ARCHON_MASTER_CLOUDFLARE_TOKEN_REF, PLATFORM_ARCHON_MASTER_COOLIFY_TOKEN_REF, PLATFORM_ARCHON_MASTER_COOLIFY_URL_REF, PLATFORM_AUTH_ADMIN_UNDELIVERED_SCOPE_KEY, PLATFORM_AUTH_ADMIN_USER_SCOPE_KEY, PLATFORM_AUTH_BROKER_DEPLOYED_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_KEY, PLATFORM_AUTH_LOGIN_CLIENT_UNDELIVERED_SCOPE_KEY, PLATFORM_AUTH_LOGIN_CLIENT_USER_SCOPE_KEY, PLATFORM_AUTH_ZITADEL_APPLIED_IMAGE_TAG_SCOPE_KEY, PLATFORM_BACKUP_DATABASE_SCOPE_KEYS, PLATFORM_BACKUP_SERVICE_KEYS, 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_KEY, PLATFORM_FILES_MANAGEMENT_KEY_SCOPE_KEY, PLATFORM_FILES_MANAGEMENT_UNDELIVERED_SCOPE_KEY, PLATFORM_FILES_ZITADEL_PROJECT_SCOPE_KEY, PLATFORM_INTEGRATIONS_APPLIED_GITREF_SCOPE_KEY, PLATFORM_INTEGRATIONS_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_INTEGRATIONS_KEY, PLATFORM_INTEGRATIONS_KEY_SET_SCOPE_KEY, PLATFORM_INTEGRATIONS_KEY_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_CONNECTOR_APP_SCOPE_KEY, PLATFORM_MAIL_INTERNAL_URL_SCOPE_KEY, PLATFORM_MAIL_KEY, PLATFORM_MAIL_ZITADEL_PROJECT_SCOPE_KEY, parseBrokerDeployRecord, parseLoginClientRecord, platformMailMxHost, } from './platform-diff.js';
9
+ import { AUTH_ZITADEL_RUN_OPTIONS, JOBS_SCHEDULER_KEY, PLATFORM_AI_APPLIED_GITREF_SCOPE_KEY, PLATFORM_AI_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_AI_DEPLOY_PENDING_SCOPE_KEY, PLATFORM_AI_INTERNAL_URL_SCOPE_KEY, PLATFORM_AI_KEY, PLATFORM_AI_MANAGEMENT_KEY_CONFLICT_SCOPE_KEY, PLATFORM_AI_MANAGEMENT_KEY_SCOPE_KEY, PLATFORM_AI_MANAGEMENT_KEY_UNDELIVERED_SCOPE_KEY, PLATFORM_AI_MIGRATE_BLOCKED_SCOPE_KEY, PLATFORM_AI_ORGANISATION_SCOPE_KEY, PLATFORM_AI_SIGNING_KEY_SET_SCOPE_KEY, PLATFORM_AI_SIGNING_KEY_UNDELIVERED_SCOPE_KEY, PLATFORM_ARCHON_APPLIED_BROKER_SCOPE_KEY, PLATFORM_ARCHON_APPLIED_GIT_REF_SCOPE_KEY, PLATFORM_ARCHON_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_ARCHON_CONNECTOR_APP_SCOPE_KEY, PLATFORM_ARCHON_GATE_CLOSED_SCOPE_KEY, PLATFORM_ARCHON_KEY, PLATFORM_ARCHON_MASTER_CLOUDFLARE_TOKEN_REF, PLATFORM_ARCHON_MASTER_COOLIFY_TOKEN_REF, PLATFORM_ARCHON_MASTER_COOLIFY_URL_REF, PLATFORM_AUTH_ADMIN_UNDELIVERED_SCOPE_KEY, PLATFORM_AUTH_ADMIN_USER_SCOPE_KEY, PLATFORM_AUTH_BROKER_DEPLOYED_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_KEY, PLATFORM_AUTH_LOGIN_CLIENT_UNDELIVERED_SCOPE_KEY, PLATFORM_AUTH_LOGIN_CLIENT_USER_SCOPE_KEY, PLATFORM_AUTH_ZITADEL_APPLIED_IMAGE_TAG_SCOPE_KEY, PLATFORM_AUTH_ZITADEL_RUN_OPTIONS_SCOPE_KEY, PLATFORM_BACKUP_DATABASE_SCOPE_KEYS, PLATFORM_BACKUP_SERVICE_KEYS, 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_KEY, PLATFORM_FILES_MANAGEMENT_KEY_SCOPE_KEY, PLATFORM_FILES_MANAGEMENT_UNDELIVERED_SCOPE_KEY, PLATFORM_FILES_ZITADEL_PROJECT_SCOPE_KEY, PLATFORM_INTEGRATIONS_APPLIED_GITREF_SCOPE_KEY, PLATFORM_INTEGRATIONS_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_INTEGRATIONS_KEY, PLATFORM_INTEGRATIONS_KEY_SET_SCOPE_KEY, PLATFORM_INTEGRATIONS_KEY_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_CONNECTOR_APP_SCOPE_KEY, PLATFORM_MAIL_INTERNAL_URL_SCOPE_KEY, PLATFORM_MAIL_KEY, PLATFORM_MAIL_ZITADEL_PROJECT_SCOPE_KEY, parseBrokerDeployRecord, parseLoginClientRecord, platformMailMxHost, } from './platform-diff.js';
10
10
  import { applyCallsSfuPlatformStep } from './platform-sfu.js';
11
11
  import { CreateInstanceError, createZitadelInstance, generateSystemApiKeypair, instanceWithoutBearerMessage, systemApiUsersEnv, } from './zitadel-system-api.js';
12
12
  /** The key `runPlatformSteps` records the apex zone's id under (`system:kind:key`). */
@@ -871,6 +871,7 @@ async function provisionPlatformAuth(context, deps) {
871
871
  docker_registry_image_name: AUTH_ZITADEL_IMAGE,
872
872
  docker_registry_image_tag: AUTH_ZITADEL_IMAGE_TAG,
873
873
  ports_exposes: AUTH_ZITADEL_PORT,
874
+ custom_docker_run_options: AUTH_ZITADEL_RUN_OPTIONS,
874
875
  name: PLATFORM_AUTH_ZITADEL_APP_NAME,
875
876
  description: `ZITADEL for the platform's own sign-in (#168, ${AUTH_ZITADEL_IMAGE}:${AUTH_ZITADEL_IMAGE_TAG})`,
876
877
  instant_deploy: false,
@@ -882,9 +883,14 @@ async function provisionPlatformAuth(context, deps) {
882
883
  // create must not leave the app unnamed for good, and the tunnel addresses
883
884
  // the app by this name. (The port cannot read the current name back, and
884
885
  // setting the same value again is harmless.)
886
+ // The run options go in the same patch, so an app made before they existed
887
+ // gets them too.
885
888
  await deps.coolify.updateApplication(zitadelAppId, {
886
889
  custom_internal_name: PLATFORM_AUTH_ZITADEL_INTERNAL_NAME,
890
+ custom_docker_run_options: AUTH_ZITADEL_RUN_OPTIONS,
887
891
  });
892
+ // The container only takes new run options at its next deploy.
893
+ const zitadelRunOptionsNew = recorded[PLATFORM_AUTH_ZITADEL_RUN_OPTIONS_SCOPE_KEY] !== AUTH_ZITADEL_RUN_OPTIONS;
888
894
  // #251: only on create, never unconditionally -- nothing here patches
889
895
  // `docker_registry_image_tag` on an app `findOrCreatePlatformApp` found
890
896
  // already running, so that is the only moment this tag is actually live.
@@ -1012,9 +1018,12 @@ async function provisionPlatformAuth(context, deps) {
1012
1018
  await deps.coolify.bulkUpsertEnvs(brokerAppId, brokerEnvs);
1013
1019
  // Deploy an app that was just created, is not running (a throw after the
1014
1020
  // create left it never deployed, or it stopped), or whose envs changed.
1015
- if (zitadelApp.justCreated || !zitadelApp.running || zitadelEnvsChanged) {
1021
+ if (zitadelApp.justCreated || !zitadelApp.running || zitadelEnvsChanged || zitadelRunOptionsNew) {
1016
1022
  await deps.coolify.deploy(zitadelAppId);
1017
1023
  await record(PLATFORM_AUTH_ZITADEL_DEPLOYED_SCOPE_KEY, zitadelAppId);
1024
+ // Only now: the options are live once the deploy went out. A throw between
1025
+ // the PATCH and here leaves the marker unset, so the next run deploys.
1026
+ await record(PLATFORM_AUTH_ZITADEL_RUN_OPTIONS_SCOPE_KEY, AUTH_ZITADEL_RUN_OPTIONS);
1018
1027
  }
1019
1028
  if (loginApp.justCreated || !loginApp.running || loginEnvsChanged) {
1020
1029
  await deps.coolify.deploy(loginAppId);
@@ -5029,11 +5038,27 @@ export class ProvidersPlatformCoolifyReader {
5029
5038
  if (recordedAppId) {
5030
5039
  try {
5031
5040
  const app = await this.client.getApplication(recordedAppId);
5041
+ // The scheduler's database status, so `planJobs` can see a stopped
5042
+ // one. Left out when it cannot be read: unknown is not "stopped".
5043
+ let databaseStatus;
5044
+ const databaseId = recorded[JOBS_DATABASE_SCOPE_KEY];
5045
+ if (databaseId) {
5046
+ try {
5047
+ databaseStatus = (await this.client.getDatabase(databaseId)).status;
5048
+ }
5049
+ catch {
5050
+ databaseStatus = undefined;
5051
+ }
5052
+ }
5032
5053
  resources.push({
5033
5054
  system: 'coolify',
5034
5055
  kind: 'jobs',
5035
5056
  key: JOBS_SCHEDULER_KEY,
5036
- config: { uuid: app.uuid, status: app.status },
5057
+ config: {
5058
+ uuid: app.uuid,
5059
+ status: app.status,
5060
+ ...(databaseStatus !== undefined && { databaseStatus }),
5061
+ },
5037
5062
  });
5038
5063
  }
5039
5064
  catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wtfalch/foundry",
3
- "version": "0.16.3",
3
+ "version": "0.16.4",
4
4
  "description": "Foundry's SDK: the declared org's types, the plan and apply contracts, and the state store's interface.",
5
5
  "repository": {
6
6
  "type": "git",