@wtfalch/foundry 0.19.1 → 0.21.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.d.ts +13 -0
- package/dist/bootstrap/declare.js +31 -7
- package/dist/bootstrap/versions.d.ts +1 -0
- package/dist/index.d.ts +7 -3
- package/dist/index.js +4 -2
- package/dist/migrations/0046_platform_hc.sql +19 -0
- package/dist/plan/cloudflared-connector.d.ts +3 -2
- package/dist/plan/cloudflared-connector.js +2 -2
- package/dist/plan/deploy-health.js +5 -1
- package/dist/plan/diff.js +27 -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 +146 -143
- package/dist/plan/executor.js +370 -115
- package/dist/plan/health-check-settings.d.ts +47 -0
- package/dist/plan/health-check-settings.js +47 -0
- package/dist/plan/health-targets.d.ts +86 -0
- package/dist/plan/health-targets.js +344 -0
- package/dist/plan/index.d.ts +17 -3
- package/dist/plan/index.js +9 -2
- package/dist/plan/live.d.ts +12 -1
- package/dist/plan/plan-apply.d.ts +4 -0
- package/dist/plan/plan-apply.js +5 -1
- package/dist/plan/platform-diff.d.ts +127 -0
- package/dist/plan/platform-diff.js +304 -8
- package/dist/plan/platform-providers.d.ts +202 -7
- package/dist/plan/platform-providers.js +545 -79
- package/dist/plan/providers-readers.d.ts +23 -1
- package/dist/plan/providers-readers.js +61 -1
- package/dist/plan/rollout.d.ts +9 -8
- package/dist/platform.d.ts +14 -1
- package/dist/platform.js +1 -0
- package/dist/store/postgres.js +8 -0
- package/dist/store/schema.d.ts +51 -0
- package/dist/store/schema.js +4 -0
- package/package.json +2 -1
- package/versions.json +2 -1
|
@@ -3,6 +3,9 @@ import type { DeclaredOrg } from '../org.js';
|
|
|
3
3
|
import type { DeclaredPlatform } from '../platform.js';
|
|
4
4
|
import type { DeclaredOrgStore, PlatformStore } from '../store.js';
|
|
5
5
|
import type { StepAction } from './diff.js';
|
|
6
|
+
import type { AccessPort } from './edge-access.js';
|
|
7
|
+
import type { DnsPort } from './edge-dns.js';
|
|
8
|
+
import type { TunnelPort } from './edge-tunnels.js';
|
|
6
9
|
import type { CloudflarePort, CoolifyPort, FoundryStepExecutorPorts, ZitadelPort } from './executor.js';
|
|
7
10
|
import type { LiveResource, PlatformCloudflareReader, PlatformCoolifyReader } from './live.js';
|
|
8
11
|
import type { PlatformStepExecutionContext, PlatformStepExecutor } from './plan-apply.js';
|
|
@@ -14,6 +17,34 @@ import { type CallsSfuPlatformDeps } from './platform-sfu.js';
|
|
|
14
17
|
* positions, so every #167-era caller of {@link FoundryPlatformStepExecutor}
|
|
15
18
|
* (zone only) keeps compiling with this third argument simply omitted.
|
|
16
19
|
*/
|
|
20
|
+
/**
|
|
21
|
+
* F4.dns: builds the {@link DnsPort} a platform step writes its records
|
|
22
|
+
* through, given the address edge answers at on the Coolify network
|
|
23
|
+
* ({@link PLATFORM_EDGE_INTERNAL_URL_SCOPE_KEY}). The host builds it with
|
|
24
|
+
* `createEdgeDns(createEdgeClient({ baseUrl, key }).platform.dns)`, `key`
|
|
25
|
+
* being the platform key held under {@link PLATFORM_EDGE_KEY_REF}.
|
|
26
|
+
*/
|
|
27
|
+
export type PlatformDnsFactory = (edgeUrl: string) => DnsPort | Promise<DnsPort>;
|
|
28
|
+
/**
|
|
29
|
+
* F4.tunnels: builds the {@link TunnelPort} a platform step creates, configures
|
|
30
|
+
* and reads the connector token of its tunnel through, given the same address
|
|
31
|
+
* as {@link PlatformDnsFactory}. The host builds it with
|
|
32
|
+
* `createEdgeTunnels(createEdgeClient({ baseUrl, key }).platform.tunnels)`,
|
|
33
|
+
* `key` being the platform key held under {@link PLATFORM_EDGE_KEY_REF}.
|
|
34
|
+
*/
|
|
35
|
+
export type PlatformTunnelsFactory = (edgeUrl: string) => PlatformTunnelPort | Promise<PlatformTunnelPort>;
|
|
36
|
+
/** The tunnel calls a platform step makes; it never deletes one. */
|
|
37
|
+
export type PlatformTunnelPort = Pick<TunnelPort, 'findTunnelByName' | 'createTunnel' | 'putTunnelConfig' | 'getTunnelToken'>;
|
|
38
|
+
/**
|
|
39
|
+
* F4.access: builds the {@link AccessPort} a platform step finds, creates and
|
|
40
|
+
* deletes its Access app through, given the same address as
|
|
41
|
+
* {@link PlatformDnsFactory}. The host builds it with
|
|
42
|
+
* `createEdgeAccess(createEdgeClient({ baseUrl, key }).platform.access)`,
|
|
43
|
+
* `key` being the platform key held under {@link PLATFORM_EDGE_KEY_REF}.
|
|
44
|
+
*/
|
|
45
|
+
export type PlatformAccessFactory = (edgeUrl: string) => PlatformAccessPort | Promise<PlatformAccessPort>;
|
|
46
|
+
/** The Access calls a platform step makes. */
|
|
47
|
+
export type PlatformAccessPort = Pick<AccessPort, 'listAccessApps' | 'createAccessApp' | 'listAccessPolicies' | 'createAccessPolicy' | 'deleteAccessApp'>;
|
|
17
48
|
/**
|
|
18
49
|
* The SMTP call `provisionPlatformAuth` makes on the instance's ZITADEL.
|
|
19
50
|
* `@wtfalch/providers` gains it in 0.7.2; stated here so foundry compiles
|
|
@@ -35,9 +66,11 @@ export interface PlatformZitadelSmtp {
|
|
|
35
66
|
}
|
|
36
67
|
export interface PlatformAuthDeps {
|
|
37
68
|
coolify: Pick<CoolifyPort, 'findProjectByName' | 'createProject' | 'findOrCreateEnvironment' | 'listServers' | 'findApplicationByName' | 'getApplication' | 'requireGithubApp' | 'createGithubAppApplication' | 'createDockerImageApplication' | 'createDockerfileApplication' | 'createPostgresDatabase' | 'getDatabase' | 'bulkUpsertEnvs' | 'listEnvs' | 'updateApplication' | 'deploy' | 'raw'>;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
69
|
+
/** F4.dns: every DNS record call, through edge at its recorded address (see {@link PlatformDnsFactory}). */
|
|
70
|
+
dns: PlatformDnsFactory;
|
|
71
|
+
/** F4.tunnels: every tunnel call, through edge at its recorded address (see {@link PlatformTunnelsFactory}). */
|
|
72
|
+
tunnels: PlatformTunnelsFactory;
|
|
73
|
+
cloudflare: Pick<CloudflarePort, 'findZoneByApex'>;
|
|
41
74
|
/** Must target the platform's own new instance (its issuer), or `zitadelForInstance` must be set. */
|
|
42
75
|
zitadel: Pick<ZitadelPort, 'findOrCreateOrg' | 'mintAdminMachineUser' | 'mintLoginClientMachineUser' | 'listLoginClientPats' | 'inviteOrgOwner'> & PlatformZitadelSmtp;
|
|
43
76
|
/**
|
|
@@ -149,6 +182,18 @@ export interface FoundryPlatformStepExecutorOptions {
|
|
|
149
182
|
* planned.
|
|
150
183
|
*/
|
|
151
184
|
backup?: PlatformBackupDeps;
|
|
185
|
+
/**
|
|
186
|
+
* #540: dependencies for `coolify:app` keyed {@link PLATFORM_HC_KEY} --
|
|
187
|
+
* optional for the same reason as `auth`; a host that declares
|
|
188
|
+
* `platform.hc.enabled` without it gets the step reported as blocked.
|
|
189
|
+
*/
|
|
190
|
+
hc?: PlatformHcDeps;
|
|
191
|
+
/**
|
|
192
|
+
* #540: dependencies for the `heartbeats:reconcile` step -- optional for
|
|
193
|
+
* the same reason as `auth`; `hc` applied with this unset gets the
|
|
194
|
+
* heartbeat checks reported as blocked rather than planned.
|
|
195
|
+
*/
|
|
196
|
+
heartbeats?: PlatformHeartbeatsDeps;
|
|
152
197
|
}
|
|
153
198
|
/**
|
|
154
199
|
* The real {@link PlatformStepExecutor} for the two steps built so far:
|
|
@@ -228,6 +273,8 @@ export declare class FoundryPlatformStepExecutor implements PlatformStepExecutor
|
|
|
228
273
|
private readonly edge?;
|
|
229
274
|
private readonly archon?;
|
|
230
275
|
private readonly backup?;
|
|
276
|
+
private readonly hc?;
|
|
277
|
+
private readonly heartbeats?;
|
|
231
278
|
constructor(cloudflare: Pick<CloudflarePort, 'findZoneByApex' | 'createZone'>, declaredOrgStore: Pick<DeclaredOrgStore, 'findOrgIdForZone' | 'list' | 'get'>, options?: FoundryPlatformStepExecutorOptions);
|
|
232
279
|
blockedSteps(platform: DeclaredPlatform, recordedIds?: Readonly<Record<string, string>>): Promise<{
|
|
233
280
|
auth?: string;
|
|
@@ -240,6 +287,9 @@ export declare class FoundryPlatformStepExecutor implements PlatformStepExecutor
|
|
|
240
287
|
archon?: string;
|
|
241
288
|
mail?: string;
|
|
242
289
|
backupExecutorMissing?: boolean;
|
|
290
|
+
hc?: string;
|
|
291
|
+
heartbeatsExecutorMissing?: boolean;
|
|
292
|
+
heartbeatsApiKeyMissing?: boolean;
|
|
243
293
|
}>;
|
|
244
294
|
/**
|
|
245
295
|
* Whether Archon's live Access policy lets through anyone but the declared
|
|
@@ -377,7 +427,11 @@ export declare function defaultStalwartForInstance(input: {
|
|
|
377
427
|
*/
|
|
378
428
|
export interface PlatformMailDeps {
|
|
379
429
|
coolify: Pick<CoolifyPort, 'findProjectByName' | 'createProject' | 'findOrCreateEnvironment' | 'listServers' | 'findApplicationByName' | 'getApplication' | 'createDockerImageApplication' | 'createDockerfileApplication' | 'raw' | 'listStorages' | 'createPersistentStorage' | 'bulkUpsertEnvs' | 'listEnvs' | 'updateApplication' | 'deploy' | 'deployAndWait'>;
|
|
380
|
-
|
|
430
|
+
/** F4.dns: every DNS record call, through edge at its recorded address (see {@link PlatformDnsFactory}). */
|
|
431
|
+
dns: PlatformDnsFactory;
|
|
432
|
+
/** F4.tunnels: every tunnel call, through edge at its recorded address (see {@link PlatformTunnelsFactory}). */
|
|
433
|
+
tunnels: PlatformTunnelsFactory;
|
|
434
|
+
cloudflare: Pick<CloudflarePort, 'findZoneByApex'>;
|
|
381
435
|
/**
|
|
382
436
|
* Must target the platform's own ZITADEL instance, or `zitadelForInstance`
|
|
383
437
|
* must be set. `provisionPlatformMail` finds or creates a `mail` project
|
|
@@ -421,7 +475,13 @@ export interface PlatformMailRuntime {
|
|
|
421
475
|
*/
|
|
422
476
|
export interface PlatformArchonDeps {
|
|
423
477
|
coolify: Pick<CoolifyPort, 'findProjectByName' | 'createProject' | 'findOrCreateEnvironment' | 'listServers' | 'findApplicationByName' | 'getApplication' | 'requireGithubApp' | 'createGithubAppApplication' | 'createDockerfileApplication' | 'listDatabases' | 'createPostgresDatabase' | 'getDatabase' | 'raw' | 'bulkUpsertEnvs' | 'listEnvs' | 'deleteEnv' | 'updateApplication' | 'deploy' | 'deployAndWait'>;
|
|
424
|
-
|
|
478
|
+
/** F4.dns: every DNS record call, through edge at its recorded address (see {@link PlatformDnsFactory}). */
|
|
479
|
+
dns: PlatformDnsFactory;
|
|
480
|
+
/** F4.tunnels: every tunnel call, through edge at its recorded address (see {@link PlatformTunnelsFactory}). */
|
|
481
|
+
tunnels: PlatformTunnelsFactory;
|
|
482
|
+
/** F4.access: every Access app call, through edge at its recorded address (see {@link PlatformAccessFactory}). */
|
|
483
|
+
access: PlatformAccessFactory;
|
|
484
|
+
cloudflare: Pick<CloudflarePort, 'findZoneByApex'>;
|
|
425
485
|
/** Must target the platform's own ZITADEL instance, or `zitadelForInstance` must be set. */
|
|
426
486
|
zitadel: Pick<ZitadelPort, 'findOrCreateOrg' | 'findOrCreateProject' | 'createOidcApp' | 'listApps' | 'deleteApp'>;
|
|
427
487
|
/** {@link PlatformAuthDeps.zitadelForInstance}, for Archon's sign-in client. */
|
|
@@ -633,6 +693,17 @@ export interface PlatformAiRuntime {
|
|
|
633
693
|
migrateIntervalMs?: number;
|
|
634
694
|
/** Tries at handing a secret to the host (default 3): the management key and the signing key both use this. */
|
|
635
695
|
deliverAttempts?: number;
|
|
696
|
+
/**
|
|
697
|
+
* #543: injected for tests, against a fake runner host -- the runner's own
|
|
698
|
+
* deploy wait now does a `'ready'` probe of its `/api/health`, the same
|
|
699
|
+
* `DeployHealthRuntime.fetch` field `provisionPlatformMail`/
|
|
700
|
+
* `provisionPlatformArchon` already take. Defaults to the global `fetch`.
|
|
701
|
+
*/
|
|
702
|
+
fetch?: typeof fetch;
|
|
703
|
+
/** How long to wait for the runner to answer ready (default 5 minutes). */
|
|
704
|
+
readyTimeoutMs?: number;
|
|
705
|
+
/** Pause between readiness polls (default 5 seconds). */
|
|
706
|
+
readyIntervalMs?: number;
|
|
636
707
|
}
|
|
637
708
|
/** The ref the AI management key is delivered under; the host's `AiOrganisationPort` adapter reads it back. */
|
|
638
709
|
export declare const PLATFORM_AI_MANAGEMENT_KEY_REF = "ai:platform:managementKey";
|
|
@@ -806,6 +877,17 @@ export interface PlatformFilesRuntime {
|
|
|
806
877
|
migrateIntervalMs?: number;
|
|
807
878
|
/** Tries at handing each secret to the host (default 3). */
|
|
808
879
|
deliverAttempts?: number;
|
|
880
|
+
/**
|
|
881
|
+
* #543: injected for tests, against a fake runner host -- the runner's own
|
|
882
|
+
* deploy wait now does a `'ready'` probe of its `/api/health`, same as
|
|
883
|
+
* `provisionPlatformMail`/`provisionPlatformArchon` already take. Defaults
|
|
884
|
+
* to the global `fetch`.
|
|
885
|
+
*/
|
|
886
|
+
fetch?: typeof fetch;
|
|
887
|
+
/** How long to wait for the runner to answer ready (default 5 minutes). */
|
|
888
|
+
readyTimeoutMs?: number;
|
|
889
|
+
/** Pause between readiness polls (default 5 seconds). */
|
|
890
|
+
readyIntervalMs?: number;
|
|
809
891
|
}
|
|
810
892
|
/**
|
|
811
893
|
* #171: how the host signs a files management request with the key this step
|
|
@@ -834,7 +916,11 @@ export interface PlatformEdgeRuntime {
|
|
|
834
916
|
}
|
|
835
917
|
export interface PlatformEdgeDeps {
|
|
836
918
|
coolify: Pick<CoolifyPort, 'findProjectByName' | 'createProject' | 'findOrCreateEnvironment' | 'listServers' | 'findApplicationByName' | 'getApplication' | 'requireGithubApp' | 'createGithubAppApplication' | 'createDockerfileApplication' | 'listDatabases' | 'createPostgresDatabase' | 'getDatabase' | 'bulkUpsertEnvs' | 'listEnvs' | 'updateApplication' | 'deploy' | 'deployAndWait' | 'raw'>;
|
|
837
|
-
|
|
919
|
+
/** F4.dns: every DNS record call, through edge at its recorded address (see {@link PlatformDnsFactory}). */
|
|
920
|
+
dns: PlatformDnsFactory;
|
|
921
|
+
/** F4.tunnels: every tunnel call, through edge at its recorded address (see {@link PlatformTunnelsFactory}). */
|
|
922
|
+
tunnels: PlatformTunnelsFactory;
|
|
923
|
+
cloudflare: Pick<CloudflarePort, 'findZoneByApex'>;
|
|
838
924
|
deliverSecret: FoundryStepExecutorPorts['deliverSecret'];
|
|
839
925
|
/**
|
|
840
926
|
* Reads the edge service's own Cloudflare API token back, held under
|
|
@@ -856,6 +942,30 @@ export interface PlatformEdgeDeps {
|
|
|
856
942
|
/** Clock, sleep and fetch for the readiness wait and delivery retries; real ones by default. */
|
|
857
943
|
runtime?: PlatformEdgeRuntime;
|
|
858
944
|
}
|
|
945
|
+
export interface PlatformHcRuntime {
|
|
946
|
+
fetch?: typeof fetch;
|
|
947
|
+
sleep?: (ms: number) => Promise<void>;
|
|
948
|
+
now?: () => number;
|
|
949
|
+
readyTimeoutMs?: number;
|
|
950
|
+
readyIntervalMs?: number;
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* #540: dependencies for `coolify:app` keyed {@link PLATFORM_HC_KEY} -- the
|
|
954
|
+
* {@link PlatformEdgeDeps} bundling shape minus a Cloudflare token of its
|
|
955
|
+
* own: `hc` writes its tunnel and DNS record through edge the same way
|
|
956
|
+
* mail and archon do, and needs no `heldKeys`/`deliverSecret` at all --
|
|
957
|
+
* `SECRET_KEY` (Django's session-signing key) is generated once by
|
|
958
|
+
* {@link provisionPlatformHc} itself and never leaves this Coolify app.
|
|
959
|
+
*/
|
|
960
|
+
export interface PlatformHcDeps {
|
|
961
|
+
coolify: Pick<CoolifyPort, 'findProjectByName' | 'createProject' | 'findOrCreateEnvironment' | 'listServers' | 'findApplicationByName' | 'getApplication' | 'createDockerImageApplication' | 'createDockerfileApplication' | 'listDatabases' | 'createPostgresDatabase' | 'getDatabase' | 'bulkUpsertEnvs' | 'listEnvs' | 'updateApplication' | 'deploy' | 'deployAndWait' | 'raw'>;
|
|
962
|
+
/** F4.dns: every DNS record call, through edge at its recorded address (see {@link PlatformDnsFactory}). */
|
|
963
|
+
dns: PlatformDnsFactory;
|
|
964
|
+
/** F4.tunnels: every tunnel call, through edge at its recorded address (see {@link PlatformTunnelsFactory}). */
|
|
965
|
+
tunnels: PlatformTunnelsFactory;
|
|
966
|
+
/** Clock, sleep and fetch for the readiness wait; real ones by default. */
|
|
967
|
+
runtime?: PlatformHcRuntime;
|
|
968
|
+
}
|
|
859
969
|
/**
|
|
860
970
|
* #174: everything {@link provisionPlatformIntegrations} needs beyond
|
|
861
971
|
* {@link PlatformStepExecutionContext} -- a bundle, like
|
|
@@ -930,6 +1040,91 @@ export interface PlatformBackupDeps {
|
|
|
930
1040
|
/** A cron string or one of Coolify's names (`daily`, `weekly`, ...). Default `'daily'`. */
|
|
931
1041
|
frequency?: string;
|
|
932
1042
|
}
|
|
1043
|
+
/** One check as self-hosted Healthchecks' own Management API reports it. */
|
|
1044
|
+
export interface HeartbeatCheck {
|
|
1045
|
+
id: string;
|
|
1046
|
+
name: string;
|
|
1047
|
+
pingUrl: string;
|
|
1048
|
+
}
|
|
1049
|
+
/**
|
|
1050
|
+
* #540: the heartbeat server's own Management API -- a project API key in a
|
|
1051
|
+
* header, `POST /api/v3/checks/` with `name`/`unique: ['name']` to upsert a
|
|
1052
|
+
* check idempotently by name, `GET /api/v3/checks/` to list every one,
|
|
1053
|
+
* `DELETE /api/v3/checks/<uuid>` to remove one -- restated here as the port
|
|
1054
|
+
* `provisionPlatformHeartbeats` plans and provisions against, never a real
|
|
1055
|
+
* HTTP client: this package never connects itself (`README.md`), so the
|
|
1056
|
+
* host implements this against its own running `hc` instance and its own
|
|
1057
|
+
* project API key, the same seam every other real I/O in this file crosses.
|
|
1058
|
+
*/
|
|
1059
|
+
export interface HeartbeatsPort {
|
|
1060
|
+
listChecks(): Promise<HeartbeatCheck[]>;
|
|
1061
|
+
/** Creates a check named `name`, or returns the existing one of that name -- the Management API's own upsert. */
|
|
1062
|
+
createCheck(name: string): Promise<HeartbeatCheck>;
|
|
1063
|
+
/**
|
|
1064
|
+
* #540 round 2: pauses a check (`POST /api/v3/checks/<uuid>/pause/`) so it
|
|
1065
|
+
* reads "paused" rather than "down" while nothing actually pings it yet --
|
|
1066
|
+
* both the uptime Worker's own check and every backup check (see this
|
|
1067
|
+
* file's own doc comment on `PlatformHeartbeatsDeps` for why). Sending a
|
|
1068
|
+
* check a ping resumes it automatically (Healthchecks' own documented
|
|
1069
|
+
* behaviour), so foundry never has to call the reverse of this: once a
|
|
1070
|
+
* real pinger exists, the check un-pauses itself on its first ping.
|
|
1071
|
+
* `provisionPlatformHeartbeats` calls this exactly once, right after
|
|
1072
|
+
* `createCheck`, and never again on a check it merely finds already live
|
|
1073
|
+
* -- a check already being pinged, or one an operator unpaused by hand,
|
|
1074
|
+
* must never be paused again by a reconcile run.
|
|
1075
|
+
*/
|
|
1076
|
+
pauseCheck(id: string): Promise<void>;
|
|
1077
|
+
deleteCheck(id: string): Promise<void>;
|
|
1078
|
+
}
|
|
1079
|
+
/**
|
|
1080
|
+
* #540: dependencies for the `heartbeats:reconcile` step
|
|
1081
|
+
* (`platform-diff.ts`'s `planHeartbeats`).
|
|
1082
|
+
*
|
|
1083
|
+
* #540 round 2: `resolve` is new. It guards only the *delete* half of
|
|
1084
|
+
* {@link provisionPlatformHeartbeats} -- a live check is only ever deleted
|
|
1085
|
+
* when foundry itself holds a ping URL for its exact name
|
|
1086
|
+
* (`heartbeatCheckUrlRef`, `platform-diff.ts`), proof this run or an
|
|
1087
|
+
* earlier one created it. Without that guard, a hand-made check sharing
|
|
1088
|
+
* foundry's naming scheme (an operator's own `uptime-worker`, say, made
|
|
1089
|
+
* before foundry ever ran) could be deleted the moment that name was not
|
|
1090
|
+
* yet desired -- a backup service with no recorded database yet is in
|
|
1091
|
+
* `provisionPlatformHeartbeats`'s own `ownedNames` but not `desiredSet`.
|
|
1092
|
+
* `resolve` is never consulted on the *create* half: a check already live
|
|
1093
|
+
* under a desired name is always treated as foundry's, the way it always
|
|
1094
|
+
* was, and (the round-2 fix for a second bug) its ping address is now
|
|
1095
|
+
* always re-saved rather than only on the create that first makes it live
|
|
1096
|
+
* -- see `put`'s own doc comment below for why that half carries no
|
|
1097
|
+
* adoption risk the way deleting does.
|
|
1098
|
+
*
|
|
1099
|
+
* `put` records a check's own ping URL -- on every create, and (new this
|
|
1100
|
+
* round) on every run that finds a desired check already live, not only
|
|
1101
|
+
* the run that created it. Before this fix, a save that failed once, or a
|
|
1102
|
+
* run that died between create and save, or a host that wired `heldKeys`
|
|
1103
|
+
* only after the original create, lost that address for good: the next run
|
|
1104
|
+
* found the check already live and never saved anything, despite this very
|
|
1105
|
+
* doc comment already claiming the step "can simply ask the live check
|
|
1106
|
+
* again." Required whenever an address actually needs saving -- optional
|
|
1107
|
+
* only so a host without one still compiles, in which case nothing here
|
|
1108
|
+
* can ever be created or re-confirmed, and the step throws rather than
|
|
1109
|
+
* silently drop an address: CLAUDE.md platform-step rule 1 is written for a
|
|
1110
|
+
* minted credential, not a ping URL (a lost one is simply re-asked from the
|
|
1111
|
+
* live check, never re-minted), but the rule's "never silently lose it"
|
|
1112
|
+
* half applies just as well here.
|
|
1113
|
+
*
|
|
1114
|
+
* Both fields are optional only so a host without either still compiles;
|
|
1115
|
+
* while `PlatformHeartbeatsDeps` is wired at all, `FoundryPlatformStepExecutor
|
|
1116
|
+
* .blockedSteps`'s own `heartbeatsApiKeyMissing` check blocks the step
|
|
1117
|
+
* until a project API key is held under {@link PLATFORM_HC_API_KEY_REF}, so
|
|
1118
|
+
* the live Healthchecks calls below are never reached with nothing behind
|
|
1119
|
+
* them.
|
|
1120
|
+
*/
|
|
1121
|
+
export interface PlatformHeartbeatsDeps {
|
|
1122
|
+
heartbeats: HeartbeatsPort;
|
|
1123
|
+
heldKeys?: {
|
|
1124
|
+
resolve(ref: string): Promise<string | null>;
|
|
1125
|
+
put?(ref: string, value: string): Promise<void>;
|
|
1126
|
+
};
|
|
1127
|
+
}
|
|
933
1128
|
/**
|
|
934
1129
|
* The real {@link PlatformCloudflareReader}: the platform's apex zone as
|
|
935
1130
|
* `planApexZone` expects to see it, and nothing else. The zone's DNS records
|
|
@@ -959,7 +1154,7 @@ export declare class ProvidersPlatformCloudflareReader implements PlatformCloudf
|
|
|
959
1154
|
export declare class ProvidersPlatformCoolifyReader implements PlatformCoolifyReader {
|
|
960
1155
|
private readonly client;
|
|
961
1156
|
private readonly platformStore;
|
|
962
|
-
constructor(client: Pick<CoolifyPort, 'getApplication' | 'getDatabase' | 'listDatabaseBackups'>, platformStore: Pick<PlatformStore, 'getServiceIds'>);
|
|
1157
|
+
constructor(client: Pick<CoolifyPort, 'getApplication' | 'getDatabase' | 'listDatabaseBackups' | 'raw'>, platformStore: Pick<PlatformStore, 'getServiceIds'>);
|
|
963
1158
|
listResources(): Promise<LiveResource[]>;
|
|
964
1159
|
}
|
|
965
1160
|
/** Scope id (no `:`): the share domains the runner's Coolify `domains` was last set to, comma-joined. */
|