@wtfalch/foundry 0.33.1 → 0.35.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/bootstrap.d.ts +1 -1
- package/dist/bootstrap/bootstrap.js +49 -11
- package/dist/bootstrap/declare.d.ts +12 -5
- package/dist/bootstrap/declare.js +33 -18
- package/dist/bootstrap/versions.d.ts +24 -0
- package/dist/bootstrap/versions.js +46 -0
- package/dist/calls-sfu.d.ts +10 -0
- package/dist/calls-sfu.js +44 -3
- package/dist/index.d.ts +21 -8
- package/dist/index.js +11 -4
- package/dist/migrations/0051_org_services.sql +13 -0
- package/dist/migrations/0052_platform_mail_relay.sql +8 -0
- package/dist/migrations/0053_heartbeats_provider.sql +9 -0
- package/dist/migrations/0053_org_version_pin.sql +6 -0
- package/dist/org.d.ts +76 -12
- package/dist/org.js +136 -20
- package/dist/plan/backup-gate.d.ts +93 -0
- package/dist/plan/backup-gate.js +261 -0
- package/dist/plan/broker-entries.d.ts +9 -1
- package/dist/plan/broker-entries.js +20 -3
- package/dist/plan/build-record.d.ts +56 -0
- package/dist/plan/build-record.js +109 -0
- package/dist/plan/calls-matrix.d.ts +9 -0
- package/dist/plan/calls-matrix.js +18 -1
- package/dist/plan/cloudflared-connector.d.ts +7 -1
- package/dist/plan/cloudflared-connector.js +15 -2
- package/dist/plan/company-backups.d.ts +31 -0
- package/dist/plan/company-backups.js +101 -0
- package/dist/plan/deploy-health.d.ts +12 -2
- package/dist/plan/deploy-health.js +3 -1
- package/dist/plan/destinations.d.ts +73 -0
- package/dist/plan/destinations.js +217 -0
- package/dist/plan/diff.d.ts +46 -8
- package/dist/plan/diff.js +367 -126
- package/dist/plan/edge-access.d.ts +15 -0
- package/dist/plan/edge-access.js +21 -2
- package/dist/plan/executor.d.ts +276 -5
- package/dist/plan/executor.js +1111 -109
- package/dist/plan/health-check-settings.d.ts +28 -0
- package/dist/plan/health-check-settings.js +36 -0
- package/dist/plan/health-gaps.d.ts +33 -0
- package/dist/plan/health-gaps.js +89 -0
- package/dist/plan/health-targets.d.ts +1 -3
- package/dist/plan/health-targets.js +36 -36
- package/dist/plan/held-key-refs.d.ts +15 -0
- package/dist/plan/held-key-refs.js +15 -0
- package/dist/plan/hostnames.d.ts +10 -0
- package/dist/plan/hostnames.js +14 -0
- package/dist/plan/index.d.ts +19 -6
- package/dist/plan/index.js +10 -3
- package/dist/plan/live.d.ts +11 -2
- package/dist/plan/live.js +16 -2
- package/dist/plan/locked-hostnames.d.ts +7 -3
- package/dist/plan/locked-hostnames.js +25 -4
- package/dist/plan/login-host.d.ts +61 -12
- package/dist/plan/login-host.js +99 -11
- package/dist/plan/mail-relay.d.ts +139 -0
- package/dist/plan/mail-relay.js +400 -0
- package/dist/plan/network-move.d.ts +20 -0
- package/dist/plan/network-move.js +41 -0
- package/dist/plan/oikos-ingress.d.ts +111 -0
- package/dist/plan/oikos-ingress.js +231 -0
- package/dist/plan/org-names.d.ts +30 -0
- package/dist/plan/org-names.js +108 -0
- package/dist/plan/own-diff.d.ts +170 -0
- package/dist/plan/own-diff.js +440 -0
- package/dist/plan/own-dns.d.ts +6 -0
- package/dist/plan/own-dns.js +15 -0
- package/dist/plan/own-hostnames.d.ts +27 -0
- package/dist/plan/own-hostnames.js +24 -0
- package/dist/plan/own-limits.d.ts +213 -0
- package/dist/plan/own-limits.js +232 -0
- package/dist/plan/own-services.d.ts +193 -0
- package/dist/plan/own-services.js +169 -0
- package/dist/plan/own-sfu.d.ts +69 -0
- package/dist/plan/own-sfu.js +256 -0
- package/dist/plan/plan-apply.d.ts +121 -10
- package/dist/plan/plan-apply.js +357 -18
- package/dist/plan/platform-diff.d.ts +155 -11
- package/dist/plan/platform-diff.js +271 -66
- package/dist/plan/platform-org.d.ts +107 -4
- package/dist/plan/platform-org.js +175 -3
- package/dist/plan/platform-providers.d.ts +474 -81
- package/dist/plan/platform-providers.js +1407 -560
- package/dist/plan/proxy-labels.d.ts +57 -11
- package/dist/plan/proxy-labels.js +94 -14
- package/dist/plan/run-options.d.ts +19 -0
- package/dist/plan/run-options.js +46 -0
- package/dist/plan/sign-in-org.d.ts +1 -1
- package/dist/plan/sign-in-org.js +20 -2
- package/dist/plan/zitadel-login-policy.d.ts +1 -1
- package/dist/plan/zitadel-login-policy.js +77 -9
- package/dist/plan/zitadel-system-api.d.ts +22 -1
- package/dist/plan/zitadel-system-api.js +49 -8
- package/dist/platform.d.ts +40 -0
- package/dist/platform.js +16 -0
- package/dist/provision.d.ts +2 -0
- package/dist/provision.js +3 -1
- package/dist/slug.d.ts +3 -1
- package/dist/slug.js +26 -3
- package/dist/store/postgres.js +93 -2
- package/dist/store/schema.d.ts +87 -0
- package/dist/store/schema.js +9 -0
- package/package.json +3 -3
- package/versions.digests.json +11 -0
- package/versions.json +3 -1
|
@@ -7,10 +7,12 @@ import type { AppliedState } from '../state.js';
|
|
|
7
7
|
import { type AppliedVersionsStore, type DeclaredOrgStore, type PlatformStore } from '../store.js';
|
|
8
8
|
import { type AdoptCandidate, type AdoptInput, type AdoptPlan } from './adopt.js';
|
|
9
9
|
import { type AgoraHostPort } from './agora.js';
|
|
10
|
+
import { type MoveGatePorts } from './backup-gate.js';
|
|
10
11
|
import { type UnconfirmedUpgrade } from './confirm-upgrade.js';
|
|
11
12
|
import { type StepAction } from './diff.js';
|
|
12
13
|
import { type LiveReaders, type LiveResource, type PlatformLiveReaders } from './live.js';
|
|
13
|
-
import { type
|
|
14
|
+
import { type LoginHostDiscoveryPort } from './login-host.js';
|
|
15
|
+
import { type EdgePortName, type HeldKeyResolver, type PlatformOwnerInput } from './platform-org.js';
|
|
14
16
|
import type { CallsSfuConnectionPort } from './platform-sfu.js';
|
|
15
17
|
/** Who did it, for `@wtfalch/audit`'s "who, when, the plan, the result" (#9, #19). */
|
|
16
18
|
export interface AuditActor {
|
|
@@ -123,6 +125,11 @@ export interface AuthzGate {
|
|
|
123
125
|
*/
|
|
124
126
|
export interface StepExecutionContext {
|
|
125
127
|
readonly org: DeclaredOrg;
|
|
128
|
+
/**
|
|
129
|
+
* True when `org` is the platform's own company (`DeclaredPlatform.ownerOrgId`). It stays on the
|
|
130
|
+
* platform's Docker network, not on `co-<slug>`, so Archon reaches its database. Absent: false.
|
|
131
|
+
*/
|
|
132
|
+
readonly ownerCompany?: boolean;
|
|
126
133
|
/**
|
|
127
134
|
* Every id foundry has recorded for this org so far — both `system:kind:key`
|
|
128
135
|
* resource ids (`runSteps` records these itself from `execute`'s own return
|
|
@@ -172,6 +179,39 @@ export interface StepExecutor {
|
|
|
172
179
|
* an executor without it supplies none.
|
|
173
180
|
*/
|
|
174
181
|
ownEdgePorts?(): Partial<Record<EdgePortName, boolean>>;
|
|
182
|
+
/**
|
|
183
|
+
* What the executor's own files dependencies supply (`ownServices.provision.files`):
|
|
184
|
+
* valid keys worker settings, and a bucket port. The planner blocks an own
|
|
185
|
+
* org's files host until both are true. Optional: an executor without it supplies neither.
|
|
186
|
+
*/
|
|
187
|
+
ownFilesDeps?(): {
|
|
188
|
+
keys: boolean;
|
|
189
|
+
bucket: boolean;
|
|
190
|
+
};
|
|
191
|
+
/**
|
|
192
|
+
* Which ports an own org's files storage key step needs this executor has: the R2 key port, and
|
|
193
|
+
* `storeR2Credentials` on the org's files-organisation port. Optional: an executor without it
|
|
194
|
+
* leaves the step unplanned.
|
|
195
|
+
*/
|
|
196
|
+
ownFilesR2Ports?(org: DeclaredOrg, recordedIds: Readonly<Record<string, string>>): Promise<{
|
|
197
|
+
r2Keys: boolean;
|
|
198
|
+
storeR2Credentials: boolean;
|
|
199
|
+
}>;
|
|
200
|
+
/** Whether the executor can mint an own org's Lettermint key: a Lettermint port and a reader for the held ref. */
|
|
201
|
+
ownMailKeyMintable?(): boolean;
|
|
202
|
+
/**
|
|
203
|
+
* Whether the host's scoped ports for this own org include a files-organisation
|
|
204
|
+
* port. The planner plans no `files:organisation` step for an own org while
|
|
205
|
+
* this says false. Optional: an executor without it is taken to have the port.
|
|
206
|
+
*/
|
|
207
|
+
ownFilesOrganisationPort?(org: DeclaredOrg, recordedIds: Readonly<Record<string, string>>): Promise<boolean>;
|
|
208
|
+
/**
|
|
209
|
+
* Whether the host's scoped ports for this own org include a broker port, the company's own
|
|
210
|
+
* broker. An own org's broker entries are planned on this answer, not on
|
|
211
|
+
* {@link FoundryPlanApplyOptions.broker}, which names the shared broker. Optional: an executor
|
|
212
|
+
* without it leaves the option as the answer.
|
|
213
|
+
*/
|
|
214
|
+
ownBrokerPort?(org: DeclaredOrg, recordedIds: Readonly<Record<string, string>>): Promise<boolean>;
|
|
175
215
|
/**
|
|
176
216
|
* Every secret this executor minted or read so far (peer password, bridge
|
|
177
217
|
* secret, invite code and link). The host's run adds them to its redactor
|
|
@@ -192,6 +232,8 @@ export interface StepExecutor {
|
|
|
192
232
|
*/
|
|
193
233
|
export interface PlatformStepExecutionContext {
|
|
194
234
|
readonly platform: DeclaredPlatform;
|
|
235
|
+
/** The owner company's own sign-in and files host, when applied. Archon and the Access provider read them. */
|
|
236
|
+
readonly owner?: PlatformOwnerInput;
|
|
195
237
|
/** {@link StepExecutionContext.recordedIds}, for the platform's own run. */
|
|
196
238
|
recordedIds(): Readonly<Record<string, string>>;
|
|
197
239
|
/** {@link StepExecutionContext.recordScopeId}, for the platform's own run. */
|
|
@@ -227,7 +269,7 @@ export interface PlatformStepExecutor {
|
|
|
227
269
|
* unmanaged item and plans no step for it, the way a blocked Agora is
|
|
228
270
|
* reported, instead of planning a step that throws on every apply.
|
|
229
271
|
*/
|
|
230
|
-
blockedSteps?(platform: DeclaredPlatform, recordedIds: Readonly<Record<string, string
|
|
272
|
+
blockedSteps?(platform: DeclaredPlatform, recordedIds: Readonly<Record<string, string>>, owner?: PlatformOwnerInput): Promise<{
|
|
231
273
|
auth?: string;
|
|
232
274
|
/** #211 round 6: see `PlanPlatformOptions.blocked.authExecutorMissing`. */
|
|
233
275
|
authExecutorMissing?: boolean;
|
|
@@ -238,8 +280,12 @@ export interface PlatformStepExecutor {
|
|
|
238
280
|
ai?: string;
|
|
239
281
|
archon?: string;
|
|
240
282
|
mail?: string;
|
|
283
|
+
/** See `PlanPlatformOptions.blocked.relay` (`platform-diff.ts`). */
|
|
284
|
+
relay?: string;
|
|
241
285
|
/** #604: see `PlanPlatformOptions.blocked.accessProvider` (`platform-diff.ts`). */
|
|
242
286
|
accessProvider?: string;
|
|
287
|
+
/** See `PlanPlatformOptions.blocked.oikosIngress` (`platform-diff.ts`). */
|
|
288
|
+
oikosIngress?: string;
|
|
243
289
|
/** #257: see `PlanPlatformOptions.blocked.backupExecutorMissing` (`platform-diff.ts`). */
|
|
244
290
|
backupExecutorMissing?: boolean;
|
|
245
291
|
/** #540: see `PlanPlatformOptions.blocked.hc` (`platform-diff.ts`). */
|
|
@@ -262,7 +308,7 @@ export interface PlatformStepExecutor {
|
|
|
262
308
|
* the token and the edge step does not clear the flag, so the host must stop
|
|
263
309
|
* reporting it once edge holds the new token and the old one is gone.
|
|
264
310
|
*/
|
|
265
|
-
driftedSteps?(platform: DeclaredPlatform, recordedIds: Readonly<Record<string, string
|
|
311
|
+
driftedSteps?(platform: DeclaredPlatform, recordedIds: Readonly<Record<string, string>>, owner?: PlatformOwnerInput): Promise<PlatformDriftedSteps>;
|
|
266
312
|
/**
|
|
267
313
|
* Gives the platform files runner a changed share-domain set and touches
|
|
268
314
|
* nothing else. Returns the scope ids to record (only the share-domains
|
|
@@ -382,6 +428,14 @@ export interface FoundryPlanApplyOptions {
|
|
|
382
428
|
* before zitadel-login's binding can be written.
|
|
383
429
|
*/
|
|
384
430
|
loginHostBindings?: boolean;
|
|
431
|
+
/**
|
|
432
|
+
* Reads `GET https://<login host>/.well-known/openid-configuration` for a
|
|
433
|
+
* company's login host (5 s limit). Optional, and without it every company's
|
|
434
|
+
* Agora keeps the platform issuer and the plan carries a blocked line for the
|
|
435
|
+
* switch. The host must route the company's login host to a login app that
|
|
436
|
+
* serves discovery (https://github.com/wtfalch/auth/pull/233).
|
|
437
|
+
*/
|
|
438
|
+
loginHostDiscovery?: LoginHostDiscoveryPort;
|
|
385
439
|
/**
|
|
386
440
|
* True when the host's ZITADEL client has `updateOidcAppIdTokenUserinfoAssertion`
|
|
387
441
|
* (`@wtfalch/providers` at the version that has it). Optional, and false
|
|
@@ -420,6 +474,23 @@ export interface FoundryPlanApplyOptions {
|
|
|
420
474
|
* reported blocked instead of planned to fail.
|
|
421
475
|
*/
|
|
422
476
|
edge?: boolean;
|
|
477
|
+
/**
|
|
478
|
+
* The executor was built with an `ownServices` port. Without it, an org
|
|
479
|
+
* that runs its own services (`services.own`) is reported blocked and none of
|
|
480
|
+
* its steps run.
|
|
481
|
+
*/
|
|
482
|
+
ownServices?: boolean;
|
|
483
|
+
/**
|
|
484
|
+
* Says whether an own org's media server slot has its ports open on the
|
|
485
|
+
* Coolify server. Foundry opens none, so without this the plan keeps a
|
|
486
|
+
* fixed line naming them. A call that fails or takes over five seconds
|
|
487
|
+
* counts as not open.
|
|
488
|
+
*/
|
|
489
|
+
sfuPortsOpen?: (range: {
|
|
490
|
+
udpStart: number;
|
|
491
|
+
udpEnd: number;
|
|
492
|
+
tcp: number;
|
|
493
|
+
}) => Promise<boolean>;
|
|
423
494
|
/**
|
|
424
495
|
* Narrows every org plan to the steps `step` accepts, and its blocked items
|
|
425
496
|
* to the keys `blocked` accepts, so a plan, its stale check and its apply
|
|
@@ -459,6 +530,15 @@ export interface FoundryPlanApplyOptions {
|
|
|
459
530
|
* every existing caller of this constructor is unaffected.
|
|
460
531
|
*/
|
|
461
532
|
appliedVersions?: AppliedVersionsStore;
|
|
533
|
+
/**
|
|
534
|
+
* F6: the Coolify slice that reads a company database's backups. With it, an
|
|
535
|
+
* org's databases get backup steps and a confirmed stateful upgrade is
|
|
536
|
+
* refused without a fresh finished dump (`backup-gate.ts`). Without it
|
|
537
|
+
* neither happens, so an existing caller is unaffected.
|
|
538
|
+
*/
|
|
539
|
+
backupGate?: {
|
|
540
|
+
coolify: MoveGatePorts['coolify'];
|
|
541
|
+
};
|
|
462
542
|
/**
|
|
463
543
|
* #167: dependencies for planning/applying the shared platform (#166)
|
|
464
544
|
* alongside an org. Optional so an org-only caller — every #19-era caller,
|
|
@@ -487,12 +567,6 @@ export interface FoundryPlanApplyOptions {
|
|
|
487
567
|
heldKeys?: HeldKeyResolver;
|
|
488
568
|
};
|
|
489
569
|
}
|
|
490
|
-
/**
|
|
491
|
-
* The #19 implementation of {@link PlanApply}: computes a plan against live
|
|
492
|
-
* state, and refuses to apply one that has gone stale, one that touches a
|
|
493
|
-
* destructive or migrating step without the org's name typed, or one the
|
|
494
|
-
* actor is not authorized to run.
|
|
495
|
-
*/
|
|
496
570
|
export declare class FoundryPlanApply implements PlanApply {
|
|
497
571
|
private readonly store;
|
|
498
572
|
private readonly readers;
|
|
@@ -504,6 +578,7 @@ export declare class FoundryPlanApply implements PlanApply {
|
|
|
504
578
|
private readonly agora;
|
|
505
579
|
private readonly jobsScheduler;
|
|
506
580
|
private readonly loginHostBindings;
|
|
581
|
+
private readonly loginHostDiscovery;
|
|
507
582
|
private readonly zitadelIdTokenUserinfo;
|
|
508
583
|
private readonly broker;
|
|
509
584
|
private readonly edgeWorkers;
|
|
@@ -511,10 +586,13 @@ export declare class FoundryPlanApply implements PlanApply {
|
|
|
511
586
|
private readonly hostFiles;
|
|
512
587
|
private readonly hostAi;
|
|
513
588
|
private readonly hostEdge;
|
|
589
|
+
private readonly hostOwnServices;
|
|
590
|
+
private readonly sfuPortsOpen;
|
|
514
591
|
private readonly onlySteps;
|
|
515
592
|
private readonly now;
|
|
516
593
|
private readonly leaseTtlMs;
|
|
517
594
|
private readonly appliedVersions;
|
|
595
|
+
private readonly backupGate;
|
|
518
596
|
private readonly platformDeps;
|
|
519
597
|
private readonly runner;
|
|
520
598
|
constructor(options: FoundryPlanApplyOptions);
|
|
@@ -653,6 +731,8 @@ export declare class FoundryPlanApply implements PlanApply {
|
|
|
653
731
|
private refusePlatform;
|
|
654
732
|
private refuseDestroy;
|
|
655
733
|
private runSteps;
|
|
734
|
+
/** An own org with calls on: the host's answer about its slot's ports. A failure or a slow answer is "not open". */
|
|
735
|
+
private readSfuPortsOpen;
|
|
656
736
|
private computeFreshPlan;
|
|
657
737
|
/**
|
|
658
738
|
* An entry may not take a name foundry owns (`locked-hostnames.ts`). The
|
|
@@ -669,12 +749,31 @@ export declare class FoundryPlanApply implements PlanApply {
|
|
|
669
749
|
* org step that needs one is blocked.
|
|
670
750
|
*/
|
|
671
751
|
private orgPlatformReadiness;
|
|
752
|
+
/** An own org's Lettermint key is held. A resolver that throws counts as not held. */
|
|
753
|
+
private ownMailKeyHeld;
|
|
754
|
+
/** Which of an own org's undelivered-secret refs hold a value now, so a rerun resumes instead of blocking. */
|
|
755
|
+
private ownResumableRefs;
|
|
756
|
+
/**
|
|
757
|
+
* The host port an own org's Stalwart publishes for the relay: the one it already has recorded, else
|
|
758
|
+
* the lowest port in the range no other org has recorded. Two plans made at the same moment could pick
|
|
759
|
+
* the same port; the second Stalwart then fails to bind it and the step fails, with nothing recorded.
|
|
760
|
+
*/
|
|
761
|
+
private ownMailSmtpPort;
|
|
762
|
+
/** The relay is declared and has recorded the routes it last applied: port 25 reaches the mail servers. */
|
|
763
|
+
private mailRelayApplied;
|
|
764
|
+
/**
|
|
765
|
+
* The relay's last applied routes (a fingerprint, recorded when it deploys) are the routes the
|
|
766
|
+
* orgs' recorded SMTP addresses give now, and one of them is this org's mail domain.
|
|
767
|
+
*/
|
|
768
|
+
private mailRelayHoldsRoute;
|
|
769
|
+
private sharedPlatformReadiness;
|
|
672
770
|
/**
|
|
673
771
|
* #177: `AgoraHostValues` from the applied platform, the same
|
|
674
772
|
* `platformDeps` {@link orgPlatformReadiness} reads -- but only when the
|
|
675
773
|
* host supplies no `agora` port of its own. An explicit `agora` (#164)
|
|
676
774
|
* always wins, unchanged, so a host that already has its own values
|
|
677
|
-
* (perhaps not yet wired to this platform) keeps them.
|
|
775
|
+
* (perhaps not yet wired to this platform) keeps them. An org that owns its
|
|
776
|
+
* services never takes the host's values: they belong to the shared services.
|
|
678
777
|
*/
|
|
679
778
|
private orgAgoraHost;
|
|
680
779
|
/**
|
|
@@ -690,6 +789,18 @@ export declare class FoundryPlanApply implements PlanApply {
|
|
|
690
789
|
/** #167: `FoundryPlanApplyOptions.platform`, or a plain `Error` naming what's missing. */
|
|
691
790
|
private requirePlatformDeps;
|
|
692
791
|
private computeFreshPlatformPlan;
|
|
792
|
+
/** The owner company's own sign-in and files host, read from its applied ids; `null` members until applied. */
|
|
793
|
+
private platformOwnerInput;
|
|
794
|
+
/**
|
|
795
|
+
* What the shared mail relay needs from the orgs. A route exists for an org
|
|
796
|
+
* that owns its services, serves mail on its own domain and has recorded its
|
|
797
|
+
* mail server's SMTP address (`mail_smtp_internal`, written by the company
|
|
798
|
+
* mail step), on its mail domain: its own domain, or `<slug>.oikos.host`.
|
|
799
|
+
* There is no platform mail server: a domain with no route is refused. An org
|
|
800
|
+
* that owns its services but has no mail server yet has no route, so the
|
|
801
|
+
* relay refuses its mail until the server exists.
|
|
802
|
+
*/
|
|
803
|
+
private mailRelayInputs;
|
|
693
804
|
private sfuBlockedReason;
|
|
694
805
|
/**
|
|
695
806
|
* {@link runSteps}, for the platform. The ids the steps return are recorded
|