@wtfalch/foundry 0.41.2 → 0.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/bootstrap/versions.d.ts +2 -0
  2. package/dist/index.d.ts +7 -3
  3. package/dist/index.js +4 -2
  4. package/dist/migrations/0054_org_signin_in_app.sql +6 -0
  5. package/dist/migrations/0055_release_requests.sql +20 -0
  6. package/dist/migrations/0056_platform_archon_blue_green.sql +7 -0
  7. package/dist/org.d.ts +30 -0
  8. package/dist/org.js +52 -1
  9. package/dist/plan/agora.d.ts +4 -1
  10. package/dist/plan/agora.js +16 -2
  11. package/dist/plan/archon-blue-green.d.ts +54 -0
  12. package/dist/plan/archon-blue-green.js +72 -0
  13. package/dist/plan/blue-green-apply.d.ts +82 -0
  14. package/dist/plan/blue-green-apply.js +360 -0
  15. package/dist/plan/blue-green.d.ts +223 -0
  16. package/dist/plan/blue-green.js +444 -0
  17. package/dist/plan/boule.d.ts +5 -2
  18. package/dist/plan/boule.js +14 -2
  19. package/dist/plan/cloudflared-connector.d.ts +26 -0
  20. package/dist/plan/cloudflared-connector.js +37 -0
  21. package/dist/plan/coolify-previews.d.ts +10 -0
  22. package/dist/plan/coolify-previews.js +14 -0
  23. package/dist/plan/diff.d.ts +6 -0
  24. package/dist/plan/diff.js +123 -11
  25. package/dist/plan/edge-ratelimits.d.ts +5 -0
  26. package/dist/plan/edge-ratelimits.js +16 -6
  27. package/dist/plan/executor.d.ts +79 -0
  28. package/dist/plan/executor.js +527 -31
  29. package/dist/plan/login-host.d.ts +23 -2
  30. package/dist/plan/login-host.js +30 -5
  31. package/dist/plan/own-diff.js +6 -0
  32. package/dist/plan/plan-apply.d.ts +51 -0
  33. package/dist/plan/plan-apply.js +222 -0
  34. package/dist/plan/platform-diff.d.ts +9 -0
  35. package/dist/plan/platform-diff.js +28 -1
  36. package/dist/plan/platform-org.d.ts +6 -0
  37. package/dist/plan/platform-org.js +5 -0
  38. package/dist/plan/platform-providers.d.ts +31 -2
  39. package/dist/plan/platform-providers.js +345 -13
  40. package/dist/plan/providers-readers.js +6 -2
  41. package/dist/plan/proxy-labels.d.ts +2 -2
  42. package/dist/plan/proxy-labels.js +12 -4
  43. package/dist/plan/release-host.d.ts +59 -0
  44. package/dist/plan/release-host.js +239 -0
  45. package/dist/plan/remote-database.d.ts +124 -0
  46. package/dist/plan/remote-database.js +373 -0
  47. package/dist/plan/sign-in-archon.d.ts +46 -0
  48. package/dist/plan/sign-in-archon.js +71 -0
  49. package/dist/plan/sign-in-bindings.d.ts +16 -0
  50. package/dist/plan/sign-in-bindings.js +44 -0
  51. package/dist/plan/sign-in.d.ts +135 -0
  52. package/dist/plan/sign-in.js +172 -0
  53. package/dist/platform.d.ts +11 -1
  54. package/dist/platform.js +4 -1
  55. package/dist/release/decide.d.ts +95 -0
  56. package/dist/release/decide.js +242 -0
  57. package/dist/release/health.d.ts +48 -0
  58. package/dist/release/health.js +61 -0
  59. package/dist/release/run.d.ts +40 -0
  60. package/dist/release/run.js +56 -0
  61. package/dist/release/state.d.ts +64 -0
  62. package/dist/release/state.js +126 -0
  63. package/dist/release/types.d.ts +54 -0
  64. package/dist/release/types.js +4 -0
  65. package/dist/store/postgres.d.ts +8 -0
  66. package/dist/store/postgres.js +62 -3
  67. package/dist/store/schema.d.ts +171 -0
  68. package/dist/store/schema.js +14 -0
  69. package/package.json +1 -1
  70. package/versions.digests.json +2 -1
  71. package/versions.json +2 -1
@@ -102,6 +102,8 @@ export interface OrgPlatformReadiness {
102
102
  * platform's, not as strangers. Absent when none is recorded.
103
103
  */
104
104
  platformZitadelIds?: readonly string[];
105
+ /** The second login app that serves `/sign-in` for companies with `signIn.inApp` is applied. Absent reads as false. */
106
+ signInLoginReady?: boolean;
105
107
  /** The platform's own sign-in (#168), which every org's own ZITADEL org lives inside. */
106
108
  authReady: boolean;
107
109
  /**
@@ -310,6 +312,10 @@ export interface OwnerSignIn {
310
312
  * is declared, absent while the owner step has recorded nothing.
311
313
  */
312
314
  firstOwnerUserId?: string | null;
315
+ /** The company's in-app login app (`<slug>-auth-login-sign-in`), once it is applied. Absent before. */
316
+ signInLoginAppId?: string;
317
+ /** True when the company has `signIn.inApp` and that login app is applied: Archon signs in on its own address. */
318
+ signInInApp?: true;
313
319
  }
314
320
  /** The owner company's own files host, as Archon's `FILES_*` env reads it. */
315
321
  export interface OwnerFiles {
@@ -7,6 +7,7 @@ import { oikosIngressApplied } from './oikos-ingress.js';
7
7
  import { ownHostnames } from './own-hostnames.js';
8
8
  import { OWN_SERVICES_APPLIED_SCOPE_KEY, orgTarget } from './own-services.js';
9
9
  import { JOBS_INTERNAL_URL_SCOPE_KEY, JOBS_SCHEDULER_KEY, PLATFORM_ACCESS_ZITADEL_APP_SCOPE_KEY, PLATFORM_ACCESS_ZITADEL_PROJECT_SCOPE_KEY, PLATFORM_AI_KEY, PLATFORM_AI_TUNNEL_SCOPE_KEY, PLATFORM_ARCHON_APPLIED_BROKER_SCOPE_KEY, PLATFORM_ARCHON_ZITADEL_APP_SCOPE_KEY, PLATFORM_ARCHON_ZITADEL_PROJECT_SCOPE_KEY, PLATFORM_AUTH_BROKER_SCOPE_KEY, PLATFORM_AUTH_KEY, PLATFORM_EDGE_KEY, PLATFORM_FILES_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_FILES_INTERNAL_URL_SCOPE_KEY, PLATFORM_FILES_KEY, PLATFORM_MAIL_INTERNAL_URL_SCOPE_KEY, PLATFORM_MAIL_KEY, PLATFORM_MAIL_ZITADEL_PROJECT_SCOPE_KEY, platformMailMxHost, } from './platform-diff.js';
10
+ import { SIGN_IN_LOGIN_APP_SCOPE_KEY, inAppSignIn } from './sign-in.js';
10
11
  /**
11
12
  * #443: the no-domain company mail apex -- `<slug>.oikos.host`, the same
12
13
  * fixed suffix `agora.ts`'s `agoraHostnameFor`, `boule.ts`'s
@@ -159,6 +160,7 @@ edgePortPresent = false) {
159
160
  ...(coolifyAppApplied(platformRecordedIds, PLATFORM_AUTH_KEY)
160
161
  ? { platformAuthRecordedHost: platform.auth.hostname }
161
162
  : {}),
163
+ ...(platformRecordedIds[SIGN_IN_LOGIN_APP_SCOPE_KEY] ? { signInLoginReady: true } : {}),
162
164
  authReady,
163
165
  authIssuer: authReady ? `https://${platform.auth.hostname}` : null,
164
166
  jobsReady: jobsApplied && jobsUrl !== undefined && missingKeys.jobs === null,
@@ -314,6 +316,7 @@ export function ownerSignInFrom(owner, recorded) {
314
316
  if (!applied || applied !== ownHostnames(owner).auth)
315
317
  return null;
316
318
  const hostname = applied;
319
+ const signInLoginAppId = recorded[SIGN_IN_LOGIN_APP_SCOPE_KEY];
317
320
  return {
318
321
  issuerUrl: `https://${applied}`,
319
322
  organisationId,
@@ -321,6 +324,8 @@ export function ownerSignInFrom(owner, recorded) {
321
324
  bearerRef: orgTarget(owner).refs.instanceBearer,
322
325
  hostname,
323
326
  firstOwnerUserId: platformOwnerUserIdFrom(owner, recorded),
327
+ ...(signInLoginAppId ? { signInLoginAppId } : {}),
328
+ ...(signInLoginAppId && inAppSignIn(owner) ? { signInInApp: true } : {}),
324
329
  };
325
330
  }
326
331
  /**
@@ -2,6 +2,7 @@ import type { CloudflareClient } from '@wtfalch/providers/cloudflare';
2
2
  import { type DeclaredOrg } from '../org.js';
3
3
  import type { DeclaredPlatform } from '../platform.js';
4
4
  import type { DeclaredOrgStore, PlatformStore } from '../store.js';
5
+ import { type BlueGreenServer, type BlueGreenSubject } from './blue-green.js';
5
6
  import { type DeployHealthRuntime } from './deploy-health.js';
6
7
  import type { StepAction } from './diff.js';
7
8
  import type { AccessPort } from './edge-access.js';
@@ -10,6 +11,7 @@ import type { RateLimitPort } from './edge-ratelimits.js';
10
11
  import type { TunnelPort } from './edge-tunnels.js';
11
12
  import type { ZonePort } from './edge-zones.js';
12
13
  import type { CloudflarePort, CoolifyPort, FoundryStepExecutorPorts, ZitadelPort } from './executor.js';
14
+ import { type ReleaseContext, type ReleaseHost } from './release-host.js';
13
15
  export { FILES_MANAGEMENT_KEY_REF, JOBS_ADMIN_TOKEN_REF, PLATFORM_AI_MANAGEMENT_KEY_REF, } from './held-key-refs.js';
14
16
  import type { LiveResource, PlatformCloudflareReader, PlatformCoolifyReader } from './live.js';
15
17
  import { type OikosIngressPorts } from './oikos-ingress.js';
@@ -349,6 +351,14 @@ export declare class FoundryPlatformStepExecutor implements PlatformStepExecutor
349
351
  private onPlatform;
350
352
  /** `deps` with its Coolify port placed on the platform destination, then limited by `profile`. */
351
353
  private limitedOnPlatform;
354
+ /** Every Coolify server and why platform-archon's blue/green cannot run here, for the planner. */
355
+ archonBlueGreen(): Promise<{
356
+ servers?: BlueGreenServer[];
357
+ blocked?: string;
358
+ }>;
359
+ /** The release runner's outside calls for platform-archon (`plan/release-host.ts`). The token is only read. */
360
+ releaseHost(platform: DeclaredPlatform, subject: BlueGreenSubject, context: ReleaseContext, renew: () => Promise<void>): ReleaseHost | null;
361
+ private archonBlueGreenStep;
352
362
  blockedSteps(platform: DeclaredPlatform, recordedIds?: Readonly<Record<string, string>>, owner?: PlatformOwnerInput): Promise<{
353
363
  auth?: string;
354
364
  authExecutorMissing?: boolean;
@@ -408,6 +418,14 @@ export declare class FoundryPlatformStepExecutor implements PlatformStepExecutor
408
418
  * (or the runner was never applied).
409
419
  */
410
420
  refreshFilesShare(_platform: DeclaredPlatform, recordedIds: Readonly<Record<string, string>>): Promise<Record<string, string> | null>;
421
+ /** True while a company signs in on its apps' own addresses through the platform's login. */
422
+ private signInLoginWanted;
423
+ /**
424
+ * In-app sign-in: the login app that serves `/sign-in` is wanted and either not recorded, gone from
425
+ * Coolify, or holding a host table other than the login's. The step then plans again and repairs it.
426
+ * A Coolify read that fails counts as not drifted, with one fixed log line (the error can carry values).
427
+ */
428
+ private signInLoginDrifted;
411
429
  execute(action: StepAction, context: PlatformStepExecutionContext): Promise<{
412
430
  resourceId?: string;
413
431
  } | undefined>;
@@ -510,6 +528,13 @@ export interface AuthStackInput {
510
528
  defaultRedirectUri: string | null;
511
529
  /** The company whose owner is invited into its own sign-in org; null for a company's own stack. */
512
530
  ownerCompany: DeclaredOrg | null;
531
+ /**
532
+ * In-app sign-in: a second login app serves `/sign-in` next to the login app. Absent: none is made.
533
+ * `bindings` is its `LOGIN_HOST_BINDINGS` document; `null` copies the login app's live one.
534
+ */
535
+ signInLogin?: {
536
+ bindings: string | null;
537
+ };
513
538
  }
514
539
  /**
515
540
  * #168: makes `coolify:app` keyed {@link PLATFORM_AUTH_KEY} true -- a Coolify
@@ -881,7 +906,9 @@ export declare function ownerInviteBlockedReason(ownerCompany: DeclaredOrg | nul
881
906
  * {@link PlatformStepExecutionContext}, bundled like {@link PlatformAuthDeps}.
882
907
  */
883
908
  export interface PlatformArchonDeps {
884
- coolify: Pick<CoolifyPort, 'findProjectByName' | 'listEnvironments' | 'createProject' | 'findOrCreateEnvironment' | 'listServers' | 'findApplicationByName' | 'getApplication' | 'requireGithubApp' | 'createGithubAppApplication' | 'createDockerfileApplication' | 'listDatabases' | 'createPostgresDatabase' | 'getDatabase' | 'raw' | 'bulkUpsertEnvs' | 'listEnvs' | 'deleteEnv' | 'updateApplication' | 'deploy' | 'deployAndWait' | 'getDeployment'>;
909
+ coolify: Pick<CoolifyPort, 'findProjectByName' | 'listEnvironments' | 'createProject' | 'findOrCreateEnvironment' | 'listServers' | 'findApplicationByName' | 'getApplication' | 'requireGithubApp' | 'createGithubAppApplication' | 'createDockerfileApplication' | 'listDatabases' | 'createPostgresDatabase' | 'getDatabase' | 'raw' | 'bulkUpsertEnvs' | 'listEnvs' | 'deleteEnv' | 'updateApplication' | 'deploy' | 'deployAndWait' | 'getDeployment' | 'listStorages' | 'createPersistentStorage' | 'createFileStorage' | 'updateFileStorage'> & {
910
+ listApplicationDeployments?: CoolifyPort['listApplicationDeployments'];
911
+ };
885
912
  /** F4.dns: every DNS record call, through edge at its recorded address (see {@link PlatformDnsFactory}). */
886
913
  dns: PlatformDnsFactory;
887
914
  /** F4.tunnels: every tunnel call, through edge at its recorded address (see {@link PlatformTunnelsFactory}). */
@@ -890,7 +917,7 @@ export interface PlatformArchonDeps {
890
917
  access: PlatformAccessFactory;
891
918
  cloudflare: Pick<CloudflarePort, 'findZoneByApex'>;
892
919
  /** Must target the platform's own ZITADEL instance, or `zitadelForInstance` must be set. */
893
- zitadel: Pick<ZitadelPort, 'findOrCreateOrg' | 'findOrCreateProject' | 'createOidcApp' | 'listApps' | 'deleteApp' | 'updateOidcAppIdTokenUserinfoAssertion'>;
920
+ zitadel: Pick<ZitadelPort, 'findOrCreateOrg' | 'findOrCreateProject' | 'createOidcApp' | 'listApps' | 'deleteApp' | 'updateOidcAppIdTokenUserinfoAssertion'> & Partial<Pick<ZitadelPort, 'ensureTrustedDomain' | 'deleteTrustedDomain' | 'updateOidcAppLoginBaseUri' | 'allowPerAppLoginBaseUri'>>;
894
921
  /**
895
922
  * {@link PlatformAuthDeps.zitadelForInstance}, for Archon's sign-in client:
896
923
  * the owner company's own ZITADEL, by its issuer and the held-key reference of
@@ -957,6 +984,8 @@ export interface PlatformArchonRuntime {
957
984
  readyTimeoutMs?: number;
958
985
  /** Pause between readiness polls (default 5 seconds). */
959
986
  readyIntervalMs?: number;
987
+ /** Called with every secret the blue/green steps read or mint (the health token, copied env), so the host can redact it. */
988
+ onSecret?: (value: string) => void;
960
989
  }
961
990
  /**
962
991
  * The name of foundry's own Cloudflare Access app in front of Archon. Same