@wtfalch/foundry 0.25.0 → 0.26.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.
package/dist/org.js
CHANGED
|
@@ -179,7 +179,7 @@ export function validateHosting(hosting, names) {
|
|
|
179
179
|
const ACCESS_RULE_HOSTNAME = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)+$/;
|
|
180
180
|
/**
|
|
181
181
|
* The rules for one {@link AccessRule}; `null` when it is valid. The hostname
|
|
182
|
-
* must be
|
|
182
|
+
* must be the org's domain or a name under it, and never a locked one;
|
|
183
183
|
* without `names` only the checks that need no domain run.
|
|
184
184
|
*/
|
|
185
185
|
function validateAccessRule(rule, hosting, names) {
|
|
@@ -201,11 +201,11 @@ function validateAccessRule(rule, hosting, names) {
|
|
|
201
201
|
return `${at} is a name foundry owns`;
|
|
202
202
|
}
|
|
203
203
|
const { domain } = names;
|
|
204
|
+
// Any name under the domain: `share.<domain>` and `storage.<domain>` are
|
|
205
|
+
// served for the org by the files service, not by one of its entries.
|
|
204
206
|
const served = hosting.usesDomain &&
|
|
205
207
|
domain !== null &&
|
|
206
|
-
|
|
207
|
-
? rule.hostname.endsWith(`.${domain}`)
|
|
208
|
-
: rule.hostname === (name === '@' ? domain : `${name}.${domain}`)));
|
|
208
|
+
(rule.hostname === domain || rule.hostname.endsWith(`.${domain}`));
|
|
209
209
|
if (!served) {
|
|
210
210
|
return `${at} is not a name the org serves under its own domain`;
|
|
211
211
|
}
|
|
@@ -197,6 +197,21 @@ export interface PlatformStepExecutionContext {
|
|
|
197
197
|
*/
|
|
198
198
|
persist?(): Promise<void>;
|
|
199
199
|
}
|
|
200
|
+
/**
|
|
201
|
+
* The flags {@link PlatformStepExecutor.driftedSteps} returns. Each one is
|
|
202
|
+
* described on `PlanPlatformOptions.drifted` (`platform-diff.ts`).
|
|
203
|
+
*/
|
|
204
|
+
export interface PlatformDriftedSteps {
|
|
205
|
+
archon?: boolean;
|
|
206
|
+
auth?: boolean;
|
|
207
|
+
/** The host sets it. */
|
|
208
|
+
edge?: boolean;
|
|
209
|
+
/** #380 */
|
|
210
|
+
files?: boolean;
|
|
211
|
+
backupStorage?: boolean;
|
|
212
|
+
/** #257 fix round 4 */
|
|
213
|
+
backupStorageBlocked?: string;
|
|
214
|
+
}
|
|
200
215
|
/** {@link StepExecutor}, for the platform (#167) rather than an org. */
|
|
201
216
|
export interface PlatformStepExecutor {
|
|
202
217
|
/**
|
|
@@ -235,17 +250,12 @@ export interface PlatformStepExecutor {
|
|
|
235
250
|
}>;
|
|
236
251
|
/**
|
|
237
252
|
* Steps whose live state differs from the declared one in a way the recorded
|
|
238
|
-
* ids cannot show, read from the live system on each plan.
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
files?: boolean;
|
|
245
|
-
backupStorage?: boolean;
|
|
246
|
-
/** #257 fix round 4: see `PlanPlatformOptions.drifted.backupStorageBlocked` (`platform-diff.ts`). */
|
|
247
|
-
backupStorageBlocked?: string;
|
|
248
|
-
}>;
|
|
253
|
+
* ids cannot show, read from the live system on each plan. The host sets
|
|
254
|
+
* `edge` (foundry's own executor never does). Foundry stores nothing about
|
|
255
|
+
* the token and the edge step does not clear the flag, so the host must stop
|
|
256
|
+
* reporting it once edge holds the new token and the old one is gone.
|
|
257
|
+
*/
|
|
258
|
+
driftedSteps?(platform: DeclaredPlatform, recordedIds: Readonly<Record<string, string>>): Promise<PlatformDriftedSteps>;
|
|
249
259
|
/**
|
|
250
260
|
* Gives the platform files runner a changed share-domain set and touches
|
|
251
261
|
* nothing else. Returns the scope ids to record (only the share-domains
|
|
@@ -1008,11 +1008,17 @@ export interface PlanPlatformOptions {
|
|
|
1008
1008
|
* retried forever. `files` (#380): the runner's live env is missing a
|
|
1009
1009
|
* setting `ensureStarted()` requires -- see `filesRunnerEnvDrifted`
|
|
1010
1010
|
* (`platform-providers.ts`) -- which the recorded hostname/gitRef alone
|
|
1011
|
-
* could never show.
|
|
1011
|
+
* could never show. `edge`: the Cloudflare token edge runs on is not the one
|
|
1012
|
+
* the host now wants (an older scope), so the recorded ids say edge is
|
|
1013
|
+
* applied while it still holds the old token. The host sets it; foundry's
|
|
1014
|
+
* own executor never does. Foundry stores nothing about the token and the
|
|
1015
|
+
* edge step does not clear the flag, so the host must stop reporting it once
|
|
1016
|
+
* edge holds the new token and the old one is gone.
|
|
1012
1017
|
*/
|
|
1013
1018
|
drifted?: {
|
|
1014
1019
|
archon?: boolean;
|
|
1015
1020
|
auth?: boolean;
|
|
1021
|
+
edge?: boolean;
|
|
1016
1022
|
files?: boolean;
|
|
1017
1023
|
backupStorage?: boolean;
|
|
1018
1024
|
backupStorageBlocked?: string;
|
|
@@ -1061,7 +1061,7 @@ export function planPlatform(platform, live, options = {}) {
|
|
|
1061
1061
|
// through it; it needs none of them (a Coolify database and app, its own
|
|
1062
1062
|
// tunnel and DNS record, a held Cloudflare token) and records the address
|
|
1063
1063
|
// they reach it at.
|
|
1064
|
-
const edge = planEdge(platform, recordedIds, blocked.edge);
|
|
1064
|
+
const edge = planEdge(platform, recordedIds, blocked.edge, drifted.edge);
|
|
1065
1065
|
if (edge.step)
|
|
1066
1066
|
plannedSteps.push(edge.step);
|
|
1067
1067
|
// Auth, mail and archon would build their tunnels and then fail at the DNS
|
|
@@ -2108,13 +2108,17 @@ function planFiles(platform, recordedIds, executorBlocked, authReady, drifted) {
|
|
|
2108
2108
|
* Coolify app. A changed hostname or `gitRef` plans the step again (a
|
|
2109
2109
|
* redeploy, no new secret).
|
|
2110
2110
|
*
|
|
2111
|
+
* `drifted` (the host's own `driftedSteps`: edge holds an older Cloudflare
|
|
2112
|
+
* token) plans the same step on an applied edge. The step writes the held
|
|
2113
|
+
* token into edge's env and redeploys it.
|
|
2114
|
+
*
|
|
2111
2115
|
* Blocked, not planned: a platform key minted but never delivered (lost,
|
|
2112
2116
|
* `ServiceError`-free the way {@link planFiles}'s own management-key check
|
|
2113
2117
|
* is), or `executorBlocked` -- the host built no edge dependencies, or
|
|
2114
2118
|
* (CLAUDE.md's "Human steps") no Cloudflare API token is held yet under
|
|
2115
2119
|
* {@link PLATFORM_EDGE_CLOUDFLARE_TOKEN_REF}.
|
|
2116
2120
|
*/
|
|
2117
|
-
function planEdge(platform, recordedIds, executorBlocked) {
|
|
2121
|
+
function planEdge(platform, recordedIds, executorBlocked, drifted) {
|
|
2118
2122
|
if (!platform.edge.enabled)
|
|
2119
2123
|
return { step: null, blocked: null };
|
|
2120
2124
|
const undelivered = recordedIds[PLATFORM_EDGE_PLATFORM_KEY_UNDELIVERED_SCOPE_KEY];
|
|
@@ -2129,7 +2133,7 @@ function planEdge(platform, recordedIds, executorBlocked) {
|
|
|
2129
2133
|
recordedIds[PLATFORM_EDGE_APPLIED_GIT_REF_SCOPE_KEY] === platform.edge.gitRef &&
|
|
2130
2134
|
// F4.dns: an edge applied before its address was recorded runs once more to record it.
|
|
2131
2135
|
Boolean(recordedIds[PLATFORM_EDGE_INTERNAL_URL_SCOPE_KEY]);
|
|
2132
|
-
if (applied)
|
|
2136
|
+
if (applied && !drifted)
|
|
2133
2137
|
return { step: null, blocked: null };
|
|
2134
2138
|
if (executorBlocked)
|
|
2135
2139
|
return { step: null, blocked: executorBlocked };
|
|
@@ -2142,7 +2146,9 @@ function planEdge(platform, recordedIds, executorBlocked) {
|
|
|
2142
2146
|
desired: { hostname: platform.edge.hostname, gitRef: platform.edge.gitRef },
|
|
2143
2147
|
},
|
|
2144
2148
|
step: {
|
|
2145
|
-
description:
|
|
2149
|
+
description: applied
|
|
2150
|
+
? `replace the Cloudflare token edge runs on at ${platform.edge.hostname} and redeploy edge`
|
|
2151
|
+
: `provision the edge host at ${platform.edge.hostname}, with its own database and foundry's platform key`,
|
|
2146
2152
|
destroys: false,
|
|
2147
2153
|
migrates: false,
|
|
2148
2154
|
},
|
|
@@ -9,7 +9,7 @@ import type { DnsPort } from './edge-dns.js';
|
|
|
9
9
|
import type { TunnelPort } from './edge-tunnels.js';
|
|
10
10
|
import type { CloudflarePort, CoolifyPort, FoundryStepExecutorPorts, ZitadelPort } from './executor.js';
|
|
11
11
|
import type { LiveResource, PlatformCloudflareReader, PlatformCoolifyReader } from './live.js';
|
|
12
|
-
import type { PlatformStepExecutionContext, PlatformStepExecutor } from './plan-apply.js';
|
|
12
|
+
import type { PlatformDriftedSteps, PlatformStepExecutionContext, PlatformStepExecutor } from './plan-apply.js';
|
|
13
13
|
import { type BrokerDeployRecord } from './platform-diff.js';
|
|
14
14
|
import { type CallsSfuPlatformDeps } from './platform-sfu.js';
|
|
15
15
|
import { type PlatformUptimeDeps } from './uptime-worker.js';
|
|
@@ -335,13 +335,7 @@ export declare class FoundryPlatformStepExecutor implements PlatformStepExecutor
|
|
|
335
335
|
* login-client token is no longer the sole live PAT on its ZITADEL machine
|
|
336
336
|
* user.
|
|
337
337
|
*/
|
|
338
|
-
driftedSteps(platform: DeclaredPlatform, recordedIds?: Readonly<Record<string, string>>): Promise<
|
|
339
|
-
archon?: boolean;
|
|
340
|
-
auth?: boolean;
|
|
341
|
-
files?: boolean;
|
|
342
|
-
backupStorage?: boolean;
|
|
343
|
-
backupStorageBlocked?: string;
|
|
344
|
-
}>;
|
|
338
|
+
driftedSteps(platform: DeclaredPlatform, recordedIds?: Readonly<Record<string, string>>): Promise<PlatformDriftedSteps>;
|
|
345
339
|
/**
|
|
346
340
|
* Gives the files runner a changed share-domain set and nothing else: its
|
|
347
341
|
* Coolify `domains`, `FILES_PUBLIC_HOSTS` and one redeploy of the runner.
|