@wtfalch/foundry 0.45.1 → 0.47.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 (78) hide show
  1. package/dist/build-json.d.ts +28 -0
  2. package/dist/build-json.js +95 -0
  3. package/dist/index.d.ts +10 -4
  4. package/dist/index.js +6 -2
  5. package/dist/migrations/0058_app_requests.sql +32 -0
  6. package/dist/org.d.ts +68 -5
  7. package/dist/org.js +287 -135
  8. package/dist/plan/agora.d.ts +14 -0
  9. package/dist/plan/agora.js +21 -0
  10. package/dist/plan/backup-gate.d.ts +15 -0
  11. package/dist/plan/backup-gate.js +38 -0
  12. package/dist/plan/boule-owners-http.d.ts +12 -0
  13. package/dist/plan/boule-owners-http.js +59 -0
  14. package/dist/plan/boule.d.ts +38 -2
  15. package/dist/plan/boule.js +72 -1
  16. package/dist/plan/company-backups.d.ts +7 -1
  17. package/dist/plan/company-backups.js +42 -2
  18. package/dist/plan/cookie-domain.d.ts +41 -0
  19. package/dist/plan/cookie-domain.js +71 -0
  20. package/dist/plan/desktop-auth.d.ts +58 -0
  21. package/dist/plan/desktop-auth.js +144 -0
  22. package/dist/plan/desktop-callback.d.ts +26 -0
  23. package/dist/plan/desktop-callback.js +59 -0
  24. package/dist/plan/diff.d.ts +13 -4
  25. package/dist/plan/diff.js +277 -88
  26. package/dist/plan/executor.d.ts +136 -6
  27. package/dist/plan/executor.js +732 -105
  28. package/dist/plan/health-gaps.js +5 -0
  29. package/dist/plan/health-targets.js +1 -2
  30. package/dist/plan/hostnames.d.ts +2 -2
  31. package/dist/plan/hostnames.js +6 -6
  32. package/dist/plan/index.d.ts +4 -1
  33. package/dist/plan/index.js +3 -1
  34. package/dist/plan/job-app.d.ts +23 -0
  35. package/dist/plan/job-app.js +41 -0
  36. package/dist/plan/live.d.ts +6 -0
  37. package/dist/plan/live.js +16 -0
  38. package/dist/plan/login-host.d.ts +28 -0
  39. package/dist/plan/login-host.js +36 -0
  40. package/dist/plan/org-names.js +9 -1
  41. package/dist/plan/org-postgres-job.d.ts +22 -0
  42. package/dist/plan/org-postgres-job.js +91 -0
  43. package/dist/plan/org-postgres-migrate.d.ts +115 -0
  44. package/dist/plan/org-postgres-migrate.js +466 -0
  45. package/dist/plan/org-postgres.d.ts +90 -0
  46. package/dist/plan/org-postgres.js +293 -0
  47. package/dist/plan/plan-apply.d.ts +60 -3
  48. package/dist/plan/plan-apply.js +174 -8
  49. package/dist/plan/platform-diff.d.ts +45 -0
  50. package/dist/plan/platform-diff.js +159 -4
  51. package/dist/plan/platform-org.d.ts +2 -0
  52. package/dist/plan/platform-org.js +4 -0
  53. package/dist/plan/platform-providers.d.ts +12 -2
  54. package/dist/plan/platform-providers.js +282 -9
  55. package/dist/plan/post-logout-uris.d.ts +22 -0
  56. package/dist/plan/post-logout-uris.js +43 -0
  57. package/dist/plan/providers-readers.d.ts +10 -1
  58. package/dist/plan/providers-readers.js +56 -5
  59. package/dist/plan/sign-in-bindings.d.ts +4 -0
  60. package/dist/plan/sign-in-bindings.js +10 -0
  61. package/dist/plan/sign-in-org.d.ts +19 -6
  62. package/dist/plan/sign-in-org.js +35 -10
  63. package/dist/plan/sign-in.d.ts +12 -5
  64. package/dist/plan/sign-in.js +12 -5
  65. package/dist/plan/signin-db-move.js +10 -34
  66. package/dist/plan/worker-app.js +3 -1
  67. package/dist/refuse-org.d.ts +7 -0
  68. package/dist/refuse-org.js +10 -0
  69. package/dist/requested-changes.d.ts +50 -4
  70. package/dist/requested-changes.js +9 -1
  71. package/dist/store/postgres.d.ts +5 -1
  72. package/dist/store/postgres.js +67 -8
  73. package/dist/store/schema.d.ts +72 -0
  74. package/dist/store/schema.js +4 -0
  75. package/dist/store.d.ts +24 -1
  76. package/dist/worker-app-config.d.ts +8 -2
  77. package/dist/worker-app-config.js +20 -6
  78. package/package.json +5 -5
@@ -3,6 +3,7 @@ import { type CallsSfuUrls } from '../calls-sfu.js';
3
3
  import type { DeclaredOrg, HostingBuildPack, OrgId } from '../org.js';
4
4
  import type { DeclaredOrgStore } from '../store.js';
5
5
  import { type BlueGreenServer, type BlueGreenSubject } from './blue-green.js';
6
+ import { type BouleOwnerSeed } from './boule.js';
6
7
  import { type BrokerPort } from './broker-entries.js';
7
8
  import { type DeployHealthRuntime } from './deploy-health.js';
8
9
  import { type StepAction } from './diff.js';
@@ -865,6 +866,10 @@ export interface ZitadelPort {
865
866
  * method is optional until it does; the platform's Access provider step
866
867
  * is blocked without it. Like `createApiApp`, the secret is shown once.
867
868
  */
869
+ /** providers 0.12.0: the app's redirect URIs. Absent on an older client. */
870
+ getOidcAppRedirectUris?(orgId: string, projectId: string, appId: string): Promise<string[]>;
871
+ /** providers 0.12.0: replaces the app's redirect URIs; true when it wrote. Absent on an older client. */
872
+ updateOidcAppRedirectUris?(orgId: string, projectId: string, appId: string, redirectUris: string[]): Promise<boolean>;
868
873
  createOidcAppWithSecret?(orgId: string, projectId: string, input: {
869
874
  name: string;
870
875
  redirectUris: string[];
@@ -1200,6 +1205,13 @@ export interface ZitadelPort {
1200
1205
  * `createOidcApp` and `createOidcAppWithSecret` send it on themselves.
1201
1206
  */
1202
1207
  updateOidcAppIdTokenUserinfoAssertion?(orgId: string, projectId: string, appId: string): Promise<boolean>;
1208
+ /**
1209
+ * Replaces an existing app's post-logout redirect URIs (`@wtfalch/providers`'
1210
+ * `ZitadelClient.updateOidcAppPostLogoutRedirectUris`, which refuses an empty
1211
+ * list). Optional: a host whose client lacks it gets no update planned
1212
+ * (`FoundryPlanApplyOptions.zitadelPostLogoutUris`), and nothing throws.
1213
+ */
1214
+ updateOidcAppPostLogoutRedirectUris?(orgId: string, projectId: string, appId: string, postLogoutRedirectUris: string[]): Promise<void>;
1203
1215
  /**
1204
1216
  * Auth ADR 0002: an existing sign-in app issues JWT access tokens and holds
1205
1217
  * every `redirectUris` entry (`@wtfalch/providers` 0.11.0). Resolves `true`
@@ -1215,6 +1227,25 @@ export interface ZitadelPort {
1215
1227
  ensureAccessTokenLifetime?(seconds: number): Promise<{
1216
1228
  changed: boolean;
1217
1229
  }>;
1230
+ /** Reads one app by id. Resolves `null` only when ZITADEL answers not-found (providers 0.12.0). */
1231
+ getApp?(orgId: string, projectId: string, appId: string): Promise<{
1232
+ id: string;
1233
+ name: string;
1234
+ appType: 'web' | 'native' | 'api';
1235
+ state: string;
1236
+ clientId?: string;
1237
+ } | null>;
1238
+ /** The app's registered redirect URIs (providers 0.12.0). */
1239
+ getOidcAppRedirectUris?(orgId: string, projectId: string, appId: string): Promise<string[]>;
1240
+ /** Replaces the app's redirect URIs. Resolves `true` when it wrote. Refuses an empty list (providers 0.12.0). */
1241
+ updateOidcAppRedirectUris?(orgId: string, projectId: string, appId: string, redirectUris: string[]): Promise<boolean>;
1242
+ /** Turns the project's role check and has-project check on or off. Idempotent (providers 0.12.0). */
1243
+ setProjectRoleChecks?(orgId: string, projectId: string, want: {
1244
+ projectRoleCheck: boolean;
1245
+ hasProjectCheck: boolean;
1246
+ }): Promise<{
1247
+ changed: boolean;
1248
+ }>;
1218
1249
  /**
1219
1250
  * Sets one metadata key on an org (`wtfalch.signIn = email-code` on the
1220
1251
  * customers organisation). An upsert, so a repeat is no change. Optional, with
@@ -1232,8 +1263,8 @@ export interface ZitadelPort {
1232
1263
  * listApps` -- a plain array, with no `loginBaseUri` or completeness signal
1233
1264
  * -- into {@link ZitadelPort.listApps}'s own shape, so a host wiring that
1234
1265
  * real client in satisfies this port without reaching into `executor.ts`
1235
- * itself to build the wrapper by hand. `complete: true` on every call:
1236
- * since providers 0.7.4 the real client pages `_search` to the end. A host
1266
+ * itself to build the wrapper by hand. `complete` is true only when the client
1267
+ * has `getApp` (#557; providers 0.12.0, which pages `_search` to the end). A host
1237
1268
  * that supplies its own `listApps` keeps whatever `complete` it reports.
1238
1269
  * Each app's `clientId` (providers 0.7.5) passes through. `loginBaseUri` comes back `undefined`
1239
1270
  * for every app for the identical reason -- the real client does not read
@@ -1244,6 +1275,7 @@ export interface ZitadelPort {
1244
1275
  */
1245
1276
  export declare function adaptZitadelListApps(client: {
1246
1277
  listApps(orgId: string, projectId: string): Promise<ZitadelListAppsRawApp[]>;
1278
+ getApp?: unknown;
1247
1279
  }): ZitadelPort['listApps'];
1248
1280
  /**
1249
1281
  * The real `@wtfalch/providers` `ZitadelClient.listApps`' own return shape
@@ -1566,6 +1598,22 @@ export interface EdgePort {
1566
1598
  * `baseUrl` is refused (the real API's `409`); this port surfaces that as a
1567
1599
  * thrown error, like any other provider refusal in this file.
1568
1600
  */
1601
+ export interface BouleOwnersPort {
1602
+ /** Posts the owners to the company's Boule. Throws `BouleOwnersError` (status only) on any failure. */
1603
+ seed(input: {
1604
+ hostname: string;
1605
+ organisationId: string;
1606
+ secret: string;
1607
+ owners: readonly BouleOwnerSeed[];
1608
+ }): Promise<{
1609
+ results: readonly {
1610
+ subject: string;
1611
+ outcome: 'created' | 'exists' | 'refused';
1612
+ filled: readonly string[];
1613
+ code?: string;
1614
+ }[];
1615
+ }>;
1616
+ }
1569
1617
  export interface JobsSchedulerPort {
1570
1618
  registerProject(input: {
1571
1619
  name: string;
@@ -1613,6 +1661,8 @@ export interface FoundryStepExecutorPorts {
1613
1661
  * org's registration as blocked instead of planning a step that would throw.
1614
1662
  */
1615
1663
  jobsScheduler?: JobsSchedulerPort;
1664
+ /** Seeds a company's declared owners into its Boule. Default: {@link httpBouleOwners}. */
1665
+ bouleOwners?: BouleOwnersPort;
1616
1666
  /** The shared calls SFU's own LiveKit credentials (#145) -- see {@link CallsSfuPort}'s own doc comment. */
1617
1667
  callsSfu: CallsSfuPort;
1618
1668
  /**
@@ -2104,6 +2154,28 @@ export declare class FoundryStepExecutor implements StepExecutor, StepDestroyer
2104
2154
  * never the cause.
2105
2155
  */
2106
2156
  private zitadelIdTokenUserinfo;
2157
+ /**
2158
+ * Replaces the post-logout redirect URIs of each live app the planner named.
2159
+ * An app's marker (its id and the set it holds) is recorded and persisted
2160
+ * only after its call returned, so a call that throws leaves none and the
2161
+ * next plan names the app again. The error names ids only, never the cause.
2162
+ */
2163
+ private zitadelPostLogoutUris;
2164
+ /**
2165
+ * #339: registers `/auth/desktop/callback` on each live web sign-in app the
2166
+ * planner named, keeping every redirect URI the app already holds (the
2167
+ * union of the current list and the wanted one; a write only when the
2168
+ * union is larger). An app's marker (its ZITADEL app id) is recorded and
2169
+ * persisted only after its read and write resolved, so a failure leaves no
2170
+ * marker and the next plan names the app again. The error names ids only,
2171
+ * never the cause.
2172
+ */
2173
+ private zitadelDesktopCallback;
2174
+ /**
2175
+ * The hostnames a company web app signs in at: the same rule `zitadelApp`
2176
+ * uses for the redirect URIs it creates the app with.
2177
+ */
2178
+ private signInHostnames;
2107
2179
  /**
2108
2180
  * `plan/diff.ts`'s `planLoginHost` (#267): adds the org's own company
2109
2181
  * login host (`action.desired.host`) to the ZITADEL instance's trusted
@@ -2113,10 +2185,12 @@ export declare class FoundryStepExecutor implements StepExecutor, StepDestroyer
2113
2185
  *
2114
2186
  * In-app sign-in (`signIn.inApp`, `plan/sign-in.ts` has the order): an app host is trusted first. It is
2115
2187
  * bound, and its app pointed at `/sign-in/`, only once its app runs `SIGNIN_UPSTREAM` (the app step
2116
- * records that). The bind moves the app's client id off the company's binding in the same write (the
2117
- * login refuses a client id in two bindings); a company binding left with no client is removed.
2118
- * Switched off, the client id goes back and the base URI returns to the login host, and a trusted
2119
- * domain is given up only after the app dropped the env.
2188
+ * records that). From auth 0.28.0 the move is three writes (the login then allows one client in two
2189
+ * bindings of one company): the app host's binding with the id kept on the company's, the base URI, then
2190
+ * the company's binding without the id. Before it, one write moves the id (the login refuses a client in
2191
+ * two bindings) and a short gap follows. A company binding left with no client is removed.
2192
+ * Switched off, the id goes back to the company binding, then the base URI returns to the login host, then
2193
+ * the app binding goes; a trusted domain is given up only after the app dropped the env.
2120
2194
  *
2121
2195
  * #274: when the org's host has moved (`recorded[key]`, the *previous*
2122
2196
  * run's resourceId, differs from this run's `host`), also removes every
@@ -2308,6 +2382,18 @@ export declare class FoundryStepExecutor implements StepExecutor, StepDestroyer
2308
2382
  * the connection URL or a response body.
2309
2383
  */
2310
2384
  private coolifyBackup;
2385
+ private orgPostgresDeps;
2386
+ /** `coolify:org-postgres` (#444): the company's one Postgres server. */
2387
+ private orgPostgresServer;
2388
+ /** `coolify:org-postgres-databases` (#444): a database and its roles for each home that has none. */
2389
+ private orgPostgresDatabases;
2390
+ /**
2391
+ * `coolify:org-postgres-migrate` (#444): one home's data from its old Coolify database to the
2392
+ * company's server. Refuses before any port call unless the staging confirmation is recorded.
2393
+ */
2394
+ private orgPostgresMigrate;
2395
+ /** The apps that use a home's database, found the way each service finds its own app. */
2396
+ private migrationConsumers;
2311
2397
  private coolifyDatabase;
2312
2398
  /**
2313
2399
  * Pushes the database's connection string into the app's environment,
@@ -2327,6 +2413,26 @@ export declare class FoundryStepExecutor implements StepExecutor, StepDestroyer
2327
2413
  * never logged, never interpolated into a thrown error.
2328
2414
  */
2329
2415
  private pushDatabaseUrl;
2416
+ /**
2417
+ * #444: the home's owner URL on the company's Postgres server, built from
2418
+ * the server's live host and the owner password the host holds
2419
+ * (`deliverSecret` under `org-postgres:<slug>:<home>:owner`). Only for a
2420
+ * home {@link homeLocation} puts on the server. The password is registered
2421
+ * for redaction and goes only into the returned URL.
2422
+ */
2423
+ private serverHome;
2424
+ /** #444: whether `schema`'s database is on the company's server (recorded, and not waiting for its migration). */
2425
+ private homeOnServer;
2426
+ /**
2427
+ * #444: a home's database. On the company's server nothing is made or
2428
+ * recorded (the legacy id stays unset, or the home would read as unmigrated)
2429
+ * and `ensureLegacy` is never called, so no `POSTGRES_INITDB_ARGS` is set:
2430
+ * the provisioning job creates the database with its collation. Otherwise
2431
+ * today's calls, unchanged.
2432
+ */
2433
+ private ensureHomeDatabase;
2434
+ /** The owner URL of a home from {@link ensureHomeDatabase}; the legacy one is read live and started. */
2435
+ private homeOwnerUrl;
2330
2436
  /**
2331
2437
  * `null` once the app already has a `DATABASE_URL` -- never rotated, the
2332
2438
  * same rule `ensureCmsRuntimeUrl` follows and for the same reason: the
@@ -2406,6 +2512,7 @@ export declare class FoundryStepExecutor implements StepExecutor, StepDestroyer
2406
2512
  * hosting entry's is: with the `AUTH_APP_KEY` `coolifyBoule` put in the
2407
2513
  * Boule app's env.
2408
2514
  */
2515
+ private bouleOwners;
2409
2516
  private bouleBrokerEntry;
2410
2517
  /** The shared tail of both broker-entry steps: read the app's key, write the entry, record what was written. */
2411
2518
  private writeAppBrokerEntry;
@@ -2978,6 +3085,29 @@ export declare class FoundryStepExecutor implements StepExecutor, StepDestroyer
2978
3085
  * behind it.
2979
3086
  */
2980
3087
  private coolifyAgora;
3088
+ /**
3089
+ * `plan/desktop-auth.ts`'s `planDesktopAuth`: Agora's desktop sign-in backend, built from the agora repo's
3090
+ * `apps/desktop-auth`, internal only (Agora proxies `/auth/desktop/*` to `<slug>-desktop-auth:8080`).
3091
+ *
3092
+ * Env: the non-secret values from `desktopAuthEnv`, the owner and runtime URLs of the company server's
3093
+ * `desktop_auth` home (rebuilt from the held passwords on every run; the owner URL is for the migrate that
3094
+ * runs first in the image, which drops it before the server starts), and two secrets generated once. A secret
3095
+ * is minted only when the app's env lacks it, and both go into the one write with the rest, so a reapply or a
3096
+ * crash before the write never leaves two. Neither is delivered anywhere else.
3097
+ *
3098
+ * Deployed once after creation, again when the app is not running, and restarted when only the env changed
3099
+ * (decided from the live app, not from this run). The readiness probe goes through Agora once Agora carries
3100
+ * the upstream; the first apply has none to probe, and the next plan turns the upstream on. The callback URI is
3101
+ * added to Agora's client last, as the union of what the client holds and the callback (never removing one).
3102
+ */
3103
+ private coolifyDesktopAuth;
3104
+ /** Whether the org's running Agora carries `AGORA_DESKTOP_AUTH_UPSTREAM`, read from its live env. */
3105
+ private agoraUpstreamLive;
3106
+ /**
3107
+ * Adds `callback` to the Agora client's redirect URIs, keeping the ones it has. A providers client older
3108
+ * than 0.12.0 cannot do it: the step records that and the planner reports it blocked, rather than failing.
3109
+ */
3110
+ private addDesktopAuthCallback;
2981
3111
  /**
2982
3112
  * Records whether an app's running env holds `SIGNIN_UPSTREAM` (the app's host while it does, `''` once it
2983
3113
  * does not), so the login-host step knows when to point the base URI at the app and when it may give the