@wtfalch/foundry 0.24.0 → 0.25.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 +4 -0
- package/dist/index.d.ts +1 -1
- package/dist/migrations/0049_platform_uptime.sql +11 -0
- package/dist/plan/edge-access.d.ts +14 -0
- package/dist/plan/edge-access.js +21 -1
- package/dist/plan/edge-workers.d.ts +16 -1
- package/dist/plan/edge-workers.js +8 -0
- package/dist/plan/executor.d.ts +14 -1
- package/dist/plan/index.d.ts +2 -1
- package/dist/plan/plan-apply.d.ts +4 -0
- package/dist/plan/platform-diff.d.ts +88 -0
- package/dist/plan/platform-diff.js +210 -0
- package/dist/plan/platform-providers.d.ts +23 -0
- package/dist/plan/platform-providers.js +162 -13
- package/dist/plan/uptime-worker.d.ts +50 -0
- package/dist/plan/uptime-worker.js +229 -0
- package/dist/platform.d.ts +18 -0
- package/dist/platform.js +14 -0
- package/dist/store/postgres.js +4 -0
- package/dist/store/schema.d.ts +51 -0
- package/dist/store/schema.js +4 -0
- package/package.json +2 -2
|
@@ -146,5 +146,9 @@ export function declaredPlatformFor(apex, serverIpv4) {
|
|
|
146
146
|
apex,
|
|
147
147
|
...(serverIpv4 ? { serverIpv4 } : {}),
|
|
148
148
|
...services,
|
|
149
|
+
// Off, like `sfu`: the pin is an Archon release tag and digest, which exist
|
|
150
|
+
// only after Archon runs. Declared on with no pin, it would be reported
|
|
151
|
+
// blocked and the bootstrap would never be complete.
|
|
152
|
+
uptime: { enabled: false, tag: '', sha256: '' },
|
|
149
153
|
};
|
|
150
154
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -41,7 +41,7 @@ export { checkMailIdentityAudience } from './plan/index.js';
|
|
|
41
41
|
export type { MailAllowlistApp } from './plan/index.js';
|
|
42
42
|
export { checkMailAppsAllowlist } from './plan/index.js';
|
|
43
43
|
export { FoundryPlatformStepExecutor, ProvidersPlatformCloudflareReader, ProvidersPlatformCoolifyReader, createZitadelAdminMinter, createZitadelProjectRoles, PLATFORM_AUTH_KEY, PLATFORM_MAIL_KEY, PLATFORM_MAIL_ADMIN_KEY_DESCRIPTION, } from './plan/index.js';
|
|
44
|
-
export type { PlatformAuthDeps, PlatformAuthRuntime, PlatformFilesDeps, PlatformFilesRuntime, PlatformEdgeDeps, PlatformDnsFactory, PlatformAccessFactory, PlatformAccessPort, PlatformTunnelsFactory, PlatformEdgeRuntime, } from './plan/index.js';
|
|
44
|
+
export type { PlatformAuthDeps, PlatformAuthRuntime, PlatformFilesDeps, PlatformFilesRuntime, PlatformEdgeDeps, PlatformDnsFactory, PlatformAccessFactory, PlatformUptimeAccessFactory, PlatformUptimeAccessPort, PlatformUptimeDeps, PlatformWorkersFactory, PlatformWorkersPort, PlatformAccessPort, PlatformTunnelsFactory, PlatformEdgeRuntime, } from './plan/index.js';
|
|
45
45
|
export type { PlatformMailDeps, PlatformMailAdminPort, PlatformMailRuntime, } from './plan/index.js';
|
|
46
46
|
export { FILES_INTROSPECTION_SECRET_REF, FILES_MANAGEMENT_ISSUER, FILES_MANAGEMENT_KEY_ID, FILES_MANAGEMENT_KEY_REF, PLATFORM_AUTH_INSTANCE_BEARER_REF, } from './plan/index.js';
|
|
47
47
|
export { AI_MANAGEMENT_ISSUER, AI_MANAGEMENT_KEY_ID, AI_SIGNING_KEY_REF, AiManagementKeyExistsError, createAiManagementKeyMinter, PLATFORM_AI_MANAGEMENT_KEY_REF, PLATFORM_AI_MANAGEMENT_PERMISSIONS, } from './plan/index.js';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
-- @wtfalch/foundry: the uptime Worker on the declared platform (foundry#542).
|
|
2
|
+
-- Mirrors `src/store/schema.ts`'s `foundryDeclaredPlatform` additions.
|
|
3
|
+
-- `uptime_tag` and `uptime_sha256` are the pin of the Archon release whose
|
|
4
|
+
-- built Worker foundry deploys; both empty means nothing is pinned yet, and
|
|
5
|
+
-- the planner reports that instead of planning the step. The Worker is on by
|
|
6
|
+
-- default, like every service except sfu and coolify.
|
|
7
|
+
|
|
8
|
+
alter table foundry_declared_platform
|
|
9
|
+
add column if not exists uptime_enabled boolean not null default true,
|
|
10
|
+
add column if not exists uptime_tag text not null default '',
|
|
11
|
+
add column if not exists uptime_sha256 text not null default '';
|
|
@@ -29,6 +29,7 @@ export interface AccessPort {
|
|
|
29
29
|
listAccessPolicies(appId: string): Promise<Array<{
|
|
30
30
|
id: string;
|
|
31
31
|
name: string;
|
|
32
|
+
decision?: AccessDecision;
|
|
32
33
|
include?: AccessPolicyRule[];
|
|
33
34
|
}>>;
|
|
34
35
|
createAccessPolicy(appId: string, policy: {
|
|
@@ -46,6 +47,19 @@ export interface AccessPort {
|
|
|
46
47
|
findServiceTokenByName(name: string): Promise<{
|
|
47
48
|
id: string;
|
|
48
49
|
} | undefined>;
|
|
50
|
+
/**
|
|
51
|
+
* Mints a service token through edge (edge 0.12.0). `clientId` and
|
|
52
|
+
* `clientSecret` come back once and are never returned again; a token of that
|
|
53
|
+
* name, made by edge or not, answers `EdgeApiError` `conflict`.
|
|
54
|
+
*/
|
|
55
|
+
createServiceToken(name: string): Promise<{
|
|
56
|
+
id: string;
|
|
57
|
+
name: string;
|
|
58
|
+
clientId: string;
|
|
59
|
+
clientSecret: string;
|
|
60
|
+
}>;
|
|
61
|
+
/** Removes a token edge created. `not_found` for any other; a token already gone is success. */
|
|
62
|
+
removeServiceToken(id: string): Promise<void>;
|
|
49
63
|
deleteAccessApp(appId: string): Promise<void>;
|
|
50
64
|
/**
|
|
51
65
|
* A company's gate: an Access application over `hostnames`, one policy
|
package/dist/plan/edge-access.js
CHANGED
|
@@ -26,6 +26,8 @@ export function createEdgeAccess(client, options = {}) {
|
|
|
26
26
|
listPolicies: (appId) => retrying(() => client.listPolicies(appId)),
|
|
27
27
|
createPolicy: (appId, input) => retrying(() => client.createPolicy(appId, input)),
|
|
28
28
|
findServiceToken: (name) => retrying(() => client.findServiceToken(name)),
|
|
29
|
+
createServiceToken: (input) => retrying(() => client.createServiceToken(input)),
|
|
30
|
+
removeServiceToken: (id) => retrying(() => client.removeServiceToken(id)),
|
|
29
31
|
adopt: (input) => retrying(() => client.adopt(input)),
|
|
30
32
|
readiness: () => retrying(() => client.readiness()),
|
|
31
33
|
ensureProvider: (input) => retrying(() => client.ensureProvider(input)),
|
|
@@ -103,7 +105,12 @@ export function createEdgeAccess(client, options = {}) {
|
|
|
103
105
|
},
|
|
104
106
|
async listAccessPolicies(appId) {
|
|
105
107
|
const { policies } = await onApp(appId, () => access.listPolicies(appId));
|
|
106
|
-
return policies.map((p) => ({
|
|
108
|
+
return policies.map((p) => ({
|
|
109
|
+
id: p.id,
|
|
110
|
+
name: p.name,
|
|
111
|
+
decision: p.decision,
|
|
112
|
+
include: p.include,
|
|
113
|
+
}));
|
|
107
114
|
},
|
|
108
115
|
async createAccessPolicy(appId, policy) {
|
|
109
116
|
const created = await onApp(appId, () => writeSlot(() => access.createPolicy(appId, policy)));
|
|
@@ -121,6 +128,19 @@ export function createEdgeAccess(client, options = {}) {
|
|
|
121
128
|
throw error;
|
|
122
129
|
}
|
|
123
130
|
},
|
|
131
|
+
async createServiceToken(name) {
|
|
132
|
+
// Not retried on anything but `rate_limited`: a second mint would be a second token.
|
|
133
|
+
const minted = await writeSlot(() => access.createServiceToken({ name }));
|
|
134
|
+
return {
|
|
135
|
+
id: minted.token.id,
|
|
136
|
+
name: minted.token.name,
|
|
137
|
+
clientId: minted.clientId,
|
|
138
|
+
clientSecret: minted.clientSecret,
|
|
139
|
+
};
|
|
140
|
+
},
|
|
141
|
+
async removeServiceToken(id) {
|
|
142
|
+
await writeSlot(() => access.removeServiceToken(id));
|
|
143
|
+
},
|
|
124
144
|
async deleteAccessApp(appId) {
|
|
125
145
|
await onApp(appId, () => writeSlot(() => access.remove(appId)));
|
|
126
146
|
},
|
|
@@ -5,6 +5,21 @@ import type { WorkersPort } from './executor.js';
|
|
|
5
5
|
export type EdgePlatformWorkers = ReturnType<typeof createEdgeClient>['platform']['workers'];
|
|
6
6
|
/** Edge answers a first upload of a name that another upload is making, or a delete in flight, `conflict` + `busy`. */
|
|
7
7
|
export declare const EDGE_WORKERS_BUSY_ATTEMPTS = 5;
|
|
8
|
+
/**
|
|
9
|
+
* What the platform's uptime Worker step needs beyond {@link WorkersPort}
|
|
10
|
+
* (edge 0.11.0): cron triggers and write-only secrets, both for a script edge
|
|
11
|
+
* owns. A secret with a `version` edge already holds is skipped; a new
|
|
12
|
+
* `version` sets the value again. Edge never returns a value, and nothing here
|
|
13
|
+
* keeps one.
|
|
14
|
+
*/
|
|
15
|
+
export interface PlatformWorkersPort extends WorkersPort {
|
|
16
|
+
setSchedules(name: string, crons: readonly string[]): Promise<void>;
|
|
17
|
+
setSecrets(name: string, secrets: readonly {
|
|
18
|
+
name: string;
|
|
19
|
+
value: string;
|
|
20
|
+
version: string;
|
|
21
|
+
}[]): Promise<void>;
|
|
22
|
+
}
|
|
8
23
|
/** What the host supplies: fetches the artifact and returns its bytes, verified against `sha256`. Foundry never fetches. */
|
|
9
24
|
export interface EdgeWorkersOptions {
|
|
10
25
|
fetchArtifact: (url: string, sha256: string) => Promise<Uint8Array>;
|
|
@@ -28,4 +43,4 @@ export interface EdgeWorkersOptions {
|
|
|
28
43
|
* foundry refuses to overwrite is never adopted. A 404 on a delete means gone.
|
|
29
44
|
* A `rate_limited` or `busy` answer is retried.
|
|
30
45
|
*/
|
|
31
|
-
export declare function createEdgeWorkers(client: EdgePlatformWorkers, options: EdgeWorkersOptions):
|
|
46
|
+
export declare function createEdgeWorkers(client: EdgePlatformWorkers, options: EdgeWorkersOptions): PlatformWorkersPort;
|
|
@@ -149,6 +149,14 @@ export function createEdgeWorkers(client, options) {
|
|
|
149
149
|
}
|
|
150
150
|
adoptedRoutes.delete(`${zoneId}/${routeId}`);
|
|
151
151
|
},
|
|
152
|
+
async setSchedules(name, crons) {
|
|
153
|
+
await adoptScript(name);
|
|
154
|
+
await writeSlot(() => retrying(() => client.setSchedules(name, { crons: [...crons] })));
|
|
155
|
+
},
|
|
156
|
+
async setSecrets(name, secrets) {
|
|
157
|
+
await adoptScript(name);
|
|
158
|
+
await writeSlot(() => retrying(() => client.setSecrets(name, { secrets: secrets.map((s) => ({ ...s })) })));
|
|
159
|
+
},
|
|
152
160
|
async deleteScript(name) {
|
|
153
161
|
if (!(await adoptScript(name)))
|
|
154
162
|
return;
|
package/dist/plan/executor.d.ts
CHANGED
|
@@ -449,6 +449,19 @@ export interface WorkersRateLimitBinding {
|
|
|
449
449
|
period: number;
|
|
450
450
|
};
|
|
451
451
|
}
|
|
452
|
+
/** A Workers KV namespace binding (edge 0.11.0): edge reuses the namespace titled `title` or creates it, and deletes one it created with the script. */
|
|
453
|
+
export interface WorkersKvBinding {
|
|
454
|
+
type: 'kv_namespace';
|
|
455
|
+
name: string;
|
|
456
|
+
title: string;
|
|
457
|
+
}
|
|
458
|
+
/** A plain text variable binding (edge 0.11.0). Never for a secret: those go through {@link PlatformWorkersPort.setSecrets}. */
|
|
459
|
+
export interface WorkersPlainTextBinding {
|
|
460
|
+
type: 'plain_text';
|
|
461
|
+
name: string;
|
|
462
|
+
text: string;
|
|
463
|
+
}
|
|
464
|
+
export type WorkersBinding = WorkersRateLimitBinding | WorkersKvBinding | WorkersPlainTextBinding;
|
|
452
465
|
/**
|
|
453
466
|
* The Workers calls `cloudflareWorker` makes (#318, F8), served by edge's
|
|
454
467
|
* `platform.workers` through `createEdgeWorkers` (`edge-workers.ts`): deploys
|
|
@@ -484,7 +497,7 @@ export interface WorkersPort {
|
|
|
484
497
|
artifactUrl: string;
|
|
485
498
|
artifactSha256: string;
|
|
486
499
|
compatibilityDate: string;
|
|
487
|
-
bindings?:
|
|
500
|
+
bindings?: WorkersBinding[];
|
|
488
501
|
}): Promise<void>;
|
|
489
502
|
/** The script name currently bound to `pattern` in `zoneId`, or `undefined` for no route there. */
|
|
490
503
|
findRoute(zoneId: string, pattern: string): Promise<{
|
package/dist/plan/index.d.ts
CHANGED
|
@@ -46,7 +46,8 @@ export type { DnsPort, DnsRecordInput, DnsRecordSummary, EdgePlatformDns } from
|
|
|
46
46
|
export { createEdgeAccess } from './edge-access.js';
|
|
47
47
|
export type { AccessAppSummary, AccessDecision, AccessPolicyRule, AccessPort, EdgePlatformAccess, } from './edge-access.js';
|
|
48
48
|
export { createEdgeWorkers } from './edge-workers.js';
|
|
49
|
-
export type { EdgePlatformWorkers, EdgeWorkersOptions } from './edge-workers.js';
|
|
49
|
+
export type { EdgePlatformWorkers, EdgeWorkersOptions, PlatformWorkersPort, } from './edge-workers.js';
|
|
50
|
+
export type { PlatformUptimeAccessFactory, PlatformUptimeAccessPort, PlatformUptimeDeps, PlatformWorkersFactory, } from './uptime-worker.js';
|
|
50
51
|
export { createEdgeRateLimits } from './edge-ratelimits.js';
|
|
51
52
|
export type { EdgePlatformRateLimits, EdgeRateLimitsOptions, RateLimitPort, RateLimitRuleInput, } from './edge-ratelimits.js';
|
|
52
53
|
export { createEdgeZones } from './edge-zones.js';
|
|
@@ -224,6 +224,10 @@ export interface PlatformStepExecutor {
|
|
|
224
224
|
hc?: string;
|
|
225
225
|
/** #540: see `PlanPlatformOptions.blocked.heartbeatsExecutorMissing` (`platform-diff.ts`). */
|
|
226
226
|
heartbeatsExecutorMissing?: boolean;
|
|
227
|
+
/** foundry#542: see `PlanPlatformOptions.blocked.uptime` (`platform-diff.ts`). */
|
|
228
|
+
uptime?: string;
|
|
229
|
+
/** foundry#542: see `PlanPlatformOptions.blocked.uptimeSecretsHeld` (`platform-diff.ts`). */
|
|
230
|
+
uptimeSecretsHeld?: string[];
|
|
227
231
|
/** See `PlanPlatformOptions.blocked.archonRunsInsideApp`. */
|
|
228
232
|
archonRunsInsideApp?: boolean;
|
|
229
233
|
/** See `PlanPlatformOptions.blocked.archonDeployRunning`. */
|
|
@@ -822,6 +822,79 @@ export declare function heartbeatCheckUrlRef(name: string): string;
|
|
|
822
822
|
* bootstrapping at all.
|
|
823
823
|
*/
|
|
824
824
|
export declare const PLATFORM_HEARTBEATS_APPLIED_SCOPE_KEY = "heartbeats_applied_desired";
|
|
825
|
+
/**
|
|
826
|
+
* foundry#542: the uptime Worker, Archon's outside prober, deployed from the
|
|
827
|
+
* built script Archon publishes with each release. The step is
|
|
828
|
+
* `cloudflare:worker` keyed {@link PLATFORM_UPTIME_KEY}; `runPlatformSteps`
|
|
829
|
+
* records the script name under `cloudflare:worker:platform:uptime` once it
|
|
830
|
+
* succeeds, which is what stops `planUptime` planning it again.
|
|
831
|
+
*/
|
|
832
|
+
export declare const PLATFORM_UPTIME_KEY = "platform:uptime";
|
|
833
|
+
/** The Worker script's name at Cloudflare. */
|
|
834
|
+
export declare const PLATFORM_UPTIME_SCRIPT_NAME = "platform-uptime";
|
|
835
|
+
/** The Workers KV namespace title behind the Worker's `UPTIME_KV` binding. */
|
|
836
|
+
export declare const PLATFORM_UPTIME_KV_TITLE = "platform-uptime";
|
|
837
|
+
/**
|
|
838
|
+
* foundry#542: the held-key refs of the Worker's secrets. `UPTIME_TOKEN` is
|
|
839
|
+
* minted by the step (the token Archon's `UPTIME_TOKEN` env var also takes);
|
|
840
|
+
* the Access pair is minted through edge by the step, and the other two are held by a person. A missing one is reported as
|
|
841
|
+
* a blocked item, never a throw.
|
|
842
|
+
*/
|
|
843
|
+
export declare const PLATFORM_UPTIME_TOKEN_REF = "uptime:worker:token";
|
|
844
|
+
export declare const PLATFORM_UPTIME_ACCESS_CLIENT_ID_REF = "uptime:worker:access-client-id";
|
|
845
|
+
export declare const PLATFORM_UPTIME_ACCESS_CLIENT_SECRET_REF = "uptime:worker:access-client-secret";
|
|
846
|
+
export declare const PLATFORM_UPTIME_CHAT_TOKEN_REF = "uptime:worker:chat-token";
|
|
847
|
+
export declare const PLATFORM_UPTIME_MAIL_KEY_REF = "uptime:worker:mail-key";
|
|
848
|
+
/**
|
|
849
|
+
* Worker secret name to the held-key ref it is read from, for the secrets a
|
|
850
|
+
* person holds. `UPTIME_TOKEN` and the two `CF_ACCESS_CLIENT_*` secrets are
|
|
851
|
+
* minted by the step and always set, so they are not listed.
|
|
852
|
+
*/
|
|
853
|
+
export declare const PLATFORM_UPTIME_OPTIONAL_SECRETS: Readonly<Record<string, string>>;
|
|
854
|
+
/**
|
|
855
|
+
* foundry#542: scope keys (colon-free), the {@link PLATFORM_EDGE_PLATFORM_KEY_UNDELIVERED_SCOPE_KEY}
|
|
856
|
+
* pattern once. The undelivered id is recorded and persisted the moment the
|
|
857
|
+
* token is generated; the guard is recorded as soon as the value is durably
|
|
858
|
+
* held, before anything is sent to the Worker or Archon. Setting the value on
|
|
859
|
+
* the Worker and on Archon is repeated from the held value on every apply
|
|
860
|
+
* that needs it. Neither key holds the token; the id is the Worker secret's
|
|
861
|
+
* version label.
|
|
862
|
+
*/
|
|
863
|
+
export declare const PLATFORM_UPTIME_TOKEN_UNDELIVERED_SCOPE_KEY = "uptime_token_undelivered";
|
|
864
|
+
export declare const PLATFORM_UPTIME_TOKEN_ID_SCOPE_KEY = "uptime_token_id";
|
|
865
|
+
/** The token id Archon's env was last written with; `planArchon` plans again when it differs from {@link PLATFORM_UPTIME_TOKEN_ID_SCOPE_KEY}. */
|
|
866
|
+
export declare const PLATFORM_ARCHON_UPTIME_TOKEN_SYNCED_SCOPE_KEY = "archon_uptime_token_synced";
|
|
867
|
+
/** The tag, sha256 and Archon hostname the last successful run applied; a change plans the step again and mints nothing. */
|
|
868
|
+
export declare const PLATFORM_UPTIME_APPLIED_TAG_SCOPE_KEY = "uptime_applied_tag";
|
|
869
|
+
export declare const PLATFORM_UPTIME_APPLIED_SHA256_SCOPE_KEY = "uptime_applied_sha256";
|
|
870
|
+
export declare const PLATFORM_UPTIME_APPLIED_HOSTNAME_SCOPE_KEY = "uptime_applied_hostname";
|
|
871
|
+
/** The secret names (names only) the last run set, sorted and comma-joined: a newly held secret plans the step again. */
|
|
872
|
+
export declare const PLATFORM_UPTIME_APPLIED_SECRETS_SCOPE_KEY = "uptime_applied_secrets";
|
|
873
|
+
/** A digest of the plain text variables the last run uploaded (they are not secrets): a changed one plans the step again. */
|
|
874
|
+
export declare const PLATFORM_UPTIME_APPLIED_VARS_SCOPE_KEY = "uptime_applied_vars";
|
|
875
|
+
/** `'1'` once the step removed the Worker because the platform no longer enables it; cleared when it deploys again. */
|
|
876
|
+
export declare const PLATFORM_UPTIME_REMOVED_SCOPE_KEY = "uptime_removed";
|
|
877
|
+
/** The digest of the Worker's plain text variables that are set; empty when none is. */
|
|
878
|
+
export declare function uptimeVarsDigest(vars?: Readonly<Record<string, string | undefined>>): string;
|
|
879
|
+
/** The Worker secret names the step sets: the three it mints, and each held one. */
|
|
880
|
+
export declare function uptimeSecretNames(held: readonly string[]): string;
|
|
881
|
+
/** The Access service token's name at Cloudflare; edge answers `conflict` when a token of this name exists. */
|
|
882
|
+
export declare const PLATFORM_UPTIME_ACCESS_TOKEN_NAME = "platform-uptime";
|
|
883
|
+
/**
|
|
884
|
+
* The Access service token pair, minted through edge once: the same
|
|
885
|
+
* undelivered-then-guard pattern as the Worker token above. The undelivered
|
|
886
|
+
* id is recorded and persisted the moment edge returns the pair; the guard is
|
|
887
|
+
* recorded once both values are held. A pair that may be lost (the undelivered
|
|
888
|
+
* id without its guard, or the guard with nothing held) is repaired by the
|
|
889
|
+
* next apply: it removes that token through edge and mints again. Both are
|
|
890
|
+
* ids, never a value. Archon's step reads the guard to allow the token.
|
|
891
|
+
*/
|
|
892
|
+
export declare const PLATFORM_UPTIME_ACCESS_UNDELIVERED_SCOPE_KEY = "uptime_access_undelivered";
|
|
893
|
+
export declare const PLATFORM_UPTIME_ACCESS_TOKEN_ID_SCOPE_KEY = "uptime_access_token_id";
|
|
894
|
+
/** Why the pair cannot be minted: a token of the name exists and foundry has no id recorded for it. */
|
|
895
|
+
export declare function uptimeAccessTokenConflict(): string;
|
|
896
|
+
/** Whether anything of the uptime Worker is recorded that disabling it must remove. */
|
|
897
|
+
export declare function uptimeRecorded(recordedIds: Readonly<Record<string, string>>): boolean;
|
|
825
898
|
/**
|
|
826
899
|
* Options for {@link planPlatform}, replacing four positional arguments that
|
|
827
900
|
* shifted on every merge that added a platform service (#186, #187, #188) --
|
|
@@ -898,6 +971,21 @@ export interface PlanPlatformOptions {
|
|
|
898
971
|
* Not a block.
|
|
899
972
|
*/
|
|
900
973
|
archonRunsInsideApp?: boolean;
|
|
974
|
+
/** foundry#542: why the uptime Worker is blocked, or absent when it is not. */
|
|
975
|
+
uptime?: string;
|
|
976
|
+
/** foundry#542: the optional Worker secret names (`CF_ACCESS_CLIENT_ID`, ...) that are held now; names only. */
|
|
977
|
+
uptimeSecretsHeld?: string[];
|
|
978
|
+
/**
|
|
979
|
+
* foundry#542: what the host reads back for the uptime Worker: whether the
|
|
980
|
+
* Worker token and the Access pair are held, and the digest of its plain
|
|
981
|
+
* text variables ({@link uptimeVarsDigest}). Nothing held plans the step
|
|
982
|
+
* again; it repairs that, it is not a block.
|
|
983
|
+
*/
|
|
984
|
+
uptimeHeld?: {
|
|
985
|
+
token: boolean;
|
|
986
|
+
access: boolean;
|
|
987
|
+
vars: string;
|
|
988
|
+
};
|
|
901
989
|
/** Why Archon's step must wait: the deployment it triggered is still running. Blocks even an applied step. */
|
|
902
990
|
archonDeployRunning?: string;
|
|
903
991
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
1
2
|
import { VERSIONS } from '../bootstrap/versions.js';
|
|
2
3
|
import { APP_ENTRY_KEY } from '../org.js';
|
|
3
4
|
import { VERSION_CHANGE_KIND, } from '../plan.js';
|
|
@@ -920,6 +921,100 @@ export function heartbeatCheckUrlRef(name) {
|
|
|
920
921
|
* bootstrapping at all.
|
|
921
922
|
*/
|
|
922
923
|
export const PLATFORM_HEARTBEATS_APPLIED_SCOPE_KEY = 'heartbeats_applied_desired';
|
|
924
|
+
/**
|
|
925
|
+
* foundry#542: the uptime Worker, Archon's outside prober, deployed from the
|
|
926
|
+
* built script Archon publishes with each release. The step is
|
|
927
|
+
* `cloudflare:worker` keyed {@link PLATFORM_UPTIME_KEY}; `runPlatformSteps`
|
|
928
|
+
* records the script name under `cloudflare:worker:platform:uptime` once it
|
|
929
|
+
* succeeds, which is what stops `planUptime` planning it again.
|
|
930
|
+
*/
|
|
931
|
+
export const PLATFORM_UPTIME_KEY = 'platform:uptime';
|
|
932
|
+
/** The Worker script's name at Cloudflare. */
|
|
933
|
+
export const PLATFORM_UPTIME_SCRIPT_NAME = 'platform-uptime';
|
|
934
|
+
/** The Workers KV namespace title behind the Worker's `UPTIME_KV` binding. */
|
|
935
|
+
export const PLATFORM_UPTIME_KV_TITLE = 'platform-uptime';
|
|
936
|
+
/**
|
|
937
|
+
* foundry#542: the held-key refs of the Worker's secrets. `UPTIME_TOKEN` is
|
|
938
|
+
* minted by the step (the token Archon's `UPTIME_TOKEN` env var also takes);
|
|
939
|
+
* the Access pair is minted through edge by the step, and the other two are held by a person. A missing one is reported as
|
|
940
|
+
* a blocked item, never a throw.
|
|
941
|
+
*/
|
|
942
|
+
export const PLATFORM_UPTIME_TOKEN_REF = 'uptime:worker:token';
|
|
943
|
+
export const PLATFORM_UPTIME_ACCESS_CLIENT_ID_REF = 'uptime:worker:access-client-id';
|
|
944
|
+
export const PLATFORM_UPTIME_ACCESS_CLIENT_SECRET_REF = 'uptime:worker:access-client-secret';
|
|
945
|
+
export const PLATFORM_UPTIME_CHAT_TOKEN_REF = 'uptime:worker:chat-token';
|
|
946
|
+
export const PLATFORM_UPTIME_MAIL_KEY_REF = 'uptime:worker:mail-key';
|
|
947
|
+
/**
|
|
948
|
+
* Worker secret name to the held-key ref it is read from, for the secrets a
|
|
949
|
+
* person holds. `UPTIME_TOKEN` and the two `CF_ACCESS_CLIENT_*` secrets are
|
|
950
|
+
* minted by the step and always set, so they are not listed.
|
|
951
|
+
*/
|
|
952
|
+
export const PLATFORM_UPTIME_OPTIONAL_SECRETS = {
|
|
953
|
+
CHAT_ACCESS_TOKEN: PLATFORM_UPTIME_CHAT_TOKEN_REF,
|
|
954
|
+
MAIL_API_KEY: PLATFORM_UPTIME_MAIL_KEY_REF,
|
|
955
|
+
};
|
|
956
|
+
/**
|
|
957
|
+
* foundry#542: scope keys (colon-free), the {@link PLATFORM_EDGE_PLATFORM_KEY_UNDELIVERED_SCOPE_KEY}
|
|
958
|
+
* pattern once. The undelivered id is recorded and persisted the moment the
|
|
959
|
+
* token is generated; the guard is recorded as soon as the value is durably
|
|
960
|
+
* held, before anything is sent to the Worker or Archon. Setting the value on
|
|
961
|
+
* the Worker and on Archon is repeated from the held value on every apply
|
|
962
|
+
* that needs it. Neither key holds the token; the id is the Worker secret's
|
|
963
|
+
* version label.
|
|
964
|
+
*/
|
|
965
|
+
export const PLATFORM_UPTIME_TOKEN_UNDELIVERED_SCOPE_KEY = 'uptime_token_undelivered';
|
|
966
|
+
export const PLATFORM_UPTIME_TOKEN_ID_SCOPE_KEY = 'uptime_token_id';
|
|
967
|
+
/** The token id Archon's env was last written with; `planArchon` plans again when it differs from {@link PLATFORM_UPTIME_TOKEN_ID_SCOPE_KEY}. */
|
|
968
|
+
export const PLATFORM_ARCHON_UPTIME_TOKEN_SYNCED_SCOPE_KEY = 'archon_uptime_token_synced';
|
|
969
|
+
/** The tag, sha256 and Archon hostname the last successful run applied; a change plans the step again and mints nothing. */
|
|
970
|
+
export const PLATFORM_UPTIME_APPLIED_TAG_SCOPE_KEY = 'uptime_applied_tag';
|
|
971
|
+
export const PLATFORM_UPTIME_APPLIED_SHA256_SCOPE_KEY = 'uptime_applied_sha256';
|
|
972
|
+
export const PLATFORM_UPTIME_APPLIED_HOSTNAME_SCOPE_KEY = 'uptime_applied_hostname';
|
|
973
|
+
/** The secret names (names only) the last run set, sorted and comma-joined: a newly held secret plans the step again. */
|
|
974
|
+
export const PLATFORM_UPTIME_APPLIED_SECRETS_SCOPE_KEY = 'uptime_applied_secrets';
|
|
975
|
+
/** A digest of the plain text variables the last run uploaded (they are not secrets): a changed one plans the step again. */
|
|
976
|
+
export const PLATFORM_UPTIME_APPLIED_VARS_SCOPE_KEY = 'uptime_applied_vars';
|
|
977
|
+
/** `'1'` once the step removed the Worker because the platform no longer enables it; cleared when it deploys again. */
|
|
978
|
+
export const PLATFORM_UPTIME_REMOVED_SCOPE_KEY = 'uptime_removed';
|
|
979
|
+
/** The digest of the Worker's plain text variables that are set; empty when none is. */
|
|
980
|
+
export function uptimeVarsDigest(vars = {}) {
|
|
981
|
+
const set = Object.entries(vars)
|
|
982
|
+
.filter(([, value]) => Boolean(value))
|
|
983
|
+
.sort(([a], [b]) => a.localeCompare(b));
|
|
984
|
+
if (set.length === 0)
|
|
985
|
+
return '';
|
|
986
|
+
return createHash('sha256').update(JSON.stringify(set)).digest('hex').slice(0, 16);
|
|
987
|
+
}
|
|
988
|
+
/** The Worker secret names the step sets: the three it mints, and each held one. */
|
|
989
|
+
export function uptimeSecretNames(held) {
|
|
990
|
+
return ['UPTIME_TOKEN', 'CF_ACCESS_CLIENT_ID', 'CF_ACCESS_CLIENT_SECRET', ...held]
|
|
991
|
+
.sort()
|
|
992
|
+
.join(',');
|
|
993
|
+
}
|
|
994
|
+
/** The Access service token's name at Cloudflare; edge answers `conflict` when a token of this name exists. */
|
|
995
|
+
export const PLATFORM_UPTIME_ACCESS_TOKEN_NAME = 'platform-uptime';
|
|
996
|
+
/**
|
|
997
|
+
* The Access service token pair, minted through edge once: the same
|
|
998
|
+
* undelivered-then-guard pattern as the Worker token above. The undelivered
|
|
999
|
+
* id is recorded and persisted the moment edge returns the pair; the guard is
|
|
1000
|
+
* recorded once both values are held. A pair that may be lost (the undelivered
|
|
1001
|
+
* id without its guard, or the guard with nothing held) is repaired by the
|
|
1002
|
+
* next apply: it removes that token through edge and mints again. Both are
|
|
1003
|
+
* ids, never a value. Archon's step reads the guard to allow the token.
|
|
1004
|
+
*/
|
|
1005
|
+
export const PLATFORM_UPTIME_ACCESS_UNDELIVERED_SCOPE_KEY = 'uptime_access_undelivered';
|
|
1006
|
+
export const PLATFORM_UPTIME_ACCESS_TOKEN_ID_SCOPE_KEY = 'uptime_access_token_id';
|
|
1007
|
+
/** Why the pair cannot be minted: a token of the name exists and foundry has no id recorded for it. */
|
|
1008
|
+
export function uptimeAccessTokenConflict() {
|
|
1009
|
+
return `the uptime Worker is blocked, not planned: a Cloudflare Access service token named '${PLATFORM_UPTIME_ACCESS_TOKEN_NAME}' already exists and foundry did not make it; remove that token in Cloudflare Zero Trust (Access, Service Auth), then the next apply mints one through edge`;
|
|
1010
|
+
}
|
|
1011
|
+
/** Whether anything of the uptime Worker is recorded that disabling it must remove. */
|
|
1012
|
+
export function uptimeRecorded(recordedIds) {
|
|
1013
|
+
return ((Boolean(recordedIds[resourceKey('cloudflare', 'worker', PLATFORM_UPTIME_KEY)]) &&
|
|
1014
|
+
!recordedIds[PLATFORM_UPTIME_REMOVED_SCOPE_KEY]) ||
|
|
1015
|
+
Boolean(recordedIds[PLATFORM_UPTIME_ACCESS_TOKEN_ID_SCOPE_KEY]) ||
|
|
1016
|
+
Boolean(recordedIds[PLATFORM_UPTIME_ACCESS_UNDELIVERED_SCOPE_KEY]));
|
|
1017
|
+
}
|
|
923
1018
|
/**
|
|
924
1019
|
* #167: the platform's own diff engine, mirroring `diff.ts`'s
|
|
925
1020
|
* `computePlanDetail` for the one shared {@link DeclaredPlatform} record
|
|
@@ -1035,6 +1130,11 @@ export function planPlatform(platform, live, options = {}) {
|
|
|
1035
1130
|
const heartbeats = planHeartbeats(platform, recordedIds, hcReady, blocked.heartbeatsExecutorMissing ?? false, blocked.heartbeatsApiKeyMissing ?? false);
|
|
1036
1131
|
if (heartbeats.step)
|
|
1037
1132
|
plannedSteps.push(heartbeats.step);
|
|
1133
|
+
// foundry#542: after Archon is applied (never merely planned): the Worker
|
|
1134
|
+
// probes Archon, and Archon's env takes the token this step mints.
|
|
1135
|
+
const uptime = planUptime(platform, recordedIds, blocked.uptime, blocked.uptimeSecretsHeld ?? [], Boolean(recordedIds[resourceKey('coolify', 'app', PLATFORM_ARCHON_KEY)]), blocked.uptimeHeld);
|
|
1136
|
+
if (uptime.step)
|
|
1137
|
+
plannedSteps.push(uptime.step);
|
|
1038
1138
|
if (archon.step && blocked.archonRunsInsideApp)
|
|
1039
1139
|
plannedSteps.push(archon.step);
|
|
1040
1140
|
// #251: one line per service whose recorded applied ref no longer matches
|
|
@@ -1120,6 +1220,8 @@ export function planPlatform(platform, live, options = {}) {
|
|
|
1120
1220
|
unmanaged.push(...backups.blocked);
|
|
1121
1221
|
if (heartbeats.blocked)
|
|
1122
1222
|
unmanaged.push({ description: heartbeats.blocked, key: 'heartbeats' });
|
|
1223
|
+
for (const description of uptime.blocked)
|
|
1224
|
+
unmanaged.push({ description, key: 'uptime' });
|
|
1123
1225
|
return { plannedSteps: plannedSteps.map(withStepSystem), unmanaged, versionChanges };
|
|
1124
1226
|
}
|
|
1125
1227
|
/**
|
|
@@ -2235,6 +2337,106 @@ function planHeartbeats(platform, recordedIds, hcReady, executorMissing, apiKeyM
|
|
|
2235
2337
|
blocked: null,
|
|
2236
2338
|
};
|
|
2237
2339
|
}
|
|
2340
|
+
/**
|
|
2341
|
+
* foundry#542: the uptime Worker, one `cloudflare:worker` step. Blocked, not
|
|
2342
|
+
* planned (rule 2), while no artifact is pinned, Archon is not applied, the
|
|
2343
|
+
* executor lacks its dependencies, a Worker or Access token of the name exists
|
|
2344
|
+
* that foundry did not make. A lost Worker token or Access pair is not a
|
|
2345
|
+
* block: the step is planned and repairs it (rule 4). Planned again when the
|
|
2346
|
+
* pinned tag or sha256, the Archon hostname (`ARCHON_BASE_URL`), the plain
|
|
2347
|
+
* text variables or the set of secrets that can be set changed (rule 3), or
|
|
2348
|
+
* when a half-made deploy never recorded its applied values; it mints nothing
|
|
2349
|
+
* that is already held. When the platform no longer enables the Worker, a
|
|
2350
|
+
* destructive step removes whatever of it is recorded (edge removes its KV
|
|
2351
|
+
* namespace with the script).
|
|
2352
|
+
*
|
|
2353
|
+
* The returned `blocked` lines are reported in `unmanaged`; some accompany a
|
|
2354
|
+
* planned step (a secret whose source does not exist yet), because the step
|
|
2355
|
+
* still delivers everything that exists.
|
|
2356
|
+
*/
|
|
2357
|
+
function planUptime(platform, recordedIds, executorBlocked, heldOptional, archonApplied, uptimeHeld = {
|
|
2358
|
+
token: true,
|
|
2359
|
+
access: true,
|
|
2360
|
+
vars: '',
|
|
2361
|
+
}) {
|
|
2362
|
+
const { uptime } = platform;
|
|
2363
|
+
const deployed = Boolean(recordedIds[resourceKey('cloudflare', 'worker', PLATFORM_UPTIME_KEY)]) &&
|
|
2364
|
+
!recordedIds[PLATFORM_UPTIME_REMOVED_SCOPE_KEY];
|
|
2365
|
+
const action = (desired) => ({
|
|
2366
|
+
system: 'cloudflare',
|
|
2367
|
+
kind: 'worker',
|
|
2368
|
+
key: PLATFORM_UPTIME_KEY,
|
|
2369
|
+
desired,
|
|
2370
|
+
});
|
|
2371
|
+
if (!uptime.enabled) {
|
|
2372
|
+
if (!uptimeRecorded(recordedIds))
|
|
2373
|
+
return { step: null, blocked: [] };
|
|
2374
|
+
if (executorBlocked)
|
|
2375
|
+
return { step: null, blocked: [executorBlocked] };
|
|
2376
|
+
return {
|
|
2377
|
+
step: {
|
|
2378
|
+
action: action({ remove: true }),
|
|
2379
|
+
step: {
|
|
2380
|
+
description: `remove the uptime Worker ${PLATFORM_UPTIME_SCRIPT_NAME} and, with it, its KV namespace, its Access service token and its held token`,
|
|
2381
|
+
destroys: true,
|
|
2382
|
+
migrates: false,
|
|
2383
|
+
},
|
|
2384
|
+
},
|
|
2385
|
+
blocked: [],
|
|
2386
|
+
};
|
|
2387
|
+
}
|
|
2388
|
+
if (!uptime.tag || !uptime.sha256) {
|
|
2389
|
+
return {
|
|
2390
|
+
step: null,
|
|
2391
|
+
blocked: [
|
|
2392
|
+
'the uptime Worker is blocked, not planned: no artifact is pinned; declare uptime.tag (an Archon release tag) and uptime.sha256 (the digest of archon-uptime-worker.js at that tag)',
|
|
2393
|
+
],
|
|
2394
|
+
};
|
|
2395
|
+
}
|
|
2396
|
+
if (!archonApplied) {
|
|
2397
|
+
return {
|
|
2398
|
+
step: null,
|
|
2399
|
+
blocked: [
|
|
2400
|
+
'the uptime Worker is blocked, not planned: platform:archon has not been applied yet',
|
|
2401
|
+
],
|
|
2402
|
+
};
|
|
2403
|
+
}
|
|
2404
|
+
if (executorBlocked)
|
|
2405
|
+
return { step: null, blocked: [executorBlocked] };
|
|
2406
|
+
const missing = [];
|
|
2407
|
+
if (!heldOptional.includes('CHAT_ACCESS_TOKEN')) {
|
|
2408
|
+
missing.push(`the uptime Worker has no chat credential (CHAT_ACCESS_TOKEN): hold one under ${PLATFORM_UPTIME_CHAT_TOKEN_REF} and it is set on the next apply; until then it sends no chat alerts`);
|
|
2409
|
+
}
|
|
2410
|
+
if (!heldOptional.includes('MAIL_API_KEY')) {
|
|
2411
|
+
missing.push(`the uptime Worker has no mail API key (MAIL_API_KEY): hold one under ${PLATFORM_UPTIME_MAIL_KEY_REF} and it is set on the next apply; until then it sends no alert email`);
|
|
2412
|
+
}
|
|
2413
|
+
const hostname = platform.archon.hostname;
|
|
2414
|
+
// Applied only once a run got to its end and recorded these: a Worker that
|
|
2415
|
+
// was uploaded and then failed has its key but none of the applied values.
|
|
2416
|
+
const applied = deployed &&
|
|
2417
|
+
Boolean(recordedIds[PLATFORM_UPTIME_TOKEN_ID_SCOPE_KEY]) &&
|
|
2418
|
+
uptimeHeld.token &&
|
|
2419
|
+
Boolean(recordedIds[PLATFORM_UPTIME_ACCESS_TOKEN_ID_SCOPE_KEY]) &&
|
|
2420
|
+
uptimeHeld.access &&
|
|
2421
|
+
recordedIds[PLATFORM_UPTIME_APPLIED_TAG_SCOPE_KEY] === uptime.tag &&
|
|
2422
|
+
recordedIds[PLATFORM_UPTIME_APPLIED_SHA256_SCOPE_KEY] === uptime.sha256 &&
|
|
2423
|
+
recordedIds[PLATFORM_UPTIME_APPLIED_HOSTNAME_SCOPE_KEY] === hostname &&
|
|
2424
|
+
(recordedIds[PLATFORM_UPTIME_APPLIED_VARS_SCOPE_KEY] ?? '') === uptimeHeld.vars &&
|
|
2425
|
+
recordedIds[PLATFORM_UPTIME_APPLIED_SECRETS_SCOPE_KEY] === uptimeSecretNames(heldOptional);
|
|
2426
|
+
if (applied)
|
|
2427
|
+
return { step: null, blocked: missing };
|
|
2428
|
+
return {
|
|
2429
|
+
step: {
|
|
2430
|
+
action: action({ hostname, gitRef: uptime.tag, sha256: uptime.sha256 }),
|
|
2431
|
+
step: {
|
|
2432
|
+
description: `deploy the uptime Worker ${PLATFORM_UPTIME_SCRIPT_NAME} from Archon release ${uptime.tag}, probing https://${hostname}, with its KV namespace, secrets and cron trigger`,
|
|
2433
|
+
destroys: false,
|
|
2434
|
+
migrates: false,
|
|
2435
|
+
},
|
|
2436
|
+
},
|
|
2437
|
+
blocked: missing,
|
|
2438
|
+
};
|
|
2439
|
+
}
|
|
2238
2440
|
/**
|
|
2239
2441
|
* #172: the platform's AI host (https://github.com/wtfalch/ai) -- a Coolify
|
|
2240
2442
|
* Postgres plus three apps built from that repo's root Dockerfile, one per
|
|
@@ -2382,6 +2584,14 @@ function planArchon(platform, recordedIds, executorBlocked, authReady, drifted,
|
|
|
2382
2584
|
// other drift reason.
|
|
2383
2585
|
recordedIds[PLATFORM_ARCHON_APPLIED_ROLLING_UPDATE_SCOPE_KEY] === '1' &&
|
|
2384
2586
|
!recordedIds[PLATFORM_ARCHON_GATE_CLOSED_SCOPE_KEY] &&
|
|
2587
|
+
// foundry#542: the uptime token minted after Archon's last apply is not
|
|
2588
|
+
// in Archon's env yet.
|
|
2589
|
+
// A disabled Worker leaves the token in Archon's env until the step drops it.
|
|
2590
|
+
(platform.uptime.enabled
|
|
2591
|
+
? !recordedIds[PLATFORM_UPTIME_TOKEN_ID_SCOPE_KEY] ||
|
|
2592
|
+
recordedIds[PLATFORM_UPTIME_TOKEN_ID_SCOPE_KEY] ===
|
|
2593
|
+
recordedIds[PLATFORM_ARCHON_UPTIME_TOKEN_SYNCED_SCOPE_KEY]
|
|
2594
|
+
: !recordedIds[PLATFORM_ARCHON_UPTIME_TOKEN_SYNCED_SCOPE_KEY]) &&
|
|
2385
2595
|
// #398: a rotated integrations key id Archon's env was not yet written
|
|
2386
2596
|
// with (see PLATFORM_ARCHON_INTEGRATIONS_KEY_SYNCED_SCOPE_KEY). Absent
|
|
2387
2597
|
// before the first rotation on a platform applied before #398.
|
|
@@ -12,6 +12,7 @@ import type { LiveResource, PlatformCloudflareReader, PlatformCoolifyReader } fr
|
|
|
12
12
|
import type { 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
|
+
import { type PlatformUptimeDeps } from './uptime-worker.js';
|
|
15
16
|
/**
|
|
16
17
|
* #168: everything {@link provisionPlatformAuth} needs beyond
|
|
17
18
|
* {@link PlatformStepExecutionContext} -- a bundle, not more constructor
|
|
@@ -197,6 +198,12 @@ export interface FoundryPlatformStepExecutorOptions {
|
|
|
197
198
|
* heartbeat checks reported as blocked rather than planned.
|
|
198
199
|
*/
|
|
199
200
|
heartbeats?: PlatformHeartbeatsDeps;
|
|
201
|
+
/**
|
|
202
|
+
* foundry#542: dependencies for the uptime Worker step
|
|
203
|
+
* (`cloudflare:worker` keyed {@link PLATFORM_UPTIME_KEY}) -- optional for
|
|
204
|
+
* the same reason as `auth`; without it the step is reported blocked.
|
|
205
|
+
*/
|
|
206
|
+
uptime?: PlatformUptimeDeps;
|
|
200
207
|
/**
|
|
201
208
|
* #604: dependencies for the `cloudflare:access-provider` step -- optional
|
|
202
209
|
* for the same reason as `auth`; edge and sign-in applied with this unset
|
|
@@ -285,6 +292,7 @@ export declare class FoundryPlatformStepExecutor implements PlatformStepExecutor
|
|
|
285
292
|
private readonly backup?;
|
|
286
293
|
private readonly hc?;
|
|
287
294
|
private readonly heartbeats?;
|
|
295
|
+
private readonly uptime?;
|
|
288
296
|
private readonly accessProvider?;
|
|
289
297
|
constructor(cloudflare: Pick<CloudflarePort, 'findZoneByApex' | 'createZone'>, declaredOrgStore: Pick<DeclaredOrgStore, 'findOrgIdForZone' | 'list' | 'get'>, options?: FoundryPlatformStepExecutorOptions);
|
|
290
298
|
blockedSteps(platform: DeclaredPlatform, recordedIds?: Readonly<Record<string, string>>): Promise<{
|
|
@@ -302,9 +310,24 @@ export declare class FoundryPlatformStepExecutor implements PlatformStepExecutor
|
|
|
302
310
|
hc?: string;
|
|
303
311
|
heartbeatsExecutorMissing?: boolean;
|
|
304
312
|
heartbeatsApiKeyMissing?: boolean;
|
|
313
|
+
uptime?: string;
|
|
314
|
+
uptimeSecretsHeld?: string[];
|
|
315
|
+
uptimeHeld?: {
|
|
316
|
+
token: boolean;
|
|
317
|
+
access: boolean;
|
|
318
|
+
vars: string;
|
|
319
|
+
};
|
|
305
320
|
archonRunsInsideApp?: boolean;
|
|
306
321
|
archonDeployRunning?: string;
|
|
307
322
|
}>;
|
|
323
|
+
/**
|
|
324
|
+
* foundry#542: why the uptime Worker cannot run yet, which optional secrets
|
|
325
|
+
* are held, and whether the minted values are held. A Worker token or Access
|
|
326
|
+
* pair that is gone is not a block here: the step repairs it. The planner
|
|
327
|
+
* checks the pin and Archon first, so none of the reads here happens before
|
|
328
|
+
* they hold.
|
|
329
|
+
*/
|
|
330
|
+
private uptimeBlocked;
|
|
308
331
|
/**
|
|
309
332
|
* Whether Archon's live Access policy lets through anyone but the declared
|
|
310
333
|
* emails, or (#211 round 4) the broker it last registered with no longer
|