@wtfalch/foundry 0.18.0 → 0.19.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.
- package/dist/bootstrap/declare.d.ts +11 -2
- package/dist/bootstrap/declare.js +17 -8
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/plan/agora.d.ts +36 -0
- package/dist/plan/agora.js +38 -0
- package/dist/plan/boule.d.ts +8 -0
- package/dist/plan/boule.js +8 -0
- package/dist/plan/chat-matrix.d.ts +3 -0
- package/dist/plan/chat-matrix.js +2 -0
- package/dist/plan/diff.js +120 -36
- package/dist/plan/executor.d.ts +51 -5
- package/dist/plan/executor.js +209 -23
- package/dist/plan/login-host.d.ts +15 -11
- package/dist/plan/login-host.js +16 -16
- package/dist/plan/platform-org-ports.d.ts +1 -0
- package/dist/plan/platform-org-ports.js +3 -0
- package/dist/plan/platform-providers.d.ts +3 -2
- package/dist/plan/platform-providers.js +29 -11
- package/dist/plan/providers-readers.d.ts +34 -11
- package/dist/plan/providers-readers.js +72 -15
- package/package.json +1 -1
|
@@ -37,8 +37,17 @@ export interface BootstrapOwner {
|
|
|
37
37
|
*/
|
|
38
38
|
export declare function ownerIdentity(owner: BootstrapOwner): OwnerIdentity;
|
|
39
39
|
/**
|
|
40
|
-
* Company Included's declared org: the platform's owner,
|
|
41
|
-
*
|
|
40
|
+
* Company Included's declared org: the platform's owner, tier 3 on the
|
|
41
|
+
* platform's own apex from the start (#512) -- it runs on that domain with
|
|
42
|
+
* hosted mail, not a level-1 org whose domain gets set by hand afterward.
|
|
43
|
+
* `issuer`/`calls`/`site` go on the domain too, the same tier-3 defaults
|
|
44
|
+
* archon#388 gives every company declared there. `calls` and `site` stay
|
|
45
|
+
* disabled (calls is not in v1; no site is declared), so `usesDomain` is
|
|
46
|
+
* inert on both until one is switched on. Chat stays off the domain: its
|
|
47
|
+
* re-key onto a per-org homeserver is not built yet (#463). The org's own
|
|
48
|
+
* sign-in still never collides with the shared ZITADEL instance's host --
|
|
49
|
+
* `loginHostnameFor` (#488) falls back to `<slug>-auth.oikos.host` whenever
|
|
50
|
+
* `auth.<domain>` would equal the platform's own auth hostname.
|
|
42
51
|
*/
|
|
43
52
|
export declare function declaredCompanyOrg(company: BootstrapCompany, owner: BootstrapOwner): DeclaredOrg;
|
|
44
53
|
/**
|
|
@@ -55,25 +55,34 @@ export function ownerIdentity(owner) {
|
|
|
55
55
|
return identity;
|
|
56
56
|
}
|
|
57
57
|
/**
|
|
58
|
-
* Company Included's declared org: the platform's owner,
|
|
59
|
-
*
|
|
58
|
+
* Company Included's declared org: the platform's owner, tier 3 on the
|
|
59
|
+
* platform's own apex from the start (#512) -- it runs on that domain with
|
|
60
|
+
* hosted mail, not a level-1 org whose domain gets set by hand afterward.
|
|
61
|
+
* `issuer`/`calls`/`site` go on the domain too, the same tier-3 defaults
|
|
62
|
+
* archon#388 gives every company declared there. `calls` and `site` stay
|
|
63
|
+
* disabled (calls is not in v1; no site is declared), so `usesDomain` is
|
|
64
|
+
* inert on both until one is switched on. Chat stays off the domain: its
|
|
65
|
+
* re-key onto a per-org homeserver is not built yet (#463). The org's own
|
|
66
|
+
* sign-in still never collides with the shared ZITADEL instance's host --
|
|
67
|
+
* `loginHostnameFor` (#488) falls back to `<slug>-auth.oikos.host` whenever
|
|
68
|
+
* `auth.<domain>` would equal the platform's own auth hostname.
|
|
60
69
|
*/
|
|
61
70
|
export function declaredCompanyOrg(company, owner) {
|
|
62
71
|
return {
|
|
63
72
|
id: COMPANY_INCLUDED_ORG_ID,
|
|
64
73
|
slug: company.slug,
|
|
65
74
|
displayName: company.name,
|
|
66
|
-
domain:
|
|
75
|
+
domain: company.apex,
|
|
67
76
|
mail: {
|
|
68
|
-
mode: '
|
|
69
|
-
usesDomain:
|
|
77
|
+
mode: 'hosted',
|
|
78
|
+
usesDomain: true,
|
|
70
79
|
ownerMailboxAddress: null,
|
|
71
80
|
noReplyLocalPart: 'no-reply',
|
|
72
81
|
},
|
|
73
82
|
chat: { enabled: true, usesDomain: false },
|
|
74
|
-
calls: { enabled: false, usesDomain:
|
|
75
|
-
issuer: { usesDomain:
|
|
76
|
-
site: { enabled: false, usesDomain:
|
|
83
|
+
calls: { enabled: false, usesDomain: true },
|
|
84
|
+
issuer: { usesDomain: true },
|
|
85
|
+
site: { enabled: false, usesDomain: true },
|
|
77
86
|
hosting: {
|
|
78
87
|
enabled: false,
|
|
79
88
|
usesDomain: false,
|
package/dist/index.d.ts
CHANGED
|
@@ -28,6 +28,8 @@ export { PlatformOrgBlockedError, PlatformOrgPorts } from './plan/index.js';
|
|
|
28
28
|
export type { CoolifyPort, CloudflarePort, ZitadelPort, CallsSfuPort, JobsSchedulerPort, FilesOrganisationPort, AiOrganisationPort, FoundryStepExecutorPorts, PlatformOrgPortSource, AgoraHostPort, AgoraHostValues, LoginHostBinding, LoginHostBindingsPort, } from './plan/index.js';
|
|
29
29
|
export { FoundryStepExecutor, NotBuiltError, healthUrls, loginHostnameFor } from './plan/index.js';
|
|
30
30
|
export { HttpJobsSchedulerClient } from './plan/index.js';
|
|
31
|
+
export type { ScopeResolver, RecordedIds } from './plan/index.js';
|
|
32
|
+
export { ProvidersCloudflareReader, ProvidersCmsReader, ProvidersCoolifyReader, ProvidersZitadelReader, storeScopeResolver, storeRecordedIds, } from './plan/index.js';
|
|
31
33
|
export type { ZitadelListAppsRawApp } from './plan/index.js';
|
|
32
34
|
export { adaptZitadelListApps } from './plan/index.js';
|
|
33
35
|
export type { MailIdentityApp, MailIdentityAudienceCheck } from './plan/index.js';
|
package/dist/index.js
CHANGED
|
@@ -17,6 +17,7 @@ export { DEFAULT_ORG_PLATFORM_READINESS, ORG_PLATFORM_KEY_REFS, agoraHostValuesF
|
|
|
17
17
|
export { PlatformOrgBlockedError, PlatformOrgPorts } from './plan/index.js';
|
|
18
18
|
export { FoundryStepExecutor, NotBuiltError, healthUrls, loginHostnameFor } from './plan/index.js';
|
|
19
19
|
export { HttpJobsSchedulerClient } from './plan/index.js';
|
|
20
|
+
export { ProvidersCloudflareReader, ProvidersCmsReader, ProvidersCoolifyReader, ProvidersZitadelReader, storeScopeResolver, storeRecordedIds, } from './plan/index.js';
|
|
20
21
|
export { adaptZitadelListApps } from './plan/index.js';
|
|
21
22
|
export { checkMailIdentityAudience } from './plan/index.js';
|
|
22
23
|
export { checkMailAppsAllowlist } from './plan/index.js';
|
package/dist/plan/agora.d.ts
CHANGED
|
@@ -110,6 +110,18 @@ export declare function agoraHostMissing(host: AgoraHostValues): string[];
|
|
|
110
110
|
export declare function agoraHostnameFor(org: DeclaredOrg): string;
|
|
111
111
|
/** The port `calls/packages/service/Dockerfile` documents (`EXPOSE 8080`); `config.mjs`'s `PORT` has no default, so the executor pushes it. */
|
|
112
112
|
export declare const CALLS_SERVICE_PORT = "8080";
|
|
113
|
+
/**
|
|
114
|
+
* The port `wtfalch/agora`'s `apps/agora/Dockerfile` serves Caddy on
|
|
115
|
+
* (`EXPOSE 8080`). Coolify's github-app build never reads a Dockerfile's
|
|
116
|
+
* `EXPOSE` on its own, so without this passed as `ports_exposes` Traefik is
|
|
117
|
+
* left routing to a port nothing listens on and Agora answers 502 behind the
|
|
118
|
+
* tunnel (#506, seen live on Company Included, 2026-10-05). `executor.ts`'s
|
|
119
|
+
* `coolifyAgora` sets it at create time and fixes it on a found app whose
|
|
120
|
+
* live value differs; `diff.ts`'s `planAgora` plans that fix the same way it
|
|
121
|
+
* plans an env difference, by comparing this constant against the live
|
|
122
|
+
* app's own `ports_exposes` (`providers-readers.ts`'s `withAgoraPortConfig`).
|
|
123
|
+
*/
|
|
124
|
+
export declare const AGORA_APP_PORTS_EXPOSES = "8080";
|
|
113
125
|
/**
|
|
114
126
|
* Recorded by the executor once the calls service app carries its fixed
|
|
115
127
|
* internal name (`callsServiceInternalName`). `desiredAgoraEnv` waits for it,
|
|
@@ -139,6 +151,17 @@ export declare const CALLS_SERVICE_TOKEN_ENV_SCOPE_KEY = "coolify_calls_service_
|
|
|
139
151
|
*/
|
|
140
152
|
export declare const CALLS_INTROSPECTION_SCOPE_KEY = "zitadel_calls_introspection_app";
|
|
141
153
|
export declare const CALLS_INTROSPECTION_UNDELIVERED_SCOPE_KEY = "zitadel_calls_introspection_undelivered";
|
|
154
|
+
/**
|
|
155
|
+
* The introspection API app minted for the org's chat service (once), in the
|
|
156
|
+
* messaging project, and the undelivered key recorded before its secret
|
|
157
|
+
* leaves the executor (platform rule 1). The first doubles as the marker
|
|
158
|
+
* `planChat` reads: recorded after the service's env carries the client. No
|
|
159
|
+
* `:` in either.
|
|
160
|
+
*/
|
|
161
|
+
export declare const CHAT_INTROSPECTION_SCOPE_KEY = "zitadel_chat_introspection_app";
|
|
162
|
+
export declare const CHAT_INTROSPECTION_UNDELIVERED_SCOPE_KEY = "zitadel_chat_introspection_undelivered";
|
|
163
|
+
/** The chat service's introspection API app name; one per org, exact match only. */
|
|
164
|
+
export declare function chatIntrospectionAppName(org: DeclaredOrg): string;
|
|
142
165
|
/** What the plan hands the calls service step: the two host values the token env needs. */
|
|
143
166
|
export interface CallsTokenHost {
|
|
144
167
|
issuer: string;
|
|
@@ -196,6 +219,19 @@ export declare function agoraEnvDrift(desired: Readonly<Record<string, string>>,
|
|
|
196
219
|
* against (#442's own probe: "one env write, zero restarts, zero deploys").
|
|
197
220
|
*/
|
|
198
221
|
export declare const AGORA_ACTIVATED_ENV_SCOPE_KEY = "coolify_agora_activated_env";
|
|
222
|
+
/**
|
|
223
|
+
* #504: recorded once `coolifyAgora` has actually called `deploy` on this
|
|
224
|
+
* org's Agora app -- `createGithubAppApplication` never sets
|
|
225
|
+
* `instant_deploy`, and Coolify defaults it to false, so a freshly created
|
|
226
|
+
* app (or one found by name that a previous apply created but crashed
|
|
227
|
+
* before deploying) sat at `exited:unhealthy` with zero deployments
|
|
228
|
+
* forever: nothing ever deployed it, and `restart` cannot start a container
|
|
229
|
+
* that was never built. Absent means "never deployed", regardless of
|
|
230
|
+
* whether the app is found live this run or created fresh -- unlike
|
|
231
|
+
* {@link AGORA_ACTIVATED_ENV_SCOPE_KEY}, this never changes value once set,
|
|
232
|
+
* so a later env change restarts rather than deploys again.
|
|
233
|
+
*/
|
|
234
|
+
export declare const AGORA_DEPLOYED_SCOPE_KEY = "coolify_agora_deployed";
|
|
199
235
|
/**
|
|
200
236
|
* A stable digest of `env`, for comparison against what was last recorded
|
|
201
237
|
* under {@link AGORA_ACTIVATED_ENV_SCOPE_KEY}. `AGORA_CONFIG` is compared by
|
package/dist/plan/agora.js
CHANGED
|
@@ -118,6 +118,18 @@ export function agoraHostnameFor(org) {
|
|
|
118
118
|
}
|
|
119
119
|
/** The port `calls/packages/service/Dockerfile` documents (`EXPOSE 8080`); `config.mjs`'s `PORT` has no default, so the executor pushes it. */
|
|
120
120
|
export const CALLS_SERVICE_PORT = '8080';
|
|
121
|
+
/**
|
|
122
|
+
* The port `wtfalch/agora`'s `apps/agora/Dockerfile` serves Caddy on
|
|
123
|
+
* (`EXPOSE 8080`). Coolify's github-app build never reads a Dockerfile's
|
|
124
|
+
* `EXPOSE` on its own, so without this passed as `ports_exposes` Traefik is
|
|
125
|
+
* left routing to a port nothing listens on and Agora answers 502 behind the
|
|
126
|
+
* tunnel (#506, seen live on Company Included, 2026-10-05). `executor.ts`'s
|
|
127
|
+
* `coolifyAgora` sets it at create time and fixes it on a found app whose
|
|
128
|
+
* live value differs; `diff.ts`'s `planAgora` plans that fix the same way it
|
|
129
|
+
* plans an env difference, by comparing this constant against the live
|
|
130
|
+
* app's own `ports_exposes` (`providers-readers.ts`'s `withAgoraPortConfig`).
|
|
131
|
+
*/
|
|
132
|
+
export const AGORA_APP_PORTS_EXPOSES = '8080';
|
|
121
133
|
/**
|
|
122
134
|
* Recorded by the executor once the calls service app carries its fixed
|
|
123
135
|
* internal name (`callsServiceInternalName`). `desiredAgoraEnv` waits for it,
|
|
@@ -151,6 +163,19 @@ export const CALLS_SERVICE_TOKEN_ENV_SCOPE_KEY = 'coolify_calls_service_token_en
|
|
|
151
163
|
*/
|
|
152
164
|
export const CALLS_INTROSPECTION_SCOPE_KEY = 'zitadel_calls_introspection_app';
|
|
153
165
|
export const CALLS_INTROSPECTION_UNDELIVERED_SCOPE_KEY = 'zitadel_calls_introspection_undelivered';
|
|
166
|
+
/**
|
|
167
|
+
* The introspection API app minted for the org's chat service (once), in the
|
|
168
|
+
* messaging project, and the undelivered key recorded before its secret
|
|
169
|
+
* leaves the executor (platform rule 1). The first doubles as the marker
|
|
170
|
+
* `planChat` reads: recorded after the service's env carries the client. No
|
|
171
|
+
* `:` in either.
|
|
172
|
+
*/
|
|
173
|
+
export const CHAT_INTROSPECTION_SCOPE_KEY = 'zitadel_chat_introspection_app';
|
|
174
|
+
export const CHAT_INTROSPECTION_UNDELIVERED_SCOPE_KEY = 'zitadel_chat_introspection_undelivered';
|
|
175
|
+
/** The chat service's introspection API app name; one per org, exact match only. */
|
|
176
|
+
export function chatIntrospectionAppName(org) {
|
|
177
|
+
return `chat-introspection-${org.slug}`;
|
|
178
|
+
}
|
|
154
179
|
/**
|
|
155
180
|
* The token env's inputs, or null while any is missing: the host's issuer and
|
|
156
181
|
* messaging project id (`AgoraHostValues`) and the org's recorded ZITADEL org
|
|
@@ -305,6 +330,19 @@ export function agoraEnvDrift(desired, live) {
|
|
|
305
330
|
* against (#442's own probe: "one env write, zero restarts, zero deploys").
|
|
306
331
|
*/
|
|
307
332
|
export const AGORA_ACTIVATED_ENV_SCOPE_KEY = 'coolify_agora_activated_env';
|
|
333
|
+
/**
|
|
334
|
+
* #504: recorded once `coolifyAgora` has actually called `deploy` on this
|
|
335
|
+
* org's Agora app -- `createGithubAppApplication` never sets
|
|
336
|
+
* `instant_deploy`, and Coolify defaults it to false, so a freshly created
|
|
337
|
+
* app (or one found by name that a previous apply created but crashed
|
|
338
|
+
* before deploying) sat at `exited:unhealthy` with zero deployments
|
|
339
|
+
* forever: nothing ever deployed it, and `restart` cannot start a container
|
|
340
|
+
* that was never built. Absent means "never deployed", regardless of
|
|
341
|
+
* whether the app is found live this run or created fresh -- unlike
|
|
342
|
+
* {@link AGORA_ACTIVATED_ENV_SCOPE_KEY}, this never changes value once set,
|
|
343
|
+
* so a later env change restarts rather than deploys again.
|
|
344
|
+
*/
|
|
345
|
+
export const AGORA_DEPLOYED_SCOPE_KEY = 'coolify_agora_deployed';
|
|
308
346
|
/**
|
|
309
347
|
* A stable digest of `env`, for comparison against what was last recorded
|
|
310
348
|
* under {@link AGORA_ACTIVATED_ENV_SCOPE_KEY}. `AGORA_CONFIG` is compared by
|
package/dist/plan/boule.d.ts
CHANGED
|
@@ -20,6 +20,14 @@ export declare const BOULE_CLIENT_ID_SCOPE_KEY = "boule_client_id";
|
|
|
20
20
|
* scope id, like the cms and calls databases.
|
|
21
21
|
*/
|
|
22
22
|
export declare const BOULE_DATABASE_SCOPE_KEY = "coolify_boule_database";
|
|
23
|
+
/**
|
|
24
|
+
* #504: the same marker `agora.ts`'s `AGORA_DEPLOYED_SCOPE_KEY` is, for
|
|
25
|
+
* Boule -- recorded once `coolifyBoule` has actually called `deploy` on this
|
|
26
|
+
* org's Boule app. See that constant's own doc comment for why: Coolify
|
|
27
|
+
* defaults `instant_deploy` to false, so without this a freshly created app
|
|
28
|
+
* sat built but never started, and `restart` cannot fix that.
|
|
29
|
+
*/
|
|
30
|
+
export declare const BOULE_DEPLOYED_SCOPE_KEY = "coolify_boule_deployed";
|
|
23
31
|
/**
|
|
24
32
|
* `boule.<domain>` when the org has a domain, `<slug>-boule.oikos.host`
|
|
25
33
|
* otherwise (the tier rule of boule#128 and foundry#147; `../slug.ts`'s
|
package/dist/plan/boule.js
CHANGED
|
@@ -19,6 +19,14 @@ export const BOULE_CLIENT_ID_SCOPE_KEY = 'boule_client_id';
|
|
|
19
19
|
* scope id, like the cms and calls databases.
|
|
20
20
|
*/
|
|
21
21
|
export const BOULE_DATABASE_SCOPE_KEY = 'coolify_boule_database';
|
|
22
|
+
/**
|
|
23
|
+
* #504: the same marker `agora.ts`'s `AGORA_DEPLOYED_SCOPE_KEY` is, for
|
|
24
|
+
* Boule -- recorded once `coolifyBoule` has actually called `deploy` on this
|
|
25
|
+
* org's Boule app. See that constant's own doc comment for why: Coolify
|
|
26
|
+
* defaults `instant_deploy` to false, so without this a freshly created app
|
|
27
|
+
* sat built but never started, and `restart` cannot fix that.
|
|
28
|
+
*/
|
|
29
|
+
export const BOULE_DEPLOYED_SCOPE_KEY = 'coolify_boule_deployed';
|
|
22
30
|
/**
|
|
23
31
|
* `boule.<domain>` when the org has a domain, `<slug>-boule.oikos.host`
|
|
24
32
|
* otherwise (the tier rule of boule#128 and foundry#147; `../slug.ts`'s
|
|
@@ -90,6 +90,9 @@ export interface ChatServiceEnvInput {
|
|
|
90
90
|
agoraHost: string;
|
|
91
91
|
masClientId: string;
|
|
92
92
|
masClientSecret: string;
|
|
93
|
+
/** The ZITADEL API app the service introspects Agora's opaque tokens as. */
|
|
94
|
+
introspectionClientId: string;
|
|
95
|
+
introspectionClientSecret: string;
|
|
93
96
|
}
|
|
94
97
|
/** Every variable `chat/apps/chat-service/src/config.ts` requires, plus the internal Synapse address and port. */
|
|
95
98
|
export declare function chatServiceEnvs(input: ChatServiceEnvInput): Array<{
|
package/dist/plan/chat-matrix.js
CHANGED
|
@@ -195,6 +195,8 @@ export function chatServiceEnvs(input) {
|
|
|
195
195
|
{ key: 'MAS_URL', value: upstreams.mas },
|
|
196
196
|
{ key: 'MAS_CLIENT_ID', value: input.masClientId },
|
|
197
197
|
{ key: 'MAS_CLIENT_SECRET', value: input.masClientSecret },
|
|
198
|
+
{ key: 'CHAT_INTROSPECTION_CLIENT_ID', value: input.introspectionClientId },
|
|
199
|
+
{ key: 'CHAT_INTROSPECTION_CLIENT_SECRET', value: input.introspectionClientSecret },
|
|
198
200
|
// DATABASE_URL_OWNER/DATABASE_URL are pushed by executor.ts's
|
|
199
201
|
// pushDatabaseUrl instead, not here (the same split coolifyApp and
|
|
200
202
|
// cmsSite follow, #239) -- that call mints a distinct <dbName>_rt
|
package/dist/plan/diff.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { VERSIONS, floatingTagBlocked } from '../bootstrap/versions.js';
|
|
2
2
|
import { APP_ENTRY_KEY, ownerMailboxRecorded, ownerMailboxes } from '../org.js';
|
|
3
|
-
import { AGORA_ACTIVATED_ENV_SCOPE_KEY, CALLS_INTROSPECTION_SCOPE_KEY, CALLS_INTROSPECTION_UNDELIVERED_SCOPE_KEY, NO_AGORA_HOST_VALUES, ZITADEL_ORG_SCOPE_KEY, agoraEnvDrift, agoraEnvRevision, agoraHostMissing, agoraHostnameFor, callsServiceHasName, callsServiceHasTokenEnv, callsTokenEnvMovedMessage, callsTokenHostFrom, desiredAgoraEnv, withRecordedMessagingProject, } from './agora.js';
|
|
3
|
+
import { AGORA_ACTIVATED_ENV_SCOPE_KEY, AGORA_APP_PORTS_EXPOSES, CALLS_INTROSPECTION_SCOPE_KEY, CALLS_INTROSPECTION_UNDELIVERED_SCOPE_KEY, CHAT_INTROSPECTION_SCOPE_KEY, NO_AGORA_HOST_VALUES, ZITADEL_ORG_SCOPE_KEY, agoraEnvDrift, agoraEnvRevision, agoraHostMissing, agoraHostnameFor, callsServiceHasName, callsServiceHasTokenEnv, callsTokenEnvMovedMessage, callsTokenHostFrom, desiredAgoraEnv, withRecordedMessagingProject, } from './agora.js';
|
|
4
4
|
import { BOULE_BROKER_ENTRY_KEY, bouleEnvDrift, desiredBouleEnv, wantedBouleBrokerEntry, } from './boule.js';
|
|
5
5
|
import { brokerEntryAppliedScopeKey, brokerEntryWarningsScopeKey, entryAuthEnv, entryAuthEnvAppliedScopeKey, wantedBrokerEntry, } from './broker-entries.js';
|
|
6
6
|
import { planEntryAiKeys } from './entry-ai-key.js';
|
|
@@ -82,7 +82,11 @@ export function computePlanDetail(org, live, recordedIds = {}, agoraHost = NO_AG
|
|
|
82
82
|
const byKey = new Map(live.map((r) => [resourceKey(r.system, r.kind, r.key), r]));
|
|
83
83
|
const claimed = new Set();
|
|
84
84
|
const plannedSteps = [];
|
|
85
|
-
|
|
85
|
+
// The platform's own org: its domain is the platform apex (the same fact
|
|
86
|
+
// #491 reads for mail). It signs in on the shared instance's own host, so
|
|
87
|
+
// `issuer.usesDomain` never moves it.
|
|
88
|
+
const isPlatformOrg = isPlatformOwnOrg(org, platformReadiness.platformApex);
|
|
89
|
+
const issuer = planIssuer(org, byKey, claimed, platformReadiness.authReady, isPlatformOrg);
|
|
86
90
|
if (issuer.step)
|
|
87
91
|
plannedSteps.push(issuer.step);
|
|
88
92
|
plannedSteps.push(...planOwners(org, recordedIds, platformReadiness.authReady, 'error' in agoraHost ? null : agoraHost.issuer));
|
|
@@ -94,7 +98,7 @@ export function computePlanDetail(org, live, recordedIds = {}, agoraHost = NO_AG
|
|
|
94
98
|
// URL (`https://<platform.auth.hostname>`), the same value every org's
|
|
95
99
|
// sign-in already reads through `planOwners` above -- no separate port
|
|
96
100
|
// or plumbing needed to know the platform's own auth hostname.
|
|
97
|
-
platformAuthHostnameFrom('error' in agoraHost ? null : agoraHost.issuer));
|
|
101
|
+
platformAuthHostnameFrom('error' in agoraHost ? null : agoraHost.issuer), isPlatformOrg);
|
|
98
102
|
if (loginHost.step)
|
|
99
103
|
plannedSteps.push(loginHost.step);
|
|
100
104
|
// Planned ahead of everything that needs the zone live (#238); its own step
|
|
@@ -391,7 +395,7 @@ export function computePlanDetail(org, live, recordedIds = {}, agoraHost = NO_AG
|
|
|
391
395
|
* already exists live is unaffected: its own domain-move logic runs
|
|
392
396
|
* regardless, since that never re-provisions the ZITADEL org itself.
|
|
393
397
|
*/
|
|
394
|
-
function planIssuer(org, byKey, claimed, authReady) {
|
|
398
|
+
function planIssuer(org, byKey, claimed, authReady, isPlatformOrg = false) {
|
|
395
399
|
const key = resourceKey('zitadel', 'issuer', org.id);
|
|
396
400
|
const live = byKey.get(key);
|
|
397
401
|
const desiredUsesDomain = org.issuer.usesDomain;
|
|
@@ -420,9 +424,14 @@ function planIssuer(org, byKey, claimed, authReady) {
|
|
|
420
424
|
};
|
|
421
425
|
}
|
|
422
426
|
claimed.add(key);
|
|
423
|
-
|
|
424
|
-
|
|
427
|
+
// The platform's own org already signs in on the shared host: no move. A
|
|
428
|
+
// live issuer that does not say (the reader never does: foundry v1 never
|
|
429
|
+
// moves one) is not read as a different one either.
|
|
430
|
+
if (isPlatformOrg ||
|
|
431
|
+
live.config.usesDomain === undefined ||
|
|
432
|
+
Boolean(live.config.usesDomain) === desiredUsesDomain) {
|
|
425
433
|
return { step: null, blocked: null };
|
|
434
|
+
}
|
|
426
435
|
const domain = org.domain ?? 'the org domain';
|
|
427
436
|
return {
|
|
428
437
|
step: {
|
|
@@ -519,6 +528,9 @@ function planOwners(org, recordedIds, authReady, authIssuer) {
|
|
|
519
528
|
* computed `auth.<domain>` against, so the platform's own org never plans
|
|
520
529
|
* its login host equal to the shared ZITADEL instance's own.
|
|
521
530
|
*/
|
|
531
|
+
function isPlatformOwnOrg(org, platformApex) {
|
|
532
|
+
return Boolean(org.domain && platformApex && org.domain.toLowerCase() === platformApex.toLowerCase());
|
|
533
|
+
}
|
|
522
534
|
function platformAuthHostnameFrom(issuer) {
|
|
523
535
|
if (!issuer)
|
|
524
536
|
return null;
|
|
@@ -565,16 +577,58 @@ function platformAuthHostnameFrom(issuer) {
|
|
|
565
577
|
* platform's own domain, never plans the shared ZITADEL instance's own login
|
|
566
578
|
* host as its own -- `auth.companyincluded.com` colliding with itself.
|
|
567
579
|
*/
|
|
568
|
-
function planLoginHost(org, recordedIds, authReady, issuerPlanned, bindingsWired, platformAuthHostname) {
|
|
580
|
+
function planLoginHost(org, recordedIds, authReady, issuerPlanned, bindingsWired, platformAuthHostname, isPlatformOrg) {
|
|
569
581
|
const none = { step: null, blocked: null };
|
|
570
582
|
const key = resourceKey('zitadel', 'login-host', org.id);
|
|
571
|
-
|
|
583
|
+
// The platform's own org signs in on the shared instance's host whatever
|
|
584
|
+
// `issuer.usesDomain` says (turning it on would move the issuer).
|
|
585
|
+
// Before that host is known, plan nothing: a guessed host would delete the
|
|
586
|
+
// working one, and a normal step would trust a domain `destroy` later deletes.
|
|
587
|
+
if (isPlatformOrg && platformAuthHostname === null) {
|
|
588
|
+
return {
|
|
589
|
+
step: null,
|
|
590
|
+
blocked: `${org.slug}'s login host is waiting for the platform auth host`,
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
const sharedInstanceHost = isPlatformOrg && platformAuthHostname !== null;
|
|
594
|
+
const host = sharedInstanceHost ? platformAuthHostname : loginHostnameFor(org);
|
|
572
595
|
// #488: the guard in `loginHostnameFor` fired -- the org's own domain host
|
|
573
596
|
// would have been the platform's shared ZITADEL instance host.
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
host
|
|
597
|
+
if (sharedInstanceHost) {
|
|
598
|
+
// The host is the shared instance's own: nothing to trust or bind, and an
|
|
599
|
+
// org with no recorded host already signs in there. Only an org recorded
|
|
600
|
+
// on another host (the old `<slug>-auth.oikos.host`) plans one step, to
|
|
601
|
+
// move its apps over and remove the old host; then it plans nothing.
|
|
602
|
+
const recordedHost = recordedIds[key];
|
|
603
|
+
const moving = recordedHost !== undefined && recordedHost !== host;
|
|
604
|
+
const removalOwed = pendingRemovalHosts(recordedIds).length > 0;
|
|
605
|
+
if (!authReady || !(moving || removalOwed))
|
|
606
|
+
return none;
|
|
607
|
+
// The same gates as the normal branch: the executor's shared step also
|
|
608
|
+
// needs the binding port and the ZITADEL org.
|
|
609
|
+
const waiting = loginHostWaiting(org, host, recordedIds, bindingsWired, issuerPlanned);
|
|
610
|
+
return waiting ?? stepFor(true);
|
|
611
|
+
}
|
|
612
|
+
function stepFor(shared) {
|
|
613
|
+
return {
|
|
614
|
+
step: {
|
|
615
|
+
action: {
|
|
616
|
+
system: 'zitadel',
|
|
617
|
+
kind: 'login-host',
|
|
618
|
+
key: org.id,
|
|
619
|
+
desired: shared ? { host, shared: true } : { host },
|
|
620
|
+
},
|
|
621
|
+
step: {
|
|
622
|
+
description: shared
|
|
623
|
+
? `move ${org.slug}'s apps onto the platform's own sign-in host ${host}`
|
|
624
|
+
: `set ${org.slug}'s login host to ${host}`,
|
|
625
|
+
destroys: false,
|
|
626
|
+
migrates: false,
|
|
627
|
+
},
|
|
628
|
+
},
|
|
629
|
+
blocked: null,
|
|
630
|
+
};
|
|
631
|
+
}
|
|
578
632
|
// Already applied -- a re-run plans nothing unless the host changed, an app
|
|
579
633
|
// client id appeared since the binding was last written (#267, rule 4), or
|
|
580
634
|
// an earlier host change still has a removal owed (#274 fix round 2):
|
|
@@ -595,6 +649,10 @@ function planLoginHost(org, recordedIds, authReady, issuerPlanned, bindingsWired
|
|
|
595
649
|
// Isolation between orgs rests on zitadel-login's binding, and repointing
|
|
596
650
|
// an app at a host with no binding breaks its sign-in: trust nothing and
|
|
597
651
|
// repoint nothing until the port that writes the binding is wired.
|
|
652
|
+
return loginHostWaiting(org, host, recordedIds, bindingsWired, issuerPlanned) ?? stepFor(false);
|
|
653
|
+
}
|
|
654
|
+
/** Why a login-host step cannot run yet, or `null` when nothing is missing. */
|
|
655
|
+
function loginHostWaiting(org, host, recordedIds, bindingsWired, issuerPlanned) {
|
|
598
656
|
if (!bindingsWired) {
|
|
599
657
|
return {
|
|
600
658
|
step: null,
|
|
@@ -609,24 +667,7 @@ function planLoginHost(org, recordedIds, authReady, issuerPlanned, bindingsWired
|
|
|
609
667
|
blocked: `${org.slug}'s login host ${host} waits for its ZITADEL org, which is not recorded yet`,
|
|
610
668
|
};
|
|
611
669
|
}
|
|
612
|
-
return
|
|
613
|
-
step: {
|
|
614
|
-
action: {
|
|
615
|
-
system: 'zitadel',
|
|
616
|
-
kind: 'login-host',
|
|
617
|
-
key: org.id,
|
|
618
|
-
desired: { host },
|
|
619
|
-
},
|
|
620
|
-
step: {
|
|
621
|
-
description: sharedInstanceHost
|
|
622
|
-
? `set ${org.slug}'s login host to ${host}: its domain host (auth.${org.domain}) is the platform's own shared ZITADEL instance host, so it stays on oikos.host`
|
|
623
|
-
: `set ${org.slug}'s login host to ${host}`,
|
|
624
|
-
destroys: false,
|
|
625
|
-
migrates: false,
|
|
626
|
-
},
|
|
627
|
-
},
|
|
628
|
-
blocked: null,
|
|
629
|
-
};
|
|
670
|
+
return null;
|
|
630
671
|
}
|
|
631
672
|
/** #230: restated from `executor.ts`'s `MAIL_ROLE_MAILBOXES_SCOPE_KEY`, same rule: comma-joined local parts already created. */
|
|
632
673
|
const MAIL_ROLE_MAILBOXES_SCOPE_KEY = 'mail_role_mailboxes';
|
|
@@ -923,6 +964,27 @@ function planChat(org, byKey, claimed, recordedIds, reading, issuerPlanned) {
|
|
|
923
964
|
blocked: null,
|
|
924
965
|
};
|
|
925
966
|
}
|
|
967
|
+
// A live chat made before its service had an introspection client
|
|
968
|
+
// answers every Agora sign-in with `token_invalid`. The marker is the
|
|
969
|
+
// client's app id, recorded after the service's env carries it.
|
|
970
|
+
if (!recordedIds[CHAT_INTROSPECTION_SCOPE_KEY]) {
|
|
971
|
+
return {
|
|
972
|
+
step: {
|
|
973
|
+
action: {
|
|
974
|
+
system: 'coolify',
|
|
975
|
+
kind: 'chat',
|
|
976
|
+
key: org.id,
|
|
977
|
+
desired: { usesDomain: liveUsesDomain, refreshIntrospection: true },
|
|
978
|
+
},
|
|
979
|
+
step: {
|
|
980
|
+
description: `give ${org.slug}'s chat service its token introspection client and redeploy it`,
|
|
981
|
+
destroys: false,
|
|
982
|
+
migrates: false,
|
|
983
|
+
},
|
|
984
|
+
},
|
|
985
|
+
blocked: null,
|
|
986
|
+
};
|
|
987
|
+
}
|
|
926
988
|
return none;
|
|
927
989
|
}
|
|
928
990
|
const domain = org.domain ?? 'its domain';
|
|
@@ -1594,6 +1656,13 @@ function planAgora(org, byKey, claimed, recordedIds, reading) {
|
|
|
1594
1656
|
if (liveEnv.AGORA_CONFIG === undefined && !drift.includes('AGORA_CONFIG')) {
|
|
1595
1657
|
drift.push('AGORA_CONFIG');
|
|
1596
1658
|
}
|
|
1659
|
+
// #506: an app Coolify never got `ports_exposes` on (or got the wrong one)
|
|
1660
|
+
// answers 502 behind the tunnel even though its env already matches --
|
|
1661
|
+
// exactly the Company Included incident this issue names. Compared
|
|
1662
|
+
// separately from `drift` above: it is a Coolify application setting, not
|
|
1663
|
+
// an env var, and `providers-readers.ts`'s `withAgoraEnvConfig` reads it
|
|
1664
|
+
// from `getApplication`, never `listEnvs`.
|
|
1665
|
+
const portDrift = live.config.ports_exposes !== AGORA_APP_PORTS_EXPOSES;
|
|
1597
1666
|
// #442: `drift` alone, once this step has ever run, is comparing `env`
|
|
1598
1667
|
// against Coolify's own env store -- which the executor's `bulkUpsertEnvs`
|
|
1599
1668
|
// leaves matching `env` the moment the write succeeds, restarted or not.
|
|
@@ -1604,14 +1673,18 @@ function planAgora(org, byKey, claimed, recordedIds, reading) {
|
|
|
1604
1673
|
// actually serving this exact `env`; while it disagrees, the step stays
|
|
1605
1674
|
// planned no matter what Coolify's env store already shows.
|
|
1606
1675
|
const activated = recordedIds[AGORA_ACTIVATED_ENV_SCOPE_KEY] === agoraEnvRevision(env);
|
|
1607
|
-
if (drift.length === 0 && activated)
|
|
1676
|
+
if (drift.length === 0 && activated && !portDrift)
|
|
1608
1677
|
return { step: null, blocked: null };
|
|
1678
|
+
const reasons = [
|
|
1679
|
+
...(drift.length > 0 ? [`${drift.join(', ')} missing or different`] : []),
|
|
1680
|
+
...(portDrift ? ["ports_exposes wrong -- Traefik can't reach it"] : []),
|
|
1681
|
+
];
|
|
1609
1682
|
return {
|
|
1610
1683
|
step: {
|
|
1611
1684
|
action,
|
|
1612
1685
|
step: {
|
|
1613
|
-
description:
|
|
1614
|
-
? `update ${org.slug}'s Agora config (${
|
|
1686
|
+
description: reasons.length > 0
|
|
1687
|
+
? `update ${org.slug}'s Agora config (${reasons.join('; ')})`
|
|
1615
1688
|
: `activate ${org.slug}'s Agora config -- written but not yet confirmed restarted`,
|
|
1616
1689
|
destroys: false,
|
|
1617
1690
|
migrates: false,
|
|
@@ -1735,13 +1808,13 @@ function planBoule(org, byKey, claimed, recordedIds, issuer, otherSlugs, archonU
|
|
|
1735
1808
|
* park-not-delete rule (#7 corrections 1) -- turning it off never deletes
|
|
1736
1809
|
* the org's own app, database or tunnel configuration, it parks it.
|
|
1737
1810
|
*/
|
|
1738
|
-
function planHostingToggle(system, kind, desiredEnabled, org, byKey, claimed, describeProvision, describePark, stepKey = org.id) {
|
|
1811
|
+
function planHostingToggle(system, kind, desiredEnabled, org, byKey, claimed, describeProvision, describePark, stepKey = org.id, isOn = (live) => live.config.enabled !== false) {
|
|
1739
1812
|
const key = resourceKey(system, kind, stepKey);
|
|
1740
1813
|
const live = byKey.get(key);
|
|
1741
1814
|
// A live resource is on unless it says it is parked. Providers' own
|
|
1742
1815
|
// configs carry no `enabled` field at all, so only a park step's
|
|
1743
1816
|
// explicit `enabled: false` reads as off.
|
|
1744
|
-
const liveEnabled = live !== undefined && live
|
|
1817
|
+
const liveEnabled = live !== undefined && isOn(live);
|
|
1745
1818
|
if (!desiredEnabled) {
|
|
1746
1819
|
// Nothing declared and nothing live: there is nothing to park.
|
|
1747
1820
|
if (!live)
|
|
@@ -2192,7 +2265,18 @@ function planTunnel(org, byKey, claimed, recordedIds) {
|
|
|
2192
2265
|
(org.site.enabled && Boolean(org.domain)) ||
|
|
2193
2266
|
(org.calls.enabled && org.calls.usesDomain && Boolean(org.domain)) ||
|
|
2194
2267
|
Boolean(org.domain);
|
|
2195
|
-
const toggled = planHostingToggle('cloudflare', 'tunnel', desired, org, byKey, claimed, () => `provision a Cloudflare tunnel for ${org.slug}'s app`, () => `park ${org.slug}'s Cloudflare tunnel (configuration is retained, not deleted -- #7 corrections 1)
|
|
2268
|
+
const toggled = planHostingToggle('cloudflare', 'tunnel', desired, org, byKey, claimed, () => `provision a Cloudflare tunnel for ${org.slug}'s app`, () => `park ${org.slug}'s Cloudflare tunnel (configuration is retained, not deleted -- #7 corrections 1)`, org.id,
|
|
2269
|
+
// Cloudflare's status, read against what is declared: a park cannot change
|
|
2270
|
+
// it, so judging on/off by status alone would never let the plan empty.
|
|
2271
|
+
// Wanted on: only `down` needs a repair (`inactive` is a fresh tunnel
|
|
2272
|
+
// whose connector has not dialled in yet). Wanted off: only a connected
|
|
2273
|
+
// tunnel needs parking; `inactive` and `down` are already parked.
|
|
2274
|
+
(live) => {
|
|
2275
|
+
const status = live.config.status;
|
|
2276
|
+
if (typeof status !== 'string')
|
|
2277
|
+
return live.config.enabled !== false;
|
|
2278
|
+
return desired ? status !== 'down' : status === 'healthy' || status === 'degraded';
|
|
2279
|
+
});
|
|
2196
2280
|
if (toggled || !desired)
|
|
2197
2281
|
return toggled;
|
|
2198
2282
|
// A live, enabled tunnel re-plans the same step again when either:
|
package/dist/plan/executor.d.ts
CHANGED
|
@@ -89,6 +89,10 @@ export interface CoolifyPort {
|
|
|
89
89
|
* `settings`, not at the top level -- confirmed against live Coolify
|
|
90
90
|
* 4.3.23 (PR #482 review); `custom_network_aliases` and
|
|
91
91
|
* `health_check_enabled` are both top-level there.
|
|
92
|
+
*
|
|
93
|
+
* #506: also the port Traefik routes to, so `coolifyAgora` can tell an app
|
|
94
|
+
* that predates passing `ports_exposes` at create apart from one already
|
|
95
|
+
* fixed.
|
|
92
96
|
*/
|
|
93
97
|
getApplication(uuid: string): Promise<{
|
|
94
98
|
uuid: string;
|
|
@@ -99,6 +103,7 @@ export interface CoolifyPort {
|
|
|
99
103
|
};
|
|
100
104
|
custom_network_aliases?: string | null;
|
|
101
105
|
health_check_enabled?: boolean;
|
|
106
|
+
ports_exposes?: string;
|
|
102
107
|
}>;
|
|
103
108
|
createGithubAppApplication(input: {
|
|
104
109
|
project_uuid: string;
|
|
@@ -1410,6 +1415,8 @@ export interface PlatformOrgPortSource {
|
|
|
1410
1415
|
files(): Promise<FilesOrganisationPort>;
|
|
1411
1416
|
ai(): Promise<AiOrganisationPort>;
|
|
1412
1417
|
jobs(): Promise<JobsSchedulerPort>;
|
|
1418
|
+
/** The platform's shared sign-in host, or `null` when none is declared. */
|
|
1419
|
+
authHostname?(): Promise<string | null>;
|
|
1413
1420
|
}
|
|
1414
1421
|
export interface FoundryStepExecutorPorts {
|
|
1415
1422
|
coolify: CoolifyPort;
|
|
@@ -2221,6 +2228,18 @@ export declare class FoundryStepExecutor implements StepExecutor, StepDestroyer
|
|
|
2221
2228
|
* so an existing one is always re-written.
|
|
2222
2229
|
*/
|
|
2223
2230
|
private ensureCallsFileStorage;
|
|
2231
|
+
/**
|
|
2232
|
+
* The chat service's introspection client: an API app in the org's
|
|
2233
|
+
* messaging project (the audience Agora's token carries), so ZITADEL's
|
|
2234
|
+
* introspection accepts it. The chat service authenticates as it with
|
|
2235
|
+
* `client_secret_basic`. Minted once, the way {@link callsTokenEnvs} does:
|
|
2236
|
+
* the id is recorded under an undelivered key and persisted before the
|
|
2237
|
+
* secret leaves, the secret goes to `deliverSecret` and to the caller's env
|
|
2238
|
+
* write, never to a log, error or return beyond that. A re-run reads the id
|
|
2239
|
+
* and secret back from the service's own env and mints nothing. `changed`
|
|
2240
|
+
* is true only for a fresh mint.
|
|
2241
|
+
*/
|
|
2242
|
+
private ensureChatIntrospection;
|
|
2224
2243
|
/**
|
|
2225
2244
|
* Creates the `chat.admin` role on the org's messaging project and grants
|
|
2226
2245
|
* it to each owner whose invite is recorded (`zitadel:owner:<orgId>:<email>`,
|
|
@@ -2331,11 +2350,25 @@ export declare class FoundryStepExecutor implements StepExecutor, StepDestroyer
|
|
|
2331
2350
|
* actually differs, or whenever the last restart this step confirmed
|
|
2332
2351
|
* ({@link AGORA_ACTIVATED_ENV_SCOPE_KEY}) is not this exact env (an org
|
|
2333
2352
|
* applied before this existed, whose live env already matched desired
|
|
2334
|
-
* with nothing ever confirmed running it).
|
|
2335
|
-
*
|
|
2336
|
-
*
|
|
2337
|
-
*
|
|
2338
|
-
*
|
|
2353
|
+
* with nothing ever confirmed running it). The activation marker is
|
|
2354
|
+
* recorded last, after the restart (if any) returns without throwing, so a
|
|
2355
|
+
* failed restart leaves it stale and the next plan replans this step even
|
|
2356
|
+
* though Coolify's own env store already matches desired.
|
|
2357
|
+
*
|
|
2358
|
+
* #504: a brand new app is never restarted -- but it is deployed, once
|
|
2359
|
+
* ({@link AGORA_DEPLOYED_SCOPE_KEY}). `createGithubAppApplication` never
|
|
2360
|
+
* sets `instant_deploy`, and Coolify defaults it to false, so without an
|
|
2361
|
+
* explicit deploy the app sat created but never built or started.
|
|
2362
|
+
*
|
|
2363
|
+
* #506: `ports_exposes: AGORA_APP_PORTS_EXPOSES` goes on the create call
|
|
2364
|
+
* itself, so Traefik has a port to route to from the moment the app
|
|
2365
|
+
* exists. An app `findOrCreateCallsApp` instead *found* may predate this
|
|
2366
|
+
* fix and carry no `ports_exposes` at all (or the wrong one), so its live
|
|
2367
|
+
* value is read and, when it differs, patched -- and, since the container
|
|
2368
|
+
* listens on `ports_exposes` only at its next deploy (the same rule
|
|
2369
|
+
* `coolifyApp`'s own `settingsNeedDeploy` follows for a port change
|
|
2370
|
+
* there), redeployed even on a run that already has its one #504 deploy
|
|
2371
|
+
* behind it.
|
|
2339
2372
|
*/
|
|
2340
2373
|
private coolifyAgora;
|
|
2341
2374
|
/**
|
|
@@ -2360,6 +2393,10 @@ export declare class FoundryStepExecutor implements StepExecutor, StepDestroyer
|
|
|
2360
2393
|
*
|
|
2361
2394
|
* `AUTH_APP_KEY` is minted once here; its broker entry is the next step
|
|
2362
2395
|
* (`bouleBrokerEntry`).
|
|
2396
|
+
*
|
|
2397
|
+
* #504: a brand new app is never restarted -- but it is deployed, once
|
|
2398
|
+
* ({@link BOULE_DEPLOYED_SCOPE_KEY}), the same reason and the same marker
|
|
2399
|
+
* shape {@link coolifyAgora}'s own doc comment explains.
|
|
2363
2400
|
*/
|
|
2364
2401
|
private coolifyBoule;
|
|
2365
2402
|
/**
|
|
@@ -2501,6 +2538,15 @@ export declare class FoundryStepExecutor implements StepExecutor, StepDestroyer
|
|
|
2501
2538
|
* shared-hostname collision this ticket exists to refuse. An org re-running
|
|
2502
2539
|
* its own already-recorded zone is unaffected: the store then names this
|
|
2503
2540
|
* same org as the owner.
|
|
2541
|
+
*
|
|
2542
|
+
* #510: also records the zone's own id under the flat `cloudflare_zone`
|
|
2543
|
+
* scope key, alongside the `cloudflare:zone:<domain>` resource id `runSteps`
|
|
2544
|
+
* already records from this method's return value. Without it, nothing
|
|
2545
|
+
* ever gives `ProvidersCloudflareReader` a zone id to resolve this org's
|
|
2546
|
+
* scope with (`providers-readers.ts`'s `storeScopeResolver` doc comment),
|
|
2547
|
+
* so that reader's `listResources` always returned `[]` and `diff.ts`'s
|
|
2548
|
+
* `planZone` re-proposed "provision a Cloudflare zone" on every apply, even
|
|
2549
|
+
* once the zone was live.
|
|
2504
2550
|
*/
|
|
2505
2551
|
private cloudflareZone;
|
|
2506
2552
|
/**
|