@wtfalch/foundry 0.4.0 → 0.6.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 (76) hide show
  1. package/dist/bootstrap/bootstrap.d.ts +111 -0
  2. package/dist/bootstrap/bootstrap.js +323 -0
  3. package/dist/bootstrap/declare.d.ts +36 -0
  4. package/dist/bootstrap/declare.js +99 -0
  5. package/dist/bootstrap/drive.d.ts +40 -0
  6. package/dist/bootstrap/drive.js +51 -0
  7. package/dist/bootstrap/index.d.ts +3 -0
  8. package/dist/bootstrap/index.js +1 -0
  9. package/dist/calls-sfu-deploy-support.d.ts +17 -6
  10. package/dist/calls-sfu-deploy-support.js +19 -7
  11. package/dist/calls-sfu-keys.d.ts +21 -0
  12. package/dist/calls-sfu-keys.js +27 -0
  13. package/dist/calls-sfu-ssh-adapter.d.ts +55 -0
  14. package/dist/calls-sfu-ssh-adapter.js +78 -0
  15. package/dist/calls-sfu-ssh-client.d.ts +49 -0
  16. package/dist/calls-sfu-ssh-client.js +82 -0
  17. package/dist/calls-sfu.d.ts +36 -12
  18. package/dist/calls-sfu.js +41 -9
  19. package/dist/index.d.ts +38 -7
  20. package/dist/index.js +20 -4
  21. package/dist/migrations/0016_platform.sql +61 -0
  22. package/dist/migrations/0017_requested_changes.sql +42 -0
  23. package/dist/migrations/0018_platform_apply.sql +19 -0
  24. package/dist/migrations/0019_mail_owner_mailbox.sql +15 -0
  25. package/dist/migrations/0020_mail_apps.sql +7 -0
  26. package/dist/migrations/0021_mail_role_mailboxes.sql +6 -0
  27. package/dist/org.d.ts +81 -0
  28. package/dist/org.js +96 -0
  29. package/dist/plan/agora.d.ts +77 -0
  30. package/dist/plan/agora.js +127 -0
  31. package/dist/plan/cloudflared-connector.d.ts +70 -0
  32. package/dist/plan/cloudflared-connector.js +106 -0
  33. package/dist/plan/diff.d.ts +20 -3
  34. package/dist/plan/diff.js +713 -115
  35. package/dist/plan/executor.d.ts +680 -172
  36. package/dist/plan/executor.js +755 -189
  37. package/dist/plan/index.d.ts +28 -5
  38. package/dist/plan/index.js +15 -2
  39. package/dist/plan/jobs-scheduler-http.d.ts +29 -0
  40. package/dist/plan/jobs-scheduler-http.js +50 -0
  41. package/dist/plan/live.d.ts +51 -2
  42. package/dist/plan/live.js +8 -0
  43. package/dist/plan/mail-copy.d.ts +125 -0
  44. package/dist/plan/mail-copy.js +166 -0
  45. package/dist/plan/mail-identity-allowlist.d.ts +50 -0
  46. package/dist/plan/mail-identity-allowlist.js +11 -0
  47. package/dist/plan/mail-identity.d.ts +68 -0
  48. package/dist/plan/mail-identity.js +17 -0
  49. package/dist/plan/plan-apply.d.ts +197 -3
  50. package/dist/plan/plan-apply.js +278 -9
  51. package/dist/plan/platform-diff.d.ts +487 -0
  52. package/dist/plan/platform-diff.js +1220 -0
  53. package/dist/plan/platform-org-ports.d.ts +50 -0
  54. package/dist/plan/platform-org-ports.js +70 -0
  55. package/dist/plan/platform-org.d.ts +121 -0
  56. package/dist/plan/platform-org.js +115 -0
  57. package/dist/plan/platform-providers.d.ts +615 -0
  58. package/dist/plan/platform-providers.js +4410 -0
  59. package/dist/plan/platform-sfu.d.ts +128 -0
  60. package/dist/plan/platform-sfu.js +142 -0
  61. package/dist/plan/providers-readers.js +22 -1
  62. package/dist/plan/zitadel-machine-user.d.ts +83 -0
  63. package/dist/plan/zitadel-machine-user.js +238 -0
  64. package/dist/plan.d.ts +6 -0
  65. package/dist/platform.d.ts +55 -0
  66. package/dist/platform.js +46 -0
  67. package/dist/requested-changes.d.ts +118 -0
  68. package/dist/requested-changes.js +125 -0
  69. package/dist/slug.js +15 -2
  70. package/dist/store/audit.js +4 -2
  71. package/dist/store/postgres.d.ts +29 -1
  72. package/dist/store/postgres.js +203 -4
  73. package/dist/store/schema.d.ts +755 -0
  74. package/dist/store/schema.js +70 -0
  75. package/dist/store.d.ts +53 -0
  76. package/package.json +1 -1
@@ -0,0 +1,487 @@
1
+ import type { OrgId } from '../org.js';
2
+ import type { Plan, UnmanagedItem } from '../plan.js';
3
+ import type { DeclaredPlatform } from '../platform.js';
4
+ import type { PlannedStep } from './diff.js';
5
+ import type { LiveResource } from './live.js';
6
+ /**
7
+ * The platform's own diff result, the {@link DeclaredPlatform} counterpart to
8
+ * `diff.ts`'s `PlanDetail` -- everything the same, minus the `orgId` an org's
9
+ * plan carries, since a platform is one singleton record, not one per org
10
+ * (`platform.ts`'s own doc comment). {@link toPlatformPlan} below turns this
11
+ * into the public `Plan` (`../plan.ts`) `FoundryPlanApply.planPlatform`
12
+ * returns.
13
+ */
14
+ export interface PlatformPlanDetail {
15
+ plannedSteps: PlannedStep[];
16
+ unmanaged: UnmanagedItem[];
17
+ }
18
+ /**
19
+ * The `Plan` (#19's public contract) a {@link PlatformPlanDetail} carries,
20
+ * mirroring `diff.ts`'s `toPlan`. There is no separate platform-shaped `Plan`
21
+ * type: `orgId` is stamped with the platform's own `ownerOrgId` -- Company
22
+ * Included's own {@link OrgId}, the org whose money and access the platform's
23
+ * resources are billed and owned by (`platform.ts`'s `DeclaredPlatform.ownerOrgId`
24
+ * doc comment) -- so `AuditLog`/`AuthzGate` (`plan-apply.ts`), both keyed by
25
+ * `OrgId`, need no platform-specific shape of their own either.
26
+ */
27
+ export declare function toPlatformPlan(detail: PlatformPlanDetail, ownerOrgId: OrgId): Plan;
28
+ /**
29
+ * #168: the resource key `planAuth` plans under and `FoundryPlatformStepExecutor`
30
+ * (`platform-providers.ts`) dispatches on -- `platform:<service>`, exactly the
31
+ * shape `planPlatform`'s own doc comment above already named as the eventual
32
+ * convention, so a platform service's `coolify:app` step never collides with
33
+ * an org's own (which is keyed by that org's id).
34
+ */
35
+ export declare const PLATFORM_AUTH_KEY = "platform:auth";
36
+ /**
37
+ * Scope keys (colon-free, so never mistaken for a `system:kind:key` resource
38
+ * id) for foundry's ZITADEL admin machine user. The delivered key is the guard
39
+ * that skips the mint, recorded only once the token reached the host. The
40
+ * undelivered key is recorded first, the moment the user is minted: an id
41
+ * under it with no delivered key means the one-time token was lost, and the
42
+ * planner reports that instead of minting again.
43
+ */
44
+ /**
45
+ * #175: the resource key `planArchon` plans under and
46
+ * `FoundryPlatformStepExecutor` (`platform-providers.ts`) dispatches on --
47
+ * the same `platform:<service>` shape as {@link PLATFORM_AUTH_KEY}.
48
+ */
49
+ export declare const PLATFORM_ARCHON_KEY = "platform:archon";
50
+ /**
51
+ * #175: the hostname and `gitRef` the last successful Archon run applied.
52
+ * `planArchon` plans the step again when either differs from the declared
53
+ * value; a re-run patches `git_branch`, `APP_URL` and the sign-in client and
54
+ * redeploys, and mints nothing.
55
+ */
56
+ export declare const PLATFORM_ARCHON_APPLIED_HOSTNAME_SCOPE_KEY = "archon_applied_hostname";
57
+ export declare const PLATFORM_ARCHON_APPLIED_GIT_REF_SCOPE_KEY = "archon_applied_git_ref";
58
+ /**
59
+ * #211 round 4: the broker app id Archon last registered its entry with.
60
+ * `provisionPlatformArchon` reads the broker to register with from
61
+ * {@link PLATFORM_AUTH_BROKER_SCOPE_KEY} alone, so a broker recreated under a
62
+ * new app id (the old one deleted by hand, or `platform:auth` minting a fresh
63
+ * one) leaves Archon "applied" against a broker that no longer holds its
64
+ * entry. `planArchon` plans the step again whenever this differs from the
65
+ * currently recorded {@link PLATFORM_AUTH_BROKER_SCOPE_KEY}, the same way a
66
+ * changed hostname or `gitRef` does.
67
+ */
68
+ export declare const PLATFORM_ARCHON_APPLIED_BROKER_SCOPE_KEY = "archon_applied_broker_app";
69
+ /**
70
+ * #175: set to '1' before the step points the tunnel at `http_status:404`
71
+ * (Access app rebuild), cleared to '' once the real ingress is written back.
72
+ * While it is non-empty `planArchon` plans the step, so a run that failed in
73
+ * between is repaired by the next one.
74
+ */
75
+ export declare const PLATFORM_ARCHON_GATE_CLOSED_SCOPE_KEY = "archon_gate_closed";
76
+ export declare const PLATFORM_AUTH_ADMIN_USER_SCOPE_KEY = "zitadel_platform_admin_user_id";
77
+ export declare const PLATFORM_AUTH_ADMIN_UNDELIVERED_SCOPE_KEY = "zitadel_platform_admin_user_undelivered";
78
+ /**
79
+ * #216 (building on #209/#215): the `IAM_LOGIN_CLIENT` machine user minted
80
+ * for the login app and the broker, mirroring
81
+ * {@link PLATFORM_AUTH_ADMIN_USER_SCOPE_KEY}/
82
+ * {@link PLATFORM_AUTH_ADMIN_UNDELIVERED_SCOPE_KEY} in shape -- a recorded
83
+ * id once delivered, an undelivered id recorded first, the moment the user
84
+ * is minted.
85
+ *
86
+ * Unlike the admin pair, an undelivered login-client key never blocks
87
+ * {@link planAuth} from planning the step again -- #216 round 2: `planAuth`
88
+ * below does read it, but only to surface a non-blocking note (alongside a
89
+ * planned step, the way #211 round 3's broker-deploy block already is)
90
+ * rather than to hold the step back. Revoking a stale login-client PAT
91
+ * destroys nothing -- unlike the admin user's, whose loss the admin pair's
92
+ * own "delete that user by hand" message exists for because nothing else
93
+ * here re-derives the ZITADEL masterkey a lost admin PAT might have been the
94
+ * only record of -- so `mintLoginClientMachineUser` (`zitadel-machine-user.ts`)
95
+ * always lists and deletes every PAT the user already holds before minting a
96
+ * fresh one. That is what lets `provisionPlatformAuth` simply re-mint on the
97
+ * next run, with no manual step, which is #216's whole fix for the bug PR
98
+ * #215 shipped with: PR #215 mirrored the admin pair's forever-blocking
99
+ * behaviour for this key too, which meant one failed Coolify call lost the
100
+ * login-client token for good.
101
+ *
102
+ * #216 round 4: the value recorded here is JSON ({@link
103
+ * PlatformAuthLoginClientRecord}, parsed back by
104
+ * {@link parseLoginClientRecord}), not the bare user id round 2-3 wrote --
105
+ * the user id alone can never tell a healthy delivery from one a concurrent
106
+ * apply has since clobbered, because `mintLoginClientMachineUser` reuses the
107
+ * same user on every mint. The PAT id travels alongside it so {@link
108
+ * planAuth} can ask ZITADEL, on every plan, whether that exact PAT is still
109
+ * the sole live one.
110
+ *
111
+ * #216 round 5: that alone is still not enough -- a recorded PAT can be the
112
+ * sole live one in ZITADEL while Coolify holds a *different*, already-dead
113
+ * token, when two concurrent applies interleave their own writes and
114
+ * records in the right (wrong) order (see `authLoginClientDrifted`'s own
115
+ * doc comment in `platform-providers.ts` for the exact ordering). So the
116
+ * record also carries `tokenHash`, a sha256 of the exact token that was
117
+ * delivered -- never the token itself -- which `authLoginClientDrifted`
118
+ * compares against what is actually live in both apps' Coolify envs.
119
+ */
120
+ export declare const PLATFORM_AUTH_LOGIN_CLIENT_USER_SCOPE_KEY = "zitadel_platform_login_client_user_id";
121
+ export declare const PLATFORM_AUTH_LOGIN_CLIENT_UNDELIVERED_SCOPE_KEY = "zitadel_platform_login_client_user_undelivered";
122
+ /**
123
+ * #216 round 4/5: what {@link PLATFORM_AUTH_LOGIN_CLIENT_USER_SCOPE_KEY}
124
+ * records once delivery succeeds -- the minted machine user's id, the exact
125
+ * PAT id that was handed to the login app and the broker, and (round 5) a
126
+ * sha256 of the exact token value, never the value itself.
127
+ */
128
+ export interface PlatformAuthLoginClientRecord {
129
+ readonly userId: string;
130
+ readonly patId: string;
131
+ readonly tokenHash: string;
132
+ }
133
+ /**
134
+ * Parses a {@link PLATFORM_AUTH_LOGIN_CLIENT_USER_SCOPE_KEY} scope id back
135
+ * into a {@link PlatformAuthLoginClientRecord}. `null` for anything that is
136
+ * not one -- absent, the pre-round-4 plain user-id string, the round 2-4
137
+ * `{userId, patId}` shape with no `tokenHash`, or corrupt JSON -- the same
138
+ * "malformed reads as unapplied, never throws" rule
139
+ * {@link parseBrokerDeployRecord} follows and for the same reason: this is a
140
+ * planning read, not a validation boundary. A pre-round-5 record reading as
141
+ * unapplied is the intended migration: the next apply's fresh mint recomputes
142
+ * it in the new shape, the same way a pre-round-4 plain-string record already
143
+ * did.
144
+ */
145
+ export declare function parseLoginClientRecord(raw: string | undefined): PlatformAuthLoginClientRecord | null;
146
+ /**
147
+ * #204/#211: the sign-in broker app `provisionPlatformAuth` creates alongside
148
+ * ZITADEL and the login app. Defined here, not in `platform-providers.ts`
149
+ * (which imports it), because `planAuth` below needs it too -- a recorded
150
+ * `coolify:app:platform:auth` key with no recorded broker id is a platform
151
+ * provisioned before #204 shipped, and must still plan the step again so the
152
+ * broker gets created rather than being read as fully applied.
153
+ */
154
+ export declare const PLATFORM_AUTH_BROKER_SCOPE_KEY = "coolify_platform_auth_broker_app";
155
+ /**
156
+ * #211: recorded once the broker has actually been deployed -- distinct from
157
+ * {@link PLATFORM_AUTH_BROKER_SCOPE_KEY} (the app exists) precisely because
158
+ * the app can exist for a while before it is ever deployed (see
159
+ * `missingBrokerInputs` in `platform-providers.ts`). Defined here, not there,
160
+ * for the same reason as {@link PLATFORM_AUTH_BROKER_SCOPE_KEY}: `planAuth`
161
+ * below needs it too, so a broker that is created but not yet deployed still
162
+ * plans the step again instead of reading as fully applied for good.
163
+ */
164
+ export declare const PLATFORM_AUTH_BROKER_DEPLOYED_SCOPE_KEY = "coolify_platform_auth_broker_deployed";
165
+ /**
166
+ * #211 round 5: what {@link PLATFORM_AUTH_BROKER_DEPLOYED_SCOPE_KEY} records
167
+ * once a deploy succeeds -- the broker app id that was deployed, and a sha256
168
+ * of the exact `AUTH_APPS` value that was live at that deploy, never the
169
+ * value itself (`platform-providers.ts`'s `hashAuthApps`: a secret must never
170
+ * reach a recorded scope id). Round 2-4 recorded a plain broker-id string
171
+ * there, which could only ever mean "deployed at least once" -- it could not
172
+ * tell a broker recreated under a new app id, or a crash between writing
173
+ * `AUTH_APPS` and deploying, from one still fully applied. This record is
174
+ * what lets `brokerDeployNeeded` (`platform-providers.ts`) and {@link planAuth}
175
+ * below answer both, from one mechanism instead of two ad hoc booleans.
176
+ */
177
+ export interface BrokerDeployRecord {
178
+ readonly brokerAppId: string;
179
+ readonly authAppsHash: string;
180
+ }
181
+ /**
182
+ * Parses a {@link PLATFORM_AUTH_BROKER_DEPLOYED_SCOPE_KEY} scope id back into
183
+ * a {@link BrokerDeployRecord}. `null` for anything that is not one --
184
+ * absent, the pre-round-5 plain broker-id string, or corrupt JSON -- because
185
+ * this is a planning read, not a validation boundary: a malformed record
186
+ * must read as "not deployed" so the step plans (and overwrites it) again,
187
+ * never throw and take the rest of the plan down with it. Exported here
188
+ * rather than defined in `platform-providers.ts` for the same reason
189
+ * {@link PLATFORM_AUTH_BROKER_DEPLOYED_SCOPE_KEY} itself was moved here in
190
+ * round 2 -- {@link planAuth} below needs it too, without a circular import.
191
+ */
192
+ export declare function parseBrokerDeployRecord(raw: string | undefined): BrokerDeployRecord | null;
193
+ /**
194
+ * #197: the `cloudflared` connector app `provisionPlatformAuth` runs for its
195
+ * own tunnel. Consulted by `planAuth` below so that a tunnel recorded with no
196
+ * connector recorded is not treated as fully applied -- the same rule that
197
+ * lets an instance provisioned before #197 shipped pick up a connector on its
198
+ * next apply, without minting anything again.
199
+ */
200
+ export declare const PLATFORM_AUTH_CONNECTOR_APP_SCOPE_KEY = "coolify_platform_auth_connector_app";
201
+ /** #197: the same connector scope id for the mail step's own tunnel, read by `planMail`. */
202
+ export declare const PLATFORM_MAIL_CONNECTOR_APP_SCOPE_KEY = "coolify_platform_mail_connector_app";
203
+ /** #197: the same connector scope id for the Archon step's own tunnel, read by `planArchon`. */
204
+ export declare const PLATFORM_ARCHON_CONNECTOR_APP_SCOPE_KEY = "coolify_platform_archon_connector_app";
205
+ /**
206
+ * #213: the three held-key refs, under tenant `platform`, Archon's own
207
+ * onboarding reads before it can start (archon#101 round 3, archon#104).
208
+ * `planArchon`'s step puts each one, once, before Archon's app deploys; an
209
+ * entry already held is left alone, never overwritten
210
+ * (`putArchonMasterKeys` in `platform-providers.ts`). The Coolify URL and
211
+ * token are values this package already deploys with; the Cloudflare
212
+ * parent token ("API Tokens Write", so Archon can mint its own scoped
213
+ * child tokens from it) is a human-created credential the host supplies --
214
+ * neither Coolify nor Cloudflare lets either be minted over an API (#213
215
+ * "Human steps").
216
+ */
217
+ export declare const PLATFORM_ARCHON_MASTER_COOLIFY_URL_REF = "platform:master:coolify-url";
218
+ export declare const PLATFORM_ARCHON_MASTER_COOLIFY_TOKEN_REF = "platform:master:coolify-token";
219
+ export declare const PLATFORM_ARCHON_MASTER_CLOUDFLARE_TOKEN_REF = "platform:master:cloudflare-token";
220
+ /**
221
+ * #169: `platform:<service>` for the platform's own mail server (Stalwart),
222
+ * mirroring {@link PLATFORM_AUTH_KEY} exactly.
223
+ */
224
+ export declare const PLATFORM_MAIL_KEY = "platform:mail";
225
+ /**
226
+ * Scope keys for Stalwart's admin API key, mirroring
227
+ * {@link PLATFORM_AUTH_ADMIN_USER_SCOPE_KEY}/{@link PLATFORM_AUTH_ADMIN_UNDELIVERED_SCOPE_KEY}
228
+ * -- except `createApiKey` (`@wtfalch/email/postmaster`'s `StalwartClient`)
229
+ * returns only the minted secret, never an id, so there is no id to record
230
+ * the way ZITADEL's `userId` is. `PLATFORM_MAIL_ADMIN_KEY_DESCRIPTION`
231
+ * (`platform-providers.ts`) is recorded instead -- the fixed description the
232
+ * key is minted under, which is what a human revokes it by in Stalwart's own
233
+ * admin UI once it is lost.
234
+ */
235
+ export declare const PLATFORM_MAIL_ADMIN_KEY_SCOPE_KEY = "stalwart_platform_admin_key_delivered";
236
+ export declare const PLATFORM_MAIL_ADMIN_UNDELIVERED_SCOPE_KEY = "stalwart_platform_admin_key_undelivered";
237
+ /**
238
+ * #169: the hostname and `gitRef` (the Stalwart image tag) the last successful
239
+ * mail run applied. `planMail` plans the step again when either differs from
240
+ * the declared value; a re-run patches the image tag and
241
+ * `STALWART_PUBLIC_URL`, redeploys, and mints nothing.
242
+ */
243
+ export declare const PLATFORM_MAIL_APPLIED_HOSTNAME_SCOPE_KEY = "mail_applied_hostname";
244
+ export declare const PLATFORM_MAIL_APPLIED_GIT_REF_SCOPE_KEY = "mail_applied_git_ref";
245
+ /**
246
+ * #177: where the mail server answers on the Coolify network
247
+ * (`http://<custom_internal_name>:<port>`), recorded by the step the same way
248
+ * {@link PLATFORM_FILES_INTERNAL_URL_SCOPE_KEY}/{@link PLATFORM_AI_INTERNAL_URL_SCOPE_KEY}
249
+ * already are -- what an org's own `coolify:mail`/`coolify:agora` steps read
250
+ * for `AGORA_MAIL_UPSTREAM` (`platform-org.ts`'s `agoraHostValuesFromPlatform`)
251
+ * instead of a literal a host used to have to supply. Not a secret: Stalwart's
252
+ * own internal address, never its admin credential.
253
+ */
254
+ export declare const PLATFORM_MAIL_INTERNAL_URL_SCOPE_KEY = "mail_internal_url";
255
+ /**
256
+ * #177: the one host the platform's mail server takes SMTP/MX on, and so the
257
+ * `mailHost` every org's own mail step writes. `provisionPlatformMail` (the
258
+ * DNS record and the server) and `platform-org.ts` (each org's mail step)
259
+ * both call this, so the two cannot name different hosts. Not
260
+ * `platform.mail.hostname`, which is the web (JMAP/admin) host.
261
+ */
262
+ export declare function platformMailMxHost(apex: string): string;
263
+ /**
264
+ * #177: the platform apex and mail host an org's `coolify:mail` step last
265
+ * wrote, recorded in that org's applied state. `diff.ts`'s `planMail` plans
266
+ * the step again when either differs from the platform's current values, so a
267
+ * later platform address change reaches orgs that are already applied.
268
+ */
269
+ export declare const ORG_MAIL_APPLIED_APEX_SCOPE_KEY = "mail_applied_apex";
270
+ export declare const ORG_MAIL_APPLIED_HOST_SCOPE_KEY = "mail_applied_host";
271
+ /**
272
+ * #172: the resource key `planAi` plans under and `FoundryPlatformStepExecutor`
273
+ * dispatches on, the same `platform:<service>` shape as
274
+ * {@link PLATFORM_AUTH_KEY}. The step's own recorded resource id (the ai
275
+ * host's runner app) lands at `coolify:app:platform:ai`.
276
+ */
277
+ export declare const PLATFORM_AI_KEY = "platform:ai";
278
+ /**
279
+ * #172: where the ai host answers on the Coolify network
280
+ * (`http://<custom_internal_name>:<port>`), recorded by the step so a host
281
+ * building `AiOrganisationPort` (`executor.ts`) can reach it without a
282
+ * public hostname. Holds whichever way the public-hostname decision goes.
283
+ */
284
+ export declare const PLATFORM_AI_INTERNAL_URL_SCOPE_KEY = "ai_platform_internal_url";
285
+ /**
286
+ * #172: the same two-key discipline as the ZITADEL admin key above, for
287
+ * foundry's AI management key (the bearer key `ai:organisation`, `ai:budget`
288
+ * and `ai:key` use). The undelivered key holds the credential id the moment
289
+ * it is minted; the delivered key is recorded only once the secret reached
290
+ * the host, and is what skips the mint on a later run.
291
+ */
292
+ export declare const PLATFORM_AI_MANAGEMENT_KEY_SCOPE_KEY = "ai_platform_management_key_id";
293
+ export declare const PLATFORM_AI_MANAGEMENT_KEY_UNDELIVERED_SCOPE_KEY = "ai_platform_management_key_undelivered";
294
+ /**
295
+ * #201: set when ai's `POST /v1/platform/keys` reports its `archon` key
296
+ * already exists but returns no secret -- a mint this record never learned
297
+ * about (most likely the crash window {@link PLATFORM_AI_MANAGEMENT_KEY_UNDELIVERED_SCOPE_KEY}'s
298
+ * own comment describes: the secret returned once and was never recorded).
299
+ * ai shows a credential's secret only at mint time, so it cannot be
300
+ * recovered; the planner reports this instead of minting under another
301
+ * name. A person revokes the live `archon` key in ai and removes this
302
+ * recorded key to let the step mint a fresh one.
303
+ */
304
+ export declare const PLATFORM_AI_MANAGEMENT_KEY_CONFLICT_SCOPE_KEY = "ai_platform_management_key_conflict";
305
+ /**
306
+ * #201: the same two-key discipline as {@link PLATFORM_INTEGRATIONS_KEY_UNDELIVERED_SCOPE_KEY}
307
+ * and {@link PLATFORM_INTEGRATIONS_KEY_SET_SCOPE_KEY}, for the Ed25519 pair
308
+ * foundry mints to sign its calls to ai's `POST /v1/platform/keys`. The
309
+ * undelivered key is recorded the moment the pair is minted; the set key,
310
+ * only once the private half was delivered and the public half pinned on
311
+ * the runner as `MANAGEMENT_PUBLIC_KEYS`.
312
+ */
313
+ export declare const PLATFORM_AI_SIGNING_KEY_UNDELIVERED_SCOPE_KEY = "ai_platform_signing_key_undelivered";
314
+ export declare const PLATFORM_AI_SIGNING_KEY_SET_SCOPE_KEY = "ai_platform_signing_key_set";
315
+ /**
316
+ * #172: ai's platform (operator) tenant id, which the management key belongs
317
+ * to -- what ai's own README calls `AI_ORGANISATION_ID`. Not a secret.
318
+ */
319
+ export declare const PLATFORM_AI_ORGANISATION_SCOPE_KEY = "ai_platform_organisation_id";
320
+ /**
321
+ * #172: the hostname and gitRef the last apply finished with. A declared
322
+ * value that differs plans the step again (without minting again, the
323
+ * management key's own guard stays set).
324
+ */
325
+ export declare const PLATFORM_AI_APPLIED_HOSTNAME_SCOPE_KEY = "ai_platform_applied_hostname";
326
+ export declare const PLATFORM_AI_APPLIED_GITREF_SCOPE_KEY = "ai_platform_applied_gitref";
327
+ /**
328
+ * #172: set (with the migrate job's last status) when the migrate job did
329
+ * not turn healthy in time. The planner reports it as blocked and plans
330
+ * nothing, so an unhealthy migration is not retried and timed out on every
331
+ * apply. A person fixes the job and removes this recorded key.
332
+ */
333
+ export declare const PLATFORM_AI_MIGRATE_BLOCKED_SCOPE_KEY = "ai_platform_migrate_blocked";
334
+ /**
335
+ * #172: set before an env or branch change is written and cleared once the
336
+ * deploys that carry it finished, so a run that fails halfway (a migrate
337
+ * that throws) is repaired by the next one.
338
+ */
339
+ export declare const PLATFORM_AI_DEPLOY_PENDING_SCOPE_KEY = "ai_platform_deploy_pending";
340
+ /**
341
+ * #171: the resource key `planFiles` plans under and `FoundryPlatformStepExecutor`
342
+ * dispatches on, the {@link PLATFORM_AUTH_KEY} shape for the files host.
343
+ * `runPlatformSteps` records the files runner app's uuid under
344
+ * `coolify:app:platform:files` once the step succeeds, which is what stops
345
+ * `planFiles` planning it again.
346
+ */
347
+ export declare const PLATFORM_FILES_KEY = "platform:files";
348
+ /**
349
+ * #171: scope keys (colon-free) the files step records in the applied
350
+ * platform.
351
+ *
352
+ * - {@link PLATFORM_FILES_INTERNAL_URL_SCOPE_KEY}: the files host's address on
353
+ * the Coolify network (`http://<custom_internal_name>:3000`), the value an
354
+ * Agora's `AGORA_FILES_UPSTREAM` (and a host's `FilesOrganisationPort`
355
+ * client) is read from. Not a secret. The host has no public address.
356
+ * - {@link PLATFORM_FILES_ZITADEL_PROJECT_SCOPE_KEY}: the files ZITADEL
357
+ * project's id in the platform's own sign-in, which is the person-token
358
+ * audience (`AUTH_PERSON_TOKEN_AUDIENCE`) an Agora requests as an extra
359
+ * scope (`urn:zitadel:iam:org:project:id:<id>:aud`).
360
+ */
361
+ export declare const PLATFORM_FILES_INTERNAL_URL_SCOPE_KEY = "files_internal_url";
362
+ export declare const PLATFORM_FILES_ZITADEL_PROJECT_SCOPE_KEY = "zitadel_platform_files_project";
363
+ /**
364
+ * #171: scope keys (colon-free) for the files host's two one-time secrets,
365
+ * the {@link PLATFORM_AUTH_ADMIN_USER_SCOPE_KEY} pattern twice. Each
366
+ * `*_UNDELIVERED_*` key is recorded and persisted the moment its secret is
367
+ * minted, before delivery. Each guard is recorded only once the secret was
368
+ * delivered AND written to the runner's env. An undelivered id with no guard
369
+ * means the secret is lost: the planner reports it and never mints again.
370
+ * None of these holds a secret.
371
+ */
372
+ export declare const PLATFORM_FILES_INTROSPECTION_SCOPE_KEY = "zitadel_platform_files_introspection_app";
373
+ export declare const PLATFORM_FILES_INTROSPECTION_UNDELIVERED_SCOPE_KEY = "zitadel_platform_files_introspection_undelivered";
374
+ export declare const PLATFORM_FILES_MANAGEMENT_KEY_SCOPE_KEY = "files_management_key_id";
375
+ export declare const PLATFORM_FILES_MANAGEMENT_UNDELIVERED_SCOPE_KEY = "files_management_key_undelivered";
376
+ /**
377
+ * #171: the hostname and `gitRef` the last successful files run applied.
378
+ * `planFiles` plans the step again when either differs from the declared
379
+ * value; a re-run updates `git_branch` and the runner env and redeploys,
380
+ * and mints nothing (the guards above hold).
381
+ */
382
+ export declare const PLATFORM_FILES_APPLIED_HOSTNAME_SCOPE_KEY = "files_applied_hostname";
383
+ export declare const PLATFORM_FILES_APPLIED_GIT_REF_SCOPE_KEY = "files_applied_git_ref";
384
+ /**
385
+ * Options for {@link planPlatform}, replacing four positional arguments that
386
+ * shifted on every merge that added a platform service (#186, #187, #188) --
387
+ * see https://github.com/wtfalch/foundry/pull/188#issuecomment-5895935971,
388
+ * the merge comment that named this shape (#189).
389
+ */
390
+ export interface PlanPlatformOptions {
391
+ /** Whether the host supplies a Coolify reader for the platform (default `true`). */
392
+ jobsObservable?: boolean;
393
+ /** Why the shared SFU is blocked, or `null` when it is not (default `null`). */
394
+ sfuBlocked?: string | null;
395
+ /** The platform's recorded resource/scope ids (default `{}`). */
396
+ recordedIds?: Readonly<Record<string, string>>;
397
+ /** Per-step block reasons the executor reports (default `{}`). */
398
+ blocked?: {
399
+ auth?: string;
400
+ /**
401
+ * #211 round 6: set once the auth block is "the host built the platform
402
+ * executor without its auth dependencies" -- distinct from `auth` itself,
403
+ * which also covers "the broker exists but cannot deploy yet" (#209/#210
404
+ * still pending). `planAuth` must withhold the step for the former
405
+ * whatever the recorded ids say; it must not for the latter.
406
+ */
407
+ authExecutorMissing?: boolean;
408
+ /**
409
+ * #216 round 5: the broker's required inputs other than
410
+ * `ZITADEL_SERVICE_USER_TOKEN` that are still missing (a subset of what
411
+ * `auth`'s own text already names, filtered) -- {@link planAuth} reads
412
+ * this to say, accurately, whether a needed login-client re-mint can
413
+ * actually happen on the next apply or is itself waiting on one of
414
+ * these. Default `[]`.
415
+ */
416
+ authOtherInputsMissing?: string[];
417
+ files?: string;
418
+ integrations?: string;
419
+ ai?: string;
420
+ archon?: string;
421
+ mail?: string;
422
+ };
423
+ /**
424
+ * Steps whose live state differs from the declared one in a way the recorded
425
+ * ids cannot show (default `{}`). `archon`: the Cloudflare Access policy's
426
+ * include list is not the declared one. `auth` (#216 round 4): the
427
+ * login-client PAT recorded under
428
+ * {@link PLATFORM_AUTH_LOGIN_CLIENT_USER_SCOPE_KEY} is no longer the sole
429
+ * live PAT on that ZITADEL machine user.
430
+ */
431
+ drifted?: {
432
+ archon?: boolean;
433
+ auth?: boolean;
434
+ };
435
+ }
436
+ /**
437
+ * #167: the platform's own diff engine, mirroring `diff.ts`'s
438
+ * `computePlanDetail` for the one shared {@link DeclaredPlatform} record
439
+ * (#166) rather than one `DeclaredOrg` per call. It reuses the same step
440
+ * kinds an org plans against (`coolify:app`, `coolify:database`,
441
+ * `cloudflare:tunnel`, `cloudflare:zone`, `zitadel:app`), keyed under the
442
+ * platform's own resources rather than an org id -- a platform service's
443
+ * eventual `coolify:app` step, for one example, is keyed `platform:<service>`
444
+ * (`platform.ts`'s `PlatformServiceKey`) rather than an `OrgId`, once that
445
+ * step exists.
446
+ *
447
+ * Scope for #167: the frame plus one step, {@link planApexZone}. #173 adds
448
+ * {@link planJobs}, #170 adds {@link planSfu}, #168 adds {@link planAuth},
449
+ * #169 adds {@link planMail}, #174 adds {@link planIntegrations}, #171 adds
450
+ * {@link planFiles}, #172 adds {@link planAi} and #175 adds
451
+ * {@link planArchon}, the same way #19 shipped org plan/apply against
452
+ * three singletons before #27, #71, #80 and the rest added the remaining
453
+ * twelve. Until then, a live resource under one of those eventual keys is
454
+ * reported unmanaged rather than silently ignored (#10's rule, carried over
455
+ * unchanged).
456
+ */
457
+ export declare function planPlatform(platform: DeclaredPlatform, live: LiveResource[], options?: PlanPlatformOptions): PlatformPlanDetail;
458
+ /**
459
+ * The one Coolify application (and key) every recorded jobs-scheduler
460
+ * resource, and its `coolify:jobs` step, is keyed under -- there is exactly
461
+ * one scheduler for the whole platform, so this is a fixed constant rather
462
+ * than something derived from `platform` (`platform-providers.ts`'s
463
+ * `FoundryPlatformStepExecutor`/`ProvidersPlatformCoolifyReader` both import
464
+ * it, so the planner and the executor/reader can never key it apart).
465
+ */
466
+ export declare const JOBS_SCHEDULER_KEY = "jobs";
467
+ /**
468
+ * #174: the resource key the integrations host is planned and recorded under,
469
+ * `platform:integrations`, like {@link PLATFORM_AUTH_KEY}: the step is
470
+ * `coolify:app` keyed by it, and the id `runPlatformSteps` records is
471
+ * `coolify:app:platform:integrations`. The planner and the executor
472
+ * (`platform-providers.ts`) both import this one value.
473
+ */
474
+ export declare const PLATFORM_INTEGRATIONS_KEY = "platform:integrations";
475
+ /**
476
+ * Scope keys (colon-free) for the integrations host's Ed25519 management key.
477
+ * The undelivered key is recorded first, the moment the pair is minted. The
478
+ * set key is the guard, recorded only after the private half was delivered
479
+ * and the public half written to the runner. An id under the undelivered key
480
+ * with no guard means the pair may be lost or half-applied, and the planner
481
+ * reports that instead of minting again.
482
+ */
483
+ export declare const PLATFORM_INTEGRATIONS_KEY_UNDELIVERED_SCOPE_KEY = "integrations_management_key_undelivered";
484
+ export declare const PLATFORM_INTEGRATIONS_KEY_SET_SCOPE_KEY = "integrations_management_key_set";
485
+ /** The hostname and gitRef the last successful apply put live; a difference from the declared ones plans the step again. */
486
+ export declare const PLATFORM_INTEGRATIONS_APPLIED_HOSTNAME_SCOPE_KEY = "integrations_applied_hostname";
487
+ export declare const PLATFORM_INTEGRATIONS_APPLIED_GITREF_SCOPE_KEY = "integrations_applied_gitref";