@wtfalch/foundry 0.19.1 → 0.20.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/bootstrap/declare.js +8 -6
- package/dist/index.d.ts +5 -3
- package/dist/index.js +3 -2
- package/dist/plan/cloudflared-connector.d.ts +3 -2
- package/dist/plan/cloudflared-connector.js +2 -2
- package/dist/plan/diff.js +4 -4
- package/dist/plan/edge-access.d.ts +68 -0
- package/dist/plan/edge-access.js +123 -0
- package/dist/plan/edge-dns.d.ts +89 -0
- package/dist/plan/edge-dns.js +227 -0
- package/dist/plan/edge-ratelimits.d.ts +54 -0
- package/dist/plan/edge-ratelimits.js +79 -0
- package/dist/plan/edge-tunnels.d.ts +44 -0
- package/dist/plan/edge-tunnels.js +108 -0
- package/dist/plan/edge-workers.d.ts +31 -0
- package/dist/plan/edge-workers.js +165 -0
- package/dist/plan/edge-zones.d.ts +37 -0
- package/dist/plan/edge-zones.js +64 -0
- package/dist/plan/entry-edge-key.d.ts +10 -0
- package/dist/plan/entry-edge-key.js +38 -8
- package/dist/plan/executor.d.ts +112 -143
- package/dist/plan/executor.js +189 -87
- package/dist/plan/index.d.ts +15 -3
- package/dist/plan/index.js +8 -2
- package/dist/plan/plan-apply.js +5 -1
- package/dist/plan/platform-diff.d.ts +9 -0
- package/dist/plan/platform-diff.js +32 -8
- package/dist/plan/platform-providers.d.ts +53 -6
- package/dist/plan/platform-providers.js +87 -44
- package/dist/plan/providers-readers.d.ts +23 -1
- package/dist/plan/providers-readers.js +29 -0
- package/package.json +2 -1
|
@@ -36,6 +36,20 @@ export const EDGE_ENTRY_KEY_WARN_WINDOW_DAYS = 30;
|
|
|
36
36
|
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
37
37
|
/** The reserved labels last sent to the edge service, as that sorted list's own JSON. */
|
|
38
38
|
export const EDGE_RESERVED_LABELS_APPLIED_SCOPE_KEY = 'edge_reserved_labels_applied';
|
|
39
|
+
/**
|
|
40
|
+
* F3: the org's own Boule holds an edge key too, so a company edits its DNS
|
|
41
|
+
* records there (`records:read`, `records:edit`: edge's entry keys carry both).
|
|
42
|
+
* It rides the hosting-entry key path under this entry key. The leading
|
|
43
|
+
* underscore is one no hosting entry key can have (`ENTRY_KEY` in `org.ts`),
|
|
44
|
+
* so it never collides with a declared entry.
|
|
45
|
+
*/
|
|
46
|
+
export const BOULE_EDGE_ENTRY_KEY = '_boule';
|
|
47
|
+
/** The recorded-id key of the Coolify app an edge key's env is written to. */
|
|
48
|
+
export function edgeKeyAppRecordKey(org, entryKey) {
|
|
49
|
+
return entryKey === BOULE_EDGE_ENTRY_KEY
|
|
50
|
+
? `coolify:boule:${org.id}`
|
|
51
|
+
: `coolify:app:${appStepKey(org, entryKey)}`;
|
|
52
|
+
}
|
|
39
53
|
export const entryEdgeKeyName = (org, entry) => `${org.slug}-${entry.key}-edge`;
|
|
40
54
|
/**
|
|
41
55
|
* Mail names foundry's mail setup puts on the org's domain. Stalwart's and
|
|
@@ -97,7 +111,15 @@ export function planEntryEdgeKeys(org, recordedIds, plannedSteps, readiness, now
|
|
|
97
111
|
// throws before `runSteps` sees a `resourceId`), so scanning the main
|
|
98
112
|
// prefix alone would never find it to revoke once its entry is gone
|
|
99
113
|
// (#317 review).
|
|
100
|
-
|
|
114
|
+
// Boule's key is wanted whenever the org has its own domain (with-domain
|
|
115
|
+
// mode): edge refuses a company on oikos.host, so none is minted there.
|
|
116
|
+
const holders = org.hosting.entries
|
|
117
|
+
.filter((e) => e.uses?.edge)
|
|
118
|
+
.map((e) => ({ entryKey: e.key, name: entryEdgeKeyName(org, e) }));
|
|
119
|
+
if (org.domain) {
|
|
120
|
+
holders.push({ entryKey: BOULE_EDGE_ENTRY_KEY, name: `${org.slug}-boule-edge` });
|
|
121
|
+
}
|
|
122
|
+
const declared = new Set(holders.map((h) => h.entryKey));
|
|
101
123
|
const mainPrefix = `edge:key:${org.id}:`;
|
|
102
124
|
const undeliveredPrefix = 'edge_key_undelivered_';
|
|
103
125
|
const goneEntryKeys = new Set();
|
|
@@ -146,11 +168,11 @@ export function planEntryEdgeKeys(org, recordedIds, plannedSteps, readiness, now
|
|
|
146
168
|
},
|
|
147
169
|
});
|
|
148
170
|
}
|
|
149
|
-
for (const
|
|
150
|
-
|
|
151
|
-
continue;
|
|
171
|
+
for (const holder of holders) {
|
|
172
|
+
const entry = { key: holder.entryKey };
|
|
152
173
|
const stepKey = appStepKey(org, entry.key);
|
|
153
174
|
const key = `edge:key:${stepKey}`;
|
|
175
|
+
const appRecordKey = edgeKeyAppRecordKey(org, entry.key);
|
|
154
176
|
const liveKeyId = recordedIds[key] && recordedIds[entryEdgeRetiredScopeKey(entry.key)] !== recordedIds[key]
|
|
155
177
|
? recordedIds[key]
|
|
156
178
|
: undefined;
|
|
@@ -186,12 +208,20 @@ export function planEntryEdgeKeys(org, recordedIds, plannedSteps, readiness, now
|
|
|
186
208
|
// re-review) -- blocking indefinitely, as a prior round did, left the
|
|
187
209
|
// orphan live for up to 365 days with no recorded trace an operator
|
|
188
210
|
// could act on beyond this description.
|
|
211
|
+
// Boule's key is not something the org declared, so a platform with no
|
|
212
|
+
// edge service, or a Boule not planned yet, is quiet: no blocked row.
|
|
213
|
+
const isBoule = entry.key === BOULE_EDGE_ENTRY_KEY;
|
|
189
214
|
if (!readiness.edgeReady) {
|
|
190
|
-
|
|
215
|
+
if (!isBoule)
|
|
216
|
+
block('the host supplies no edge service, or the platform has not enabled it');
|
|
191
217
|
continue;
|
|
192
218
|
}
|
|
193
|
-
|
|
194
|
-
|
|
219
|
+
const appPlanned = isBoule
|
|
220
|
+
? planned('coolify', 'boule', org.id)
|
|
221
|
+
: planned('coolify', 'app', stepKey);
|
|
222
|
+
if (!recordedIds[appRecordKey] && !appPlanned) {
|
|
223
|
+
if (!isBoule)
|
|
224
|
+
block("the entry's app is not recorded yet");
|
|
195
225
|
continue;
|
|
196
226
|
}
|
|
197
227
|
steps.push({
|
|
@@ -201,7 +231,7 @@ export function planEntryEdgeKeys(org, recordedIds, plannedSteps, readiness, now
|
|
|
201
231
|
key: stepKey,
|
|
202
232
|
desired: {
|
|
203
233
|
mode: 'mint',
|
|
204
|
-
name:
|
|
234
|
+
name: holder.name,
|
|
205
235
|
edgeUrl: readiness.edgeUrl,
|
|
206
236
|
},
|
|
207
237
|
},
|
package/dist/plan/executor.d.ts
CHANGED
|
@@ -3,6 +3,11 @@ import type { DeclaredOrg, HostingBuildPack } from '../org.js';
|
|
|
3
3
|
import type { DeclaredOrgStore } from '../store.js';
|
|
4
4
|
import { type BrokerPort } from './broker-entries.js';
|
|
5
5
|
import { type StepAction } from './diff.js';
|
|
6
|
+
import { type AccessPort, type EdgePlatformAccess } from './edge-access.js';
|
|
7
|
+
import { type DnsPort, type EdgePlatformDns } from './edge-dns.js';
|
|
8
|
+
import { type EdgePlatformRateLimits, type RateLimitPort } from './edge-ratelimits.js';
|
|
9
|
+
import { type EdgePlatformTunnels, type TunnelPort } from './edge-tunnels.js';
|
|
10
|
+
import { type EdgePlatformZones, type ZonePort } from './edge-zones.js';
|
|
6
11
|
import { type LoginHostBindingsPort } from './login-host.js';
|
|
7
12
|
import { type MailCopyPort } from './mail-copy.js';
|
|
8
13
|
import type { DestroyTarget, StepDestroyer, StepExecutionContext, StepExecutor } from './plan-apply.js';
|
|
@@ -415,8 +420,10 @@ export interface CoolifyPort {
|
|
|
415
420
|
}>;
|
|
416
421
|
}
|
|
417
422
|
/**
|
|
418
|
-
* The Cloudflare calls
|
|
419
|
-
* `@wtfalch/providers/cloudflare`'s `CloudflareClient` method-for-method.
|
|
423
|
+
* The Cloudflare zone calls the platform steps make directly, shaped to match
|
|
424
|
+
* `@wtfalch/providers/cloudflare`'s `CloudflareClient` method-for-method. They
|
|
425
|
+
* run before edge exists (edge's own step finds the apex zone), so they cannot
|
|
426
|
+
* go through edge. An org's steps use {@link ZonePort}, which does.
|
|
420
427
|
*/
|
|
421
428
|
export interface CloudflarePort {
|
|
422
429
|
findZoneByApex(domain: string): Promise<{
|
|
@@ -427,132 +434,6 @@ export interface CloudflarePort {
|
|
|
427
434
|
id: string;
|
|
428
435
|
name: string;
|
|
429
436
|
}>;
|
|
430
|
-
findTunnelByName(name: string): Promise<{
|
|
431
|
-
id: string;
|
|
432
|
-
name: string;
|
|
433
|
-
} | undefined>;
|
|
434
|
-
createTunnel(name: string): Promise<{
|
|
435
|
-
id: string;
|
|
436
|
-
name: string;
|
|
437
|
-
}>;
|
|
438
|
-
putTunnelConfig(tunnelId: string, config: unknown): Promise<unknown>;
|
|
439
|
-
/**
|
|
440
|
-
* The tunnel's connector token (#197) -- a `cloudflared` container reads
|
|
441
|
-
* this at boot to dial into the tunnel. A live credential: matches
|
|
442
|
-
* `@wtfalch/providers`' `CloudflareClient.getTunnelToken` method-for-method.
|
|
443
|
-
* A caller must hand this straight to `bulkUpsertEnvs` and nowhere else --
|
|
444
|
-
* never a log, an error, a return value or a recorded scope id (see
|
|
445
|
-
* `cloudflared-connector.ts`'s `ensureCloudflaredConnector`).
|
|
446
|
-
*/
|
|
447
|
-
getTunnelToken(tunnelId: string): Promise<string>;
|
|
448
|
-
/** `FoundryPlanApply.destroy`'s teardown of the org's own Cloudflare tunnel. */
|
|
449
|
-
deleteTunnel(tunnelId: string): Promise<void>;
|
|
450
|
-
/** `cloudflareAccess` finds the org's Access app by domain before creating one (#84). */
|
|
451
|
-
listAccessApps(): Promise<Array<{
|
|
452
|
-
id: string;
|
|
453
|
-
name: string;
|
|
454
|
-
domain: string;
|
|
455
|
-
}>>;
|
|
456
|
-
createAccessApp(app: {
|
|
457
|
-
name: string;
|
|
458
|
-
domain: string;
|
|
459
|
-
type: string;
|
|
460
|
-
}): Promise<{
|
|
461
|
-
id: string;
|
|
462
|
-
}>;
|
|
463
|
-
listAccessPolicies(appId: string): Promise<Array<{
|
|
464
|
-
id: string;
|
|
465
|
-
name: string;
|
|
466
|
-
include?: unknown[];
|
|
467
|
-
}>>;
|
|
468
|
-
createAccessPolicy(appId: string, policy: {
|
|
469
|
-
name: string;
|
|
470
|
-
decision: string;
|
|
471
|
-
include: unknown[];
|
|
472
|
-
}): Promise<{
|
|
473
|
-
id: string;
|
|
474
|
-
}>;
|
|
475
|
-
/**
|
|
476
|
-
* `cloudflareAccess` resolves a `kind: 'service-token'` rule's named
|
|
477
|
-
* token to its id (#246) -- the token itself is a live Cloudflare
|
|
478
|
-
* resource this package never mints; `undefined` when no token by that
|
|
479
|
-
* name exists yet.
|
|
480
|
-
*/
|
|
481
|
-
findServiceTokenByName(name: string): Promise<{
|
|
482
|
-
id: string;
|
|
483
|
-
} | undefined>;
|
|
484
|
-
deleteAccessApp(appId: string): Promise<void>;
|
|
485
|
-
/**
|
|
486
|
-
* `FoundryPlanApply.destroy`'s teardown of any DNS record foundry created
|
|
487
|
-
* for the org -- `destroy`'s `cloudflare:tunnel` case uses it to remove
|
|
488
|
-
* every CNAME {@link publishTunnelDnsRecords} published (#236).
|
|
489
|
-
*/
|
|
490
|
-
deleteDnsRecord(zoneId: string, recordId: string): Promise<void>;
|
|
491
|
-
/** `coolify:mail`'s lookup of an existing MX/TXT record before upserting it, filtered by type so a stale record of a different type is never mistaken for a match. */
|
|
492
|
-
findDnsRecord(zoneId: string, name: string, type?: string): Promise<Array<{
|
|
493
|
-
id: string;
|
|
494
|
-
type: string;
|
|
495
|
-
content: string;
|
|
496
|
-
name: string;
|
|
497
|
-
}>>;
|
|
498
|
-
/**
|
|
499
|
-
* #236: `destroy`'s `cloudflare:tunnel` case and
|
|
500
|
-
* {@link FoundryStepExecutor.deleteStaleTunnelDnsRecords} each fall back to
|
|
501
|
-
* this for a domain recorded before
|
|
502
|
-
* {@link CLOUDFLARE_TUNNEL_DNS_HOSTNAMES_SCOPE_KEY} existed, with no
|
|
503
|
-
* hostname list to look up instead -- the one case left that must still
|
|
504
|
-
* see every CNAME in the zone, not one matched by name. Round 3: this is
|
|
505
|
-
* no longer either caller's normal path, since the real adapter
|
|
506
|
-
* (`@wtfalch/providers`' `cloudflare.ts`) asks for one page of 100 and
|
|
507
|
-
* never walks `result_info.total_pages`, so a zone with more records than
|
|
508
|
-
* that would silently lose everything past the first page.
|
|
509
|
-
*/
|
|
510
|
-
listDnsRecords(zoneId: string, opts?: {
|
|
511
|
-
type?: string;
|
|
512
|
-
name?: string;
|
|
513
|
-
}): Promise<Array<{
|
|
514
|
-
id: string;
|
|
515
|
-
type: string;
|
|
516
|
-
content: string;
|
|
517
|
-
name: string;
|
|
518
|
-
}>>;
|
|
519
|
-
createDnsRecord(zoneId: string, rec: {
|
|
520
|
-
type: string;
|
|
521
|
-
name: string;
|
|
522
|
-
content: string;
|
|
523
|
-
proxied?: boolean;
|
|
524
|
-
ttl?: number;
|
|
525
|
-
/** Required by Cloudflare for an `MX` record. */
|
|
526
|
-
priority?: number;
|
|
527
|
-
}): Promise<{
|
|
528
|
-
id: string;
|
|
529
|
-
name: string;
|
|
530
|
-
content: string;
|
|
531
|
-
}>;
|
|
532
|
-
updateDnsRecord(zoneId: string, recordId: string, rec: {
|
|
533
|
-
type: string;
|
|
534
|
-
name: string;
|
|
535
|
-
content: string;
|
|
536
|
-
proxied?: boolean;
|
|
537
|
-
ttl?: number;
|
|
538
|
-
priority?: number;
|
|
539
|
-
}): Promise<{
|
|
540
|
-
id: string;
|
|
541
|
-
name: string;
|
|
542
|
-
content: string;
|
|
543
|
-
}>;
|
|
544
|
-
/**
|
|
545
|
-
* Escape hatch (mirrors `CoolifyPort.raw`'s own precedent above) --
|
|
546
|
-
* `@wtfalch/providers` 0.5.0's `CloudflareClient` has no typed method for
|
|
547
|
-
* Health Checks or zone rulesets/rate-limiting rules
|
|
548
|
-
* (https://github.com/wtfalch/foundry/issues/43), only this public,
|
|
549
|
-
* generic `request<T>(method, path, body?, missingOkay?)`, which
|
|
550
|
-
* this port names directly rather than inventing a narrower method no real
|
|
551
|
-
* client could structurally satisfy. `cloudflareRateLimit` below is its one
|
|
552
|
-
* caller today. `missingOkay` makes a 404 resolve to `undefined` instead of
|
|
553
|
-
* throwing, matching `CloudflareClient`'s own real behavior exactly.
|
|
554
|
-
*/
|
|
555
|
-
request<T>(method: string, path: string, body?: unknown, missingOkay?: boolean): Promise<T>;
|
|
556
437
|
}
|
|
557
438
|
/**
|
|
558
439
|
* One Cloudflare Workers rate limiting binding, Cloudflare's own "simple"
|
|
@@ -569,7 +450,8 @@ export interface WorkersRateLimitBinding {
|
|
|
569
450
|
};
|
|
570
451
|
}
|
|
571
452
|
/**
|
|
572
|
-
* The
|
|
453
|
+
* The Workers calls `cloudflareWorker` makes (#318, F8), served by edge's
|
|
454
|
+
* `platform.workers` through `createEdgeWorkers` (`edge-workers.ts`): deploys
|
|
573
455
|
* a plain Worker from an entry's `worker` declaration and routes it on the
|
|
574
456
|
* org's own zone. Optional on {@link FoundryStepExecutorPorts} -- a host
|
|
575
457
|
* with no Workers rights on its Cloudflare token (the issue's own "Human
|
|
@@ -583,15 +465,13 @@ export interface WorkersRateLimitBinding {
|
|
|
583
465
|
* package (estate DECISIONS.md, 2026-10-04, "offline.tf Worker setup" --
|
|
584
466
|
* "Foundry consumes and deploys the artifact through the provider
|
|
585
467
|
* boundary... No manual build step and no source checkout/bundling in
|
|
586
|
-
* Foundry"). The
|
|
587
|
-
*
|
|
588
|
-
*
|
|
589
|
-
* `CloudflarePort.request` sends JSON only, so this port names the call
|
|
590
|
-
* directly rather than forcing it through that escape hatch.
|
|
468
|
+
* Foundry"). The host hands `createEdgeWorkers` a `fetchArtifact(url, sha256)`
|
|
469
|
+
* that fetches and verifies the bytes; the adapter sends them to edge as
|
|
470
|
+
* base64 with the digest, and edge checks the digest again.
|
|
591
471
|
*
|
|
592
472
|
* `deleteRoute`/`deleteScript` are idempotent: deleting a route or script
|
|
593
473
|
* that is already gone resolves rather than throwing, the same contract
|
|
594
|
-
* `
|
|
474
|
+
* `DnsPort.deleteDnsRecord`/`deleteAccessApp` give their own
|
|
595
475
|
* already-gone case via `isNotFoundError` at the call site -- here it is
|
|
596
476
|
* the port's own job, since `cloudflareWorker` deletes a route or script by
|
|
597
477
|
* id/name it already recorded, with nothing live to re-check first.
|
|
@@ -1373,6 +1253,37 @@ export interface EdgePort {
|
|
|
1373
1253
|
secret: string;
|
|
1374
1254
|
}>;
|
|
1375
1255
|
revokeKey(organisationId: string, keyId: string): Promise<void>;
|
|
1256
|
+
/**
|
|
1257
|
+
* F4.dns: edge's platform DNS client (`createEdgeClient({ baseUrl, key
|
|
1258
|
+
* }).platform.dns`), built with the same platform key that mints and
|
|
1259
|
+
* revokes. Foundry reads and writes every DNS record through it.
|
|
1260
|
+
*/
|
|
1261
|
+
dns: EdgePlatformDns;
|
|
1262
|
+
/**
|
|
1263
|
+
* F4.tunnels: edge's platform tunnel client (`createEdgeClient({ baseUrl,
|
|
1264
|
+
* key }).platform.tunnels`), built with the same platform key. Foundry
|
|
1265
|
+
* creates, configures, reads the token of and deletes every tunnel through it.
|
|
1266
|
+
*/
|
|
1267
|
+
tunnels: EdgePlatformTunnels;
|
|
1268
|
+
/**
|
|
1269
|
+
* F4.access: edge's platform Access client (`createEdgeClient({ baseUrl,
|
|
1270
|
+
* key }).platform.access`), built with the same platform key. Foundry
|
|
1271
|
+
* finds, creates, adopts and deletes every Access app, and adds its
|
|
1272
|
+
* policies, through it.
|
|
1273
|
+
*/
|
|
1274
|
+
access: EdgePlatformAccess;
|
|
1275
|
+
/**
|
|
1276
|
+
* F4.ratelimits: edge's platform rate limiting client (`createEdgeClient({
|
|
1277
|
+
* baseUrl, key }).platform.ratelimits`), built with the same platform key.
|
|
1278
|
+
* Foundry lists, adopts and creates every rate limiting rule through it.
|
|
1279
|
+
*/
|
|
1280
|
+
ratelimits: EdgePlatformRateLimits;
|
|
1281
|
+
/**
|
|
1282
|
+
* F4.zones: edge's platform zones client (`createEdgeClient({ baseUrl,
|
|
1283
|
+
* key }).platform.zones`), built with the same platform key. Foundry
|
|
1284
|
+
* looks up and creates every org zone through it.
|
|
1285
|
+
*/
|
|
1286
|
+
zones: EdgePlatformZones;
|
|
1376
1287
|
/** Refuses a label any hosting entry of `organisationId` already holds. Rerun whenever the org's own label set changes. */
|
|
1377
1288
|
setReservedLabels(organisationId: string, labels: string[]): Promise<void>;
|
|
1378
1289
|
}
|
|
@@ -1420,7 +1331,6 @@ export interface PlatformOrgPortSource {
|
|
|
1420
1331
|
}
|
|
1421
1332
|
export interface FoundryStepExecutorPorts {
|
|
1422
1333
|
coolify: CoolifyPort;
|
|
1423
|
-
cloudflare: CloudflarePort;
|
|
1424
1334
|
zitadel: ZitadelPort;
|
|
1425
1335
|
mail: MailPort;
|
|
1426
1336
|
lettermint: LettermintPort;
|
|
@@ -1455,6 +1365,31 @@ export interface FoundryStepExecutorPorts {
|
|
|
1455
1365
|
* throw.
|
|
1456
1366
|
*/
|
|
1457
1367
|
edge?: EdgePort;
|
|
1368
|
+
/**
|
|
1369
|
+
* F4.dns: overrides the DNS calls, which otherwise go through `edge.dns`
|
|
1370
|
+
* (see {@link EdgePort.dns}). Kept for a host or a test that supplies its own.
|
|
1371
|
+
*/
|
|
1372
|
+
dns?: DnsPort;
|
|
1373
|
+
/**
|
|
1374
|
+
* F4.tunnels: overrides the tunnel calls, which otherwise go through
|
|
1375
|
+
* `edge.tunnels` (see {@link EdgePort.tunnels}). Kept for a host or a test that supplies its own.
|
|
1376
|
+
*/
|
|
1377
|
+
tunnels?: TunnelPort;
|
|
1378
|
+
/**
|
|
1379
|
+
* F4.access: overrides the Access calls, which otherwise go through
|
|
1380
|
+
* `edge.access` (see {@link EdgePort.access}). Kept for a host or a test that supplies its own.
|
|
1381
|
+
*/
|
|
1382
|
+
access?: AccessPort;
|
|
1383
|
+
/**
|
|
1384
|
+
* F4.ratelimits: overrides the rate limiting rule calls, which otherwise go through
|
|
1385
|
+
* `edge.ratelimits` (see {@link EdgePort.ratelimits}). Kept for a host or a test that supplies its own.
|
|
1386
|
+
*/
|
|
1387
|
+
ratelimits?: RateLimitPort;
|
|
1388
|
+
/**
|
|
1389
|
+
* F4.zones: overrides the zone calls, which otherwise go through
|
|
1390
|
+
* `edge.zones` (see {@link EdgePort.zones}). Kept for a host or a test that supplies its own.
|
|
1391
|
+
*/
|
|
1392
|
+
zones?: ZonePort;
|
|
1458
1393
|
/**
|
|
1459
1394
|
* #177: builds the files, AI and jobs ports from the platform's addresses
|
|
1460
1395
|
* and held keys. A port set above overrides it (tests); with neither, the
|
|
@@ -1579,6 +1514,12 @@ export declare const databaseStartWait: {
|
|
|
1579
1514
|
};
|
|
1580
1515
|
export declare class FoundryStepExecutor implements StepExecutor, StepDestroyer {
|
|
1581
1516
|
private readonly ports;
|
|
1517
|
+
private edgeDns;
|
|
1518
|
+
private edgeTunnels;
|
|
1519
|
+
private edgeAccess;
|
|
1520
|
+
private edgeRateLimits;
|
|
1521
|
+
private edgeZones;
|
|
1522
|
+
private edgeWriteSlot;
|
|
1582
1523
|
constructor(ports: FoundryStepExecutorPorts);
|
|
1583
1524
|
execute(action: StepAction, context: StepExecutionContext): Promise<{
|
|
1584
1525
|
resourceId?: string;
|
|
@@ -1615,12 +1556,10 @@ export declare class FoundryStepExecutor implements StepExecutor, StepDestroyer
|
|
|
1615
1556
|
* gate), so `org.domain` is set and that zone is the org's alone, never
|
|
1616
1557
|
* shared with another org the way `oikos.host` is.
|
|
1617
1558
|
*
|
|
1618
|
-
* Rate limiting rules
|
|
1619
|
-
*
|
|
1620
|
-
*
|
|
1621
|
-
*
|
|
1622
|
-
* `rules[]` and matching `RATE_LIMIT_RULE_DESCRIPTION`, the same shape
|
|
1623
|
-
* `cloudflareAccess` above finds its own Access app by name. The Free
|
|
1559
|
+
* Rate limiting rules go through edge (`RateLimitPort`) -- so "find by
|
|
1560
|
+
* name" here means listing the zone's rules and matching
|
|
1561
|
+
* `RATE_LIMIT_RULE_DESCRIPTION`, the same shape `cloudflareAccess` above
|
|
1562
|
+
* finds its own Access app by name. The Free
|
|
1624
1563
|
* plan allows exactly one rule per zone, so a rule already there under a
|
|
1625
1564
|
* different description is refused, never adopted or replaced, mirroring
|
|
1626
1565
|
* `cloudflareAccess`'s own refusal when a foreign Access app already
|
|
@@ -2419,6 +2358,36 @@ export declare class FoundryStepExecutor implements StepExecutor, StepDestroyer
|
|
|
2419
2358
|
/** #177: the host's own port when it supplies one, else the platform's, built from its address and held key. */
|
|
2420
2359
|
private filesPort;
|
|
2421
2360
|
private aiPort;
|
|
2361
|
+
/**
|
|
2362
|
+
* Every DNS record call runs through edge (`edge.dns`, the platform
|
|
2363
|
+
* credential), adopting live records before it first changes them. `dns` on
|
|
2364
|
+
* the ports is a seam for a host or test that supplies its own {@link DnsPort}.
|
|
2365
|
+
*/
|
|
2366
|
+
private dnsPort;
|
|
2367
|
+
/**
|
|
2368
|
+
* Every tunnel call runs through edge (`edge.tunnels`, the platform
|
|
2369
|
+
* credential), adopting a live tunnel before it first configures, reads the
|
|
2370
|
+
* token of or deletes it. Shares edge's write slot with {@link dnsPort}.
|
|
2371
|
+
*/
|
|
2372
|
+
private tunnelPort;
|
|
2373
|
+
/**
|
|
2374
|
+
* Every Access call runs through edge (`edge.access`, the platform
|
|
2375
|
+
* credential), adopting a live app before it first adds a policy to or
|
|
2376
|
+
* deletes it. Shares edge's write slot with {@link dnsPort}.
|
|
2377
|
+
*/
|
|
2378
|
+
private accessPort;
|
|
2379
|
+
/**
|
|
2380
|
+
* Every rate limiting rule call runs through edge (`edge.ratelimits`, the
|
|
2381
|
+
* platform credential), adopting the live rule before it first creates it.
|
|
2382
|
+
* Shares edge's write slot with {@link dnsPort}.
|
|
2383
|
+
*/
|
|
2384
|
+
private rateLimitPort;
|
|
2385
|
+
/**
|
|
2386
|
+
* Every zone lookup and create runs through edge (`edge.zones`, the
|
|
2387
|
+
* platform credential), adopting a live zone before it first creates it.
|
|
2388
|
+
* Shares edge's write slot with {@link dnsPort}.
|
|
2389
|
+
*/
|
|
2390
|
+
private zonePort;
|
|
2422
2391
|
private jobsPort;
|
|
2423
2392
|
private jobsRegistration;
|
|
2424
2393
|
/** `deliverSecret`, tried up to {@link JOBS_DELIVERY_ATTEMPTS} times; true once one attempt succeeds. */
|
|
@@ -2647,7 +2616,7 @@ export declare class FoundryStepExecutor implements StepExecutor, StepDestroyer
|
|
|
2647
2616
|
* a record someone proxied by hand is put back. An `MX` record carries the
|
|
2648
2617
|
* priority it was given, which Cloudflare requires.
|
|
2649
2618
|
*
|
|
2650
|
-
* Found by an exact name+type match (`
|
|
2619
|
+
* Found by an exact name+type match (`DnsPort.findDnsRecord`'s own
|
|
2651
2620
|
* doc comment) so a stale record of a different type is never taken for a
|
|
2652
2621
|
* match. A name can hold several `TXT` records (an apex often holds a site
|
|
2653
2622
|
* verification beside SPF), so a `TXT` record only replaces one that
|