@wtfalch/foundry 0.66.0 → 0.67.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/catalogue/copy-en.js +5 -0
- package/dist/catalogue/integrations.js +5 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/modules/analytics.d.ts +8 -0
- package/dist/modules/analytics.js +28 -0
- package/dist/modules/index.js +2 -0
- package/dist/org.d.ts +9 -1
- package/dist/org.js +4 -1
- package/dist/plan/analytics-boule.d.ts +17 -0
- package/dist/plan/analytics-boule.js +43 -0
- package/dist/plan/analytics.d.ts +58 -0
- package/dist/plan/analytics.js +89 -0
- package/dist/plan/company-backups.js +2 -0
- package/dist/plan/diff.d.ts +2 -0
- package/dist/plan/diff.js +86 -0
- package/dist/plan/entry-analytics-key.d.ts +35 -0
- package/dist/plan/entry-analytics-key.js +134 -0
- package/dist/plan/executor.d.ts +36 -0
- package/dist/plan/executor.js +290 -1
- package/dist/plan/hostnames.js +4 -0
- package/dist/plan/index.d.ts +2 -0
- package/dist/plan/index.js +1 -0
- package/dist/plan/live.d.ts +1 -1
- package/dist/plan/locked-hostnames.js +3 -0
- package/dist/plan/org-names.d.ts +3 -3
- package/dist/plan/org-names.js +3 -0
- package/dist/plan/own-limits.d.ts +6 -0
- package/dist/plan/own-limits.js +1 -0
- package/dist/plan/plan-apply.d.ts +3 -0
- package/dist/plan/plan-apply.js +9 -0
- package/dist/plan/platform-diff.d.ts +17 -1
- package/dist/plan/platform-diff.js +35 -2
- package/dist/plan/platform-providers.js +35 -2
- package/dist/plan/providers-readers.js +16 -1
- package/dist/plan/stage-services.d.ts +3 -1
- package/dist/plan/stage-services.js +2 -1
- package/dist/plan/uptime-worker.js +53 -3
- package/dist/slug.d.ts +1 -1
- package/dist/slug.js +5 -2
- package/dist/worker-app-config.js +2 -2
- package/package.json +1 -1
package/dist/plan/own-limits.js
CHANGED
|
@@ -583,6 +583,8 @@ export interface FoundryPlanApplyOptions {
|
|
|
583
583
|
workerAppKeysUrl?: string;
|
|
584
584
|
/** F7: the keys service's registrar address, when the executor has a `KeysRegistrarPort`. */
|
|
585
585
|
keysRegistrarUrl?: string;
|
|
586
|
+
/** True when the host's executor was built with an `analytics` port. A `uses.analytics` key is blocked without it. */
|
|
587
|
+
analyticsPort?: boolean;
|
|
586
588
|
/** C2: true when the host's executor was built with a `WorkerAppDatabasePort`. */
|
|
587
589
|
workerAppDatabase?: boolean;
|
|
588
590
|
/** True when that port has `deletableTables`. A `workerApp.database.noDelete` is blocked without it. */
|
|
@@ -747,6 +749,7 @@ export declare class FoundryPlanApply implements PlanApply {
|
|
|
747
749
|
private readonly workerApps;
|
|
748
750
|
private readonly workerAppKeysUrl;
|
|
749
751
|
private readonly keysRegistrarUrl;
|
|
752
|
+
private readonly analyticsPort;
|
|
750
753
|
private readonly workerAppDatabase;
|
|
751
754
|
private readonly workerAppNoDeleteCheck;
|
|
752
755
|
private readonly workerAppRegistryAccountId;
|
package/dist/plan/plan-apply.js
CHANGED
|
@@ -188,6 +188,7 @@ export class FoundryPlanApply {
|
|
|
188
188
|
workerApps;
|
|
189
189
|
workerAppKeysUrl;
|
|
190
190
|
keysRegistrarUrl;
|
|
191
|
+
analyticsPort;
|
|
191
192
|
workerAppDatabase;
|
|
192
193
|
workerAppNoDeleteCheck;
|
|
193
194
|
workerAppRegistryAccountId;
|
|
@@ -233,6 +234,7 @@ export class FoundryPlanApply {
|
|
|
233
234
|
this.workerApps = options.workerApps ?? false;
|
|
234
235
|
this.workerAppKeysUrl = options.workerAppKeysUrl;
|
|
235
236
|
this.keysRegistrarUrl = options.keysRegistrarUrl;
|
|
237
|
+
this.analyticsPort = options.analyticsPort ?? false;
|
|
236
238
|
this.workerAppDatabase = options.workerAppDatabase ?? false;
|
|
237
239
|
this.workerAppNoDeleteCheck = options.workerAppNoDeleteCheck ?? false;
|
|
238
240
|
this.workerAppRegistryAccountId = options.workerAppRegistryAccountId;
|
|
@@ -1627,6 +1629,7 @@ export class FoundryPlanApply {
|
|
|
1627
1629
|
workerApps: this.workerApps,
|
|
1628
1630
|
workerAppKeysUrl: this.workerAppKeysUrl,
|
|
1629
1631
|
keysRegistrarUrl: this.keysRegistrarUrl,
|
|
1632
|
+
analyticsPort: this.analyticsPort,
|
|
1630
1633
|
workerAppDatabase: this.workerAppDatabase,
|
|
1631
1634
|
workerAppNoDeleteCheck: this.workerAppNoDeleteCheck,
|
|
1632
1635
|
workerAppRegistryAccountId: this.workerAppRegistryAccountId,
|
|
@@ -2665,6 +2668,8 @@ const STEP_FIELDS = {
|
|
|
2665
2668
|
'cloudflare:worker': 'hosting.entries.worker',
|
|
2666
2669
|
'cloudflare:worker-app': 'hosting.entries.workerApp',
|
|
2667
2670
|
// F4 (#925): both hang off the company's Apps switch.
|
|
2671
|
+
'coolify:analytics': 'hosting.entries.uses.analytics',
|
|
2672
|
+
'coolify:analytics-boule': 'boule.apps',
|
|
2668
2673
|
'coolify:org-apps-admin': 'boule.apps',
|
|
2669
2674
|
'cloudflare:apps-db-route': 'boule.apps',
|
|
2670
2675
|
'cloudflare:apps-zones': 'boule.apps',
|
|
@@ -2688,6 +2693,10 @@ function stepField(action) {
|
|
|
2688
2693
|
// An entry's jobs project: always `<orgId>:<entry>`.
|
|
2689
2694
|
if (action.system === 'jobs' && action.kind === 'project')
|
|
2690
2695
|
return 'hosting.entries.uses.jobs';
|
|
2696
|
+
// An entry's analytics site key: always `<orgId>:<entry>`.
|
|
2697
|
+
if (action.system === 'analytics' && action.kind === 'key') {
|
|
2698
|
+
return 'hosting.entries.uses.analytics';
|
|
2699
|
+
}
|
|
2691
2700
|
// An entry's audit ledger registration: always `<orgId>:<entry>`.
|
|
2692
2701
|
if (action.system === 'audit' && action.kind === 'ledger')
|
|
2693
2702
|
return 'hosting.entries.uses.audit';
|
|
@@ -1117,6 +1117,13 @@ export declare const PLATFORM_UPTIME_KV_TITLE = "platform-uptime";
|
|
|
1117
1117
|
* a blocked item, never a throw.
|
|
1118
1118
|
*/
|
|
1119
1119
|
export declare const PLATFORM_UPTIME_TOKEN_REF = "uptime:worker:token";
|
|
1120
|
+
/**
|
|
1121
|
+
* The release tick and audit seal tick tokens: minted by the step like `UPTIME_TOKEN`, each its own
|
|
1122
|
+
* value, set on the Worker and on Archon's env under the same names. The Worker skips a tick while
|
|
1123
|
+
* its token is unset, and Archon's route refuses every call while its own is unset.
|
|
1124
|
+
*/
|
|
1125
|
+
export declare const PLATFORM_UPTIME_RELEASE_TICK_TOKEN_REF = "uptime:worker:release-tick-token";
|
|
1126
|
+
export declare const PLATFORM_UPTIME_AUDIT_SEAL_TICK_TOKEN_REF = "uptime:worker:audit-seal-tick-token";
|
|
1120
1127
|
export declare const PLATFORM_UPTIME_ACCESS_CLIENT_ID_REF = "uptime:worker:access-client-id";
|
|
1121
1128
|
export declare const PLATFORM_UPTIME_ACCESS_CLIENT_SECRET_REF = "uptime:worker:access-client-secret";
|
|
1122
1129
|
export declare const PLATFORM_UPTIME_CHAT_TOKEN_REF = "uptime:worker:chat-token";
|
|
@@ -1138,6 +1145,15 @@ export declare const PLATFORM_UPTIME_OPTIONAL_SECRETS: Readonly<Record<string, s
|
|
|
1138
1145
|
*/
|
|
1139
1146
|
export declare const PLATFORM_UPTIME_TOKEN_UNDELIVERED_SCOPE_KEY = "uptime_token_undelivered";
|
|
1140
1147
|
export declare const PLATFORM_UPTIME_TOKEN_ID_SCOPE_KEY = "uptime_token_id";
|
|
1148
|
+
/** The same pair of scope keys for each tick token: `<undelivered>` before the value is held, `<id>` once it is. */
|
|
1149
|
+
export declare const PLATFORM_UPTIME_RELEASE_TICK_UNDELIVERED_SCOPE_KEY = "uptime_release_tick_undelivered";
|
|
1150
|
+
export declare const PLATFORM_UPTIME_RELEASE_TICK_ID_SCOPE_KEY = "uptime_release_tick_id";
|
|
1151
|
+
export declare const PLATFORM_UPTIME_AUDIT_SEAL_TICK_UNDELIVERED_SCOPE_KEY = "uptime_audit_seal_tick_undelivered";
|
|
1152
|
+
export declare const PLATFORM_UPTIME_AUDIT_SEAL_TICK_ID_SCOPE_KEY = "uptime_audit_seal_tick_id";
|
|
1153
|
+
/** The tick token ids (`<release>:<seal>`) Archon's env was last written with; like {@link PLATFORM_ARCHON_UPTIME_TOKEN_SYNCED_SCOPE_KEY}. */
|
|
1154
|
+
export declare const PLATFORM_ARCHON_UPTIME_TICKS_SYNCED_SCOPE_KEY = "archon_uptime_ticks_synced";
|
|
1155
|
+
/** What {@link PLATFORM_ARCHON_UPTIME_TICKS_SYNCED_SCOPE_KEY} holds once Archon's env carries the tick tokens; empty while neither is minted. */
|
|
1156
|
+
export declare function uptimeTicksSyncValue(recorded: Readonly<Record<string, string>>): string;
|
|
1141
1157
|
/** The token id Archon's env was last written with; `planArchon` plans again when it differs from {@link PLATFORM_UPTIME_TOKEN_ID_SCOPE_KEY}. */
|
|
1142
1158
|
export declare const PLATFORM_ARCHON_UPTIME_TOKEN_SYNCED_SCOPE_KEY = "archon_uptime_token_synced";
|
|
1143
1159
|
/** The tag, sha256 and Archon hostname the last successful run applied; a change plans the step again and mints nothing. */
|
|
@@ -1171,7 +1187,7 @@ export declare function uptimeEffectiveVars(hostVars: Readonly<Record<string, st
|
|
|
1171
1187
|
} | null): Record<string, string | undefined>;
|
|
1172
1188
|
/** The digest of the Worker's plain text variables that are set; empty when none is. */
|
|
1173
1189
|
export declare function uptimeVarsDigest(vars?: Readonly<Record<string, string | undefined>>): string;
|
|
1174
|
-
/** The Worker secret names the step sets: the
|
|
1190
|
+
/** The Worker secret names the step sets: the five it mints, and each held one. */
|
|
1175
1191
|
export declare function uptimeSecretNames(held: readonly string[]): string;
|
|
1176
1192
|
/** The Access service token's name at Cloudflare; edge answers `conflict` when a token of this name exists. */
|
|
1177
1193
|
export declare const PLATFORM_UPTIME_ACCESS_TOKEN_NAME = "platform-uptime";
|
|
@@ -1272,6 +1272,13 @@ export const PLATFORM_UPTIME_KV_TITLE = 'platform-uptime';
|
|
|
1272
1272
|
* a blocked item, never a throw.
|
|
1273
1273
|
*/
|
|
1274
1274
|
export const PLATFORM_UPTIME_TOKEN_REF = 'uptime:worker:token';
|
|
1275
|
+
/**
|
|
1276
|
+
* The release tick and audit seal tick tokens: minted by the step like `UPTIME_TOKEN`, each its own
|
|
1277
|
+
* value, set on the Worker and on Archon's env under the same names. The Worker skips a tick while
|
|
1278
|
+
* its token is unset, and Archon's route refuses every call while its own is unset.
|
|
1279
|
+
*/
|
|
1280
|
+
export const PLATFORM_UPTIME_RELEASE_TICK_TOKEN_REF = 'uptime:worker:release-tick-token';
|
|
1281
|
+
export const PLATFORM_UPTIME_AUDIT_SEAL_TICK_TOKEN_REF = 'uptime:worker:audit-seal-tick-token';
|
|
1275
1282
|
export const PLATFORM_UPTIME_ACCESS_CLIENT_ID_REF = 'uptime:worker:access-client-id';
|
|
1276
1283
|
export const PLATFORM_UPTIME_ACCESS_CLIENT_SECRET_REF = 'uptime:worker:access-client-secret';
|
|
1277
1284
|
export const PLATFORM_UPTIME_CHAT_TOKEN_REF = 'uptime:worker:chat-token';
|
|
@@ -1296,6 +1303,19 @@ export const PLATFORM_UPTIME_OPTIONAL_SECRETS = {
|
|
|
1296
1303
|
*/
|
|
1297
1304
|
export const PLATFORM_UPTIME_TOKEN_UNDELIVERED_SCOPE_KEY = 'uptime_token_undelivered';
|
|
1298
1305
|
export const PLATFORM_UPTIME_TOKEN_ID_SCOPE_KEY = 'uptime_token_id';
|
|
1306
|
+
/** The same pair of scope keys for each tick token: `<undelivered>` before the value is held, `<id>` once it is. */
|
|
1307
|
+
export const PLATFORM_UPTIME_RELEASE_TICK_UNDELIVERED_SCOPE_KEY = 'uptime_release_tick_undelivered';
|
|
1308
|
+
export const PLATFORM_UPTIME_RELEASE_TICK_ID_SCOPE_KEY = 'uptime_release_tick_id';
|
|
1309
|
+
export const PLATFORM_UPTIME_AUDIT_SEAL_TICK_UNDELIVERED_SCOPE_KEY = 'uptime_audit_seal_tick_undelivered';
|
|
1310
|
+
export const PLATFORM_UPTIME_AUDIT_SEAL_TICK_ID_SCOPE_KEY = 'uptime_audit_seal_tick_id';
|
|
1311
|
+
/** The tick token ids (`<release>:<seal>`) Archon's env was last written with; like {@link PLATFORM_ARCHON_UPTIME_TOKEN_SYNCED_SCOPE_KEY}. */
|
|
1312
|
+
export const PLATFORM_ARCHON_UPTIME_TICKS_SYNCED_SCOPE_KEY = 'archon_uptime_ticks_synced';
|
|
1313
|
+
/** What {@link PLATFORM_ARCHON_UPTIME_TICKS_SYNCED_SCOPE_KEY} holds once Archon's env carries the tick tokens; empty while neither is minted. */
|
|
1314
|
+
export function uptimeTicksSyncValue(recorded) {
|
|
1315
|
+
const release = recorded[PLATFORM_UPTIME_RELEASE_TICK_ID_SCOPE_KEY];
|
|
1316
|
+
const seal = recorded[PLATFORM_UPTIME_AUDIT_SEAL_TICK_ID_SCOPE_KEY];
|
|
1317
|
+
return release && seal ? `${release}:${seal}` : '';
|
|
1318
|
+
}
|
|
1299
1319
|
/** The token id Archon's env was last written with; `planArchon` plans again when it differs from {@link PLATFORM_UPTIME_TOKEN_ID_SCOPE_KEY}. */
|
|
1300
1320
|
export const PLATFORM_ARCHON_UPTIME_TOKEN_SYNCED_SCOPE_KEY = 'archon_uptime_token_synced';
|
|
1301
1321
|
/** The tag, sha256 and Archon hostname the last successful run applied; a change plans the step again and mints nothing. */
|
|
@@ -1339,9 +1359,16 @@ export function uptimeVarsDigest(vars = {}) {
|
|
|
1339
1359
|
return '';
|
|
1340
1360
|
return createHash('sha256').update(JSON.stringify(set)).digest('hex').slice(0, 16);
|
|
1341
1361
|
}
|
|
1342
|
-
/** The Worker secret names the step sets: the
|
|
1362
|
+
/** The Worker secret names the step sets: the five it mints, and each held one. */
|
|
1343
1363
|
export function uptimeSecretNames(held) {
|
|
1344
|
-
return [
|
|
1364
|
+
return [
|
|
1365
|
+
'UPTIME_TOKEN',
|
|
1366
|
+
'RELEASE_TICK_TOKEN',
|
|
1367
|
+
'AUDIT_SEAL_TICK_TOKEN',
|
|
1368
|
+
'CF_ACCESS_CLIENT_ID',
|
|
1369
|
+
'CF_ACCESS_CLIENT_SECRET',
|
|
1370
|
+
...held,
|
|
1371
|
+
]
|
|
1345
1372
|
.sort()
|
|
1346
1373
|
.join(',');
|
|
1347
1374
|
}
|
|
@@ -3250,6 +3277,7 @@ function planUptime(platform, recordedIds, executorBlocked, heldOptional, archon
|
|
|
3250
3277
|
const applied = deployed &&
|
|
3251
3278
|
Boolean(recordedIds[PLATFORM_UPTIME_TOKEN_ID_SCOPE_KEY]) &&
|
|
3252
3279
|
uptimeHeld.token &&
|
|
3280
|
+
Boolean(uptimeTicksSyncValue(recordedIds)) &&
|
|
3253
3281
|
Boolean(recordedIds[PLATFORM_UPTIME_ACCESS_TOKEN_ID_SCOPE_KEY]) &&
|
|
3254
3282
|
uptimeHeld.access &&
|
|
3255
3283
|
recordedIds[PLATFORM_UPTIME_APPLIED_TAG_SCOPE_KEY] === uptime.tag &&
|
|
@@ -3511,6 +3539,11 @@ function planArchon(platform, recordedIds, executorBlocked, ownerSignIn, ownerFi
|
|
|
3511
3539
|
recordedIds[PLATFORM_UPTIME_TOKEN_ID_SCOPE_KEY] ===
|
|
3512
3540
|
recordedIds[PLATFORM_ARCHON_UPTIME_TOKEN_SYNCED_SCOPE_KEY]
|
|
3513
3541
|
: !recordedIds[PLATFORM_ARCHON_UPTIME_TOKEN_SYNCED_SCOPE_KEY]) &&
|
|
3542
|
+
// The tick tokens the same way: minted after Archon's last apply means not in its env yet.
|
|
3543
|
+
(platform.uptime.enabled
|
|
3544
|
+
? (recordedIds[PLATFORM_ARCHON_UPTIME_TICKS_SYNCED_SCOPE_KEY] ?? '') ===
|
|
3545
|
+
uptimeTicksSyncValue(recordedIds)
|
|
3546
|
+
: !recordedIds[PLATFORM_ARCHON_UPTIME_TICKS_SYNCED_SCOPE_KEY]) &&
|
|
3514
3547
|
// #398: a rotated integrations key id Archon's env was not yet written
|
|
3515
3548
|
// with (see PLATFORM_ARCHON_INTEGRATIONS_KEY_SYNCED_SCOPE_KEY). Absent
|
|
3516
3549
|
// before the first rotation on a platform applied before #398.
|
|
@@ -33,7 +33,7 @@ import { MAIL_APP_SCOPE_KEY, MAIL_DATABASE_NAME, MAIL_DATABASE_PASSWORD_ENV, MAI
|
|
|
33
33
|
import { OIKOS_INGRESS_BLOCKED, PLATFORM_OIKOS_INGRESS_KEY, oikosIngressApplied, oikosIngressZoneBlocked, provisionOikosIngress, } from './oikos-ingress.js';
|
|
34
34
|
import { recordOwnDnsHostname } from './own-dns.js';
|
|
35
35
|
import { platformNetwork, platformTarget, scopeFromPlatform, } from './own-services.js';
|
|
36
|
-
import { AUTH_TUNNEL_ROUTES_VERSION, AUTH_ZITADEL_RUN_OPTIONS, GITHUB_RELEASE_HOOK_DEPS_MISSING, GITHUB_RELEASE_WEBHOOK_SECRET_ENV, JOBS_DEPLOY_PENDING_SCOPE_KEY, JOBS_INTERNAL_URL_SCOPE_KEY, JOBS_SCHEDULER_KEY, PLATFORM_ACCESS_PROVIDER_APPLIED_AUTH_DOMAIN_SCOPE_KEY, PLATFORM_ACCESS_PROVIDER_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_ACCESS_PROVIDER_UNDELIVERED_SCOPE_KEY, PLATFORM_ACCESS_ZITADEL_APP_SCOPE_KEY, PLATFORM_ACCESS_ZITADEL_PROJECT_SCOPE_KEY, PLATFORM_AI_APPLIED_GITREF_SCOPE_KEY, PLATFORM_AI_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_AI_CONNECTOR_APP_SCOPE_KEY, PLATFORM_AI_DEPLOY_PENDING_SCOPE_KEY, PLATFORM_AI_INTERNAL_URL_SCOPE_KEY, PLATFORM_AI_KEY, PLATFORM_AI_MANAGEMENT_KEY_CONFLICT_SCOPE_KEY, PLATFORM_AI_MANAGEMENT_KEY_SCOPE_KEY, PLATFORM_AI_MANAGEMENT_KEY_UNDELIVERED_SCOPE_KEY, PLATFORM_AI_MIGRATE_BLOCKED_SCOPE_KEY, PLATFORM_AI_ORGANISATION_SCOPE_KEY, PLATFORM_AI_SIGNING_KEY_SET_SCOPE_KEY, PLATFORM_AI_SIGNING_KEY_UNDELIVERED_SCOPE_KEY, PLATFORM_AI_TUNNEL_SCOPE_KEY, PLATFORM_ARCHON_ACCESS_PIN_SCOPE_KEY, PLATFORM_ARCHON_APPLIED_AUTH_HOSTNAME_SCOPE_KEY, PLATFORM_ARCHON_APPLIED_AUTH_ORG_SCOPE_KEY, PLATFORM_ARCHON_APPLIED_BROKER_SCOPE_KEY, PLATFORM_ARCHON_APPLIED_CI_ORG_SCOPE_KEY, PLATFORM_ARCHON_APPLIED_FILES_SCOPE_KEY, PLATFORM_ARCHON_APPLIED_FIRST_OWNER_LINK_SCOPE_KEY, PLATFORM_ARCHON_APPLIED_GIT_REF_SCOPE_KEY, PLATFORM_ARCHON_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_ARCHON_APPLIED_ROLLING_UPDATE_SCOPE_KEY, PLATFORM_ARCHON_APP_KEY_ORG_SCOPE_KEY, PLATFORM_ARCHON_CONNECTOR_APP_SCOPE_KEY, PLATFORM_ARCHON_COOLIFY_READ_TOKEN_REF, PLATFORM_ARCHON_DEPLOYMENT_SCOPE_KEY, PLATFORM_ARCHON_DESKTOP_CALLBACK_APPLIED_SCOPE_KEY, PLATFORM_ARCHON_ENVS_DEPLOYED_SCOPE_KEY, PLATFORM_ARCHON_GATE_CLOSED_SCOPE_KEY, PLATFORM_ARCHON_ID_TOKEN_USERINFO_APPLIED_SCOPE_KEY, PLATFORM_ARCHON_INTEGRATIONS_KEY_SYNCED_SCOPE_KEY, PLATFORM_ARCHON_KEY, PLATFORM_ARCHON_MASTER_CLOUDFLARE_TOKEN_REF, PLATFORM_ARCHON_MASTER_COOLIFY_TOKEN_REF, PLATFORM_ARCHON_MASTER_COOLIFY_URL_REF, PLATFORM_ARCHON_ORG_DOMAIN_BLOCKED_SCOPE_KEY, PLATFORM_ARCHON_ORG_DOMAIN_SCOPE_KEY, PLATFORM_ARCHON_ORG_POLICY_SCOPE_KEY, PLATFORM_ARCHON_ORG_REGISTRATION_CLOSED_SCOPE_KEY, PLATFORM_ARCHON_ORG_SCOPE_KEY, PLATFORM_ARCHON_OWNER_INVITE_ORG_SCOPE_KEY, PLATFORM_ARCHON_OWNER_INVITE_SCOPE_KEY, PLATFORM_ARCHON_POST_LOGOUT_APPLIED_SCOPE_KEY, PLATFORM_ARCHON_RETIRED_CLIENT_SCOPE_KEY, PLATFORM_ARCHON_UPTIME_TOKEN_SYNCED_SCOPE_KEY, PLATFORM_ARCHON_WEBHOOK_SECRET_DEPLOYED_SCOPE_KEY, PLATFORM_ARCHON_ZITADEL_APP_SCOPE_KEY, PLATFORM_ARCHON_ZITADEL_PROJECT_SCOPE_KEY, PLATFORM_AUTH_ADMIN_UNDELIVERED_SCOPE_KEY, PLATFORM_AUTH_ADMIN_USER_SCOPE_KEY, PLATFORM_AUTH_APPLIED_GIT_REF_SCOPE_KEY, PLATFORM_AUTH_BROKER_DEPLOYED_SCOPE_KEY, PLATFORM_AUTH_BROKER_SCOPE_KEY, PLATFORM_AUTH_CONNECTOR_APP_SCOPE_KEY, PLATFORM_AUTH_INSTANCE_SCOPE_KEY, PLATFORM_AUTH_INSTANCE_UNDELIVERED_SCOPE_KEY, PLATFORM_AUTH_KEY, PLATFORM_AUTH_LOGIN_CLIENT_UNDELIVERED_SCOPE_KEY, PLATFORM_AUTH_LOGIN_CLIENT_USER_SCOPE_KEY, PLATFORM_AUTH_MCP_SCOPE_KEY, PLATFORM_AUTH_OWNER_INVITE_ORG_SCOPE_KEY, PLATFORM_AUTH_OWNER_INVITE_SCOPE_KEY, PLATFORM_AUTH_REGISTRATION_CLOSED_SCOPE_KEY, PLATFORM_AUTH_RESERVED_ADMIN_USERNAME_SCOPE_KEY, PLATFORM_AUTH_SMTP_SCOPE_KEY, PLATFORM_AUTH_TUNNEL_ROUTES_SCOPE_KEY, PLATFORM_AUTH_ZITADEL_APPLIED_IMAGE_TAG_SCOPE_KEY, PLATFORM_AUTH_ZITADEL_RUN_OPTIONS_SCOPE_KEY, PLATFORM_BACKUP_DATABASE_SCOPE_KEYS, PLATFORM_BACKUP_SERVICE_KEYS, PLATFORM_EDGE_APPLIED_GIT_REF_SCOPE_KEY, PLATFORM_EDGE_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_EDGE_CLOUDFLARE_TOKEN_REF, PLATFORM_EDGE_INTERNAL_URL_SCOPE_KEY, PLATFORM_EDGE_KEY, PLATFORM_EDGE_KEY_REF, PLATFORM_EDGE_PLATFORM_KEY_SET_SCOPE_KEY, PLATFORM_EDGE_PLATFORM_KEY_UNDELIVERED_SCOPE_KEY, PLATFORM_FILES_APPLIED_GIT_REF_SCOPE_KEY, PLATFORM_FILES_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_FILES_INTERNAL_URL_SCOPE_KEY, PLATFORM_FILES_INTROSPECTION_SCOPE_KEY, PLATFORM_FILES_INTROSPECTION_UNDELIVERED_SCOPE_KEY, PLATFORM_FILES_KEY, PLATFORM_FILES_MANAGEMENT_KEY_SCOPE_KEY, PLATFORM_FILES_MANAGEMENT_UNDELIVERED_SCOPE_KEY, PLATFORM_FILES_ZITADEL_PROJECT_SCOPE_KEY, PLATFORM_GITHUB_RELEASE_HOOK_KEY, PLATFORM_HC_ACCESS_SCOPE_KEY, PLATFORM_HC_API_KEY_REF, PLATFORM_HC_APPLIED_GIT_REF_SCOPE_KEY, PLATFORM_HC_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_HC_KEY, PLATFORM_HEARTBEATS_APPLIED_SCOPE_KEY, PLATFORM_HEARTBEATS_HOSTED_API_KEY_REF, PLATFORM_INTEGRATIONS_APPLIED_GITREF_SCOPE_KEY, PLATFORM_INTEGRATIONS_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_INTEGRATIONS_KEY, PLATFORM_INTEGRATIONS_KEY_ID_SCOPE_KEY, PLATFORM_INTEGRATIONS_KEY_ROTATION_PREVIOUS_SCOPE_KEY, PLATFORM_INTEGRATIONS_KEY_ROTATION_REQUESTED_SCOPE_KEY, PLATFORM_INTEGRATIONS_KEY_ROTATION_UNDELIVERED_SCOPE_KEY, PLATFORM_INTEGRATIONS_KEY_SET_SCOPE_KEY, PLATFORM_INTEGRATIONS_KEY_UNDELIVERED_SCOPE_KEY, PLATFORM_INTEGRATIONS_RUNNER_KEYRING_DEPLOYED_SCOPE_KEY, PLATFORM_MAIL_ADMIN_KEY_SCOPE_KEY, PLATFORM_MAIL_ADMIN_UNDELIVERED_SCOPE_KEY, PLATFORM_MAIL_APPLIED_GIT_REF_SCOPE_KEY, PLATFORM_MAIL_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_MAIL_CONNECTOR_APP_SCOPE_KEY, PLATFORM_MAIL_HOST_PORTS_DIRECT, PLATFORM_MAIL_HOST_PORTS_SCOPE_KEY, PLATFORM_MAIL_INTERNAL_URL_SCOPE_KEY, PLATFORM_MAIL_KEY, PLATFORM_MAIL_RELAY_KEY, PLATFORM_MAIL_ROLE_CHECK_SCOPE_KEY, PLATFORM_MAIL_ROLE_ENSURED_SCOPE_KEY, PLATFORM_MAIL_ROLE_GRANTED_SCOPE_KEY, PLATFORM_MAIL_ROUTE_SCOPE_KEY, PLATFORM_MAIL_ROUTE_WANTED, PLATFORM_MAIL_USER_ROLE, PLATFORM_MAIL_ZITADEL_PROJECT_SCOPE_KEY, PLATFORM_OWNER_ORG_DOMAIN_BLOCKED_SCOPE_KEY, PLATFORM_OWNER_ORG_DOMAIN_SCOPE_KEY, PLATFORM_OWNER_ORG_SCOPE_KEY, PLATFORM_SFU_APPLIED_SCOPE_KEY, PLATFORM_UPTIME_ACCESS_CLIENT_ID_REF, PLATFORM_UPTIME_ACCESS_CLIENT_SECRET_REF, PLATFORM_UPTIME_ACCESS_TOKEN_ID_SCOPE_KEY, PLATFORM_UPTIME_ACCESS_TOKEN_NAME, PLATFORM_UPTIME_ACCESS_UNDELIVERED_SCOPE_KEY, PLATFORM_UPTIME_CHAT_ROOM_ID_SCOPE_KEY, PLATFORM_UPTIME_HEARTBEAT_CHECK_NAME, PLATFORM_UPTIME_KEY, PLATFORM_UPTIME_REMOVED_SCOPE_KEY, PLATFORM_UPTIME_SCRIPT_NAME, PLATFORM_UPTIME_TOKEN_ID_SCOPE_KEY, PLATFORM_UPTIME_TOKEN_REF, PLATFORM_ZITADEL_ORG_SCOPE_KEY, archonFilesMarker, archonFirstOwnerLink, archonOrgDomain, archonOrgName, desiredHeartbeatCheckNames, heartbeatCheckUrlRef, orgDeclaresMcp, parseBrokerDeployRecord, parseLoginClientRecord, platformBackupHeartbeatCheckName, platformMailHostPorts, platformMailMxHost, uptimeAccessTokenConflict, uptimeEffectiveVars, uptimeRecorded, uptimeVarsDigest, } from './platform-diff.js';
|
|
36
|
+
import { AUTH_TUNNEL_ROUTES_VERSION, AUTH_ZITADEL_RUN_OPTIONS, GITHUB_RELEASE_HOOK_DEPS_MISSING, GITHUB_RELEASE_WEBHOOK_SECRET_ENV, JOBS_DEPLOY_PENDING_SCOPE_KEY, JOBS_INTERNAL_URL_SCOPE_KEY, JOBS_SCHEDULER_KEY, PLATFORM_ACCESS_PROVIDER_APPLIED_AUTH_DOMAIN_SCOPE_KEY, PLATFORM_ACCESS_PROVIDER_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_ACCESS_PROVIDER_UNDELIVERED_SCOPE_KEY, PLATFORM_ACCESS_ZITADEL_APP_SCOPE_KEY, PLATFORM_ACCESS_ZITADEL_PROJECT_SCOPE_KEY, PLATFORM_AI_APPLIED_GITREF_SCOPE_KEY, PLATFORM_AI_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_AI_CONNECTOR_APP_SCOPE_KEY, PLATFORM_AI_DEPLOY_PENDING_SCOPE_KEY, PLATFORM_AI_INTERNAL_URL_SCOPE_KEY, PLATFORM_AI_KEY, PLATFORM_AI_MANAGEMENT_KEY_CONFLICT_SCOPE_KEY, PLATFORM_AI_MANAGEMENT_KEY_SCOPE_KEY, PLATFORM_AI_MANAGEMENT_KEY_UNDELIVERED_SCOPE_KEY, PLATFORM_AI_MIGRATE_BLOCKED_SCOPE_KEY, PLATFORM_AI_ORGANISATION_SCOPE_KEY, PLATFORM_AI_SIGNING_KEY_SET_SCOPE_KEY, PLATFORM_AI_SIGNING_KEY_UNDELIVERED_SCOPE_KEY, PLATFORM_AI_TUNNEL_SCOPE_KEY, PLATFORM_ARCHON_ACCESS_PIN_SCOPE_KEY, PLATFORM_ARCHON_APPLIED_AUTH_HOSTNAME_SCOPE_KEY, PLATFORM_ARCHON_APPLIED_AUTH_ORG_SCOPE_KEY, PLATFORM_ARCHON_APPLIED_BROKER_SCOPE_KEY, PLATFORM_ARCHON_APPLIED_CI_ORG_SCOPE_KEY, PLATFORM_ARCHON_APPLIED_FILES_SCOPE_KEY, PLATFORM_ARCHON_APPLIED_FIRST_OWNER_LINK_SCOPE_KEY, PLATFORM_ARCHON_APPLIED_GIT_REF_SCOPE_KEY, PLATFORM_ARCHON_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_ARCHON_APPLIED_ROLLING_UPDATE_SCOPE_KEY, PLATFORM_ARCHON_APP_KEY_ORG_SCOPE_KEY, PLATFORM_ARCHON_CONNECTOR_APP_SCOPE_KEY, PLATFORM_ARCHON_COOLIFY_READ_TOKEN_REF, PLATFORM_ARCHON_DEPLOYMENT_SCOPE_KEY, PLATFORM_ARCHON_DESKTOP_CALLBACK_APPLIED_SCOPE_KEY, PLATFORM_ARCHON_ENVS_DEPLOYED_SCOPE_KEY, PLATFORM_ARCHON_GATE_CLOSED_SCOPE_KEY, PLATFORM_ARCHON_ID_TOKEN_USERINFO_APPLIED_SCOPE_KEY, PLATFORM_ARCHON_INTEGRATIONS_KEY_SYNCED_SCOPE_KEY, PLATFORM_ARCHON_KEY, PLATFORM_ARCHON_MASTER_CLOUDFLARE_TOKEN_REF, PLATFORM_ARCHON_MASTER_COOLIFY_TOKEN_REF, PLATFORM_ARCHON_MASTER_COOLIFY_URL_REF, PLATFORM_ARCHON_ORG_DOMAIN_BLOCKED_SCOPE_KEY, PLATFORM_ARCHON_ORG_DOMAIN_SCOPE_KEY, PLATFORM_ARCHON_ORG_POLICY_SCOPE_KEY, PLATFORM_ARCHON_ORG_REGISTRATION_CLOSED_SCOPE_KEY, PLATFORM_ARCHON_ORG_SCOPE_KEY, PLATFORM_ARCHON_OWNER_INVITE_ORG_SCOPE_KEY, PLATFORM_ARCHON_OWNER_INVITE_SCOPE_KEY, PLATFORM_ARCHON_POST_LOGOUT_APPLIED_SCOPE_KEY, PLATFORM_ARCHON_RETIRED_CLIENT_SCOPE_KEY, PLATFORM_ARCHON_UPTIME_TICKS_SYNCED_SCOPE_KEY, PLATFORM_ARCHON_UPTIME_TOKEN_SYNCED_SCOPE_KEY, PLATFORM_ARCHON_WEBHOOK_SECRET_DEPLOYED_SCOPE_KEY, PLATFORM_ARCHON_ZITADEL_APP_SCOPE_KEY, PLATFORM_ARCHON_ZITADEL_PROJECT_SCOPE_KEY, PLATFORM_AUTH_ADMIN_UNDELIVERED_SCOPE_KEY, PLATFORM_AUTH_ADMIN_USER_SCOPE_KEY, PLATFORM_AUTH_APPLIED_GIT_REF_SCOPE_KEY, PLATFORM_AUTH_BROKER_DEPLOYED_SCOPE_KEY, PLATFORM_AUTH_BROKER_SCOPE_KEY, PLATFORM_AUTH_CONNECTOR_APP_SCOPE_KEY, PLATFORM_AUTH_INSTANCE_SCOPE_KEY, PLATFORM_AUTH_INSTANCE_UNDELIVERED_SCOPE_KEY, PLATFORM_AUTH_KEY, PLATFORM_AUTH_LOGIN_CLIENT_UNDELIVERED_SCOPE_KEY, PLATFORM_AUTH_LOGIN_CLIENT_USER_SCOPE_KEY, PLATFORM_AUTH_MCP_SCOPE_KEY, PLATFORM_AUTH_OWNER_INVITE_ORG_SCOPE_KEY, PLATFORM_AUTH_OWNER_INVITE_SCOPE_KEY, PLATFORM_AUTH_REGISTRATION_CLOSED_SCOPE_KEY, PLATFORM_AUTH_RESERVED_ADMIN_USERNAME_SCOPE_KEY, PLATFORM_AUTH_SMTP_SCOPE_KEY, PLATFORM_AUTH_TUNNEL_ROUTES_SCOPE_KEY, PLATFORM_AUTH_ZITADEL_APPLIED_IMAGE_TAG_SCOPE_KEY, PLATFORM_AUTH_ZITADEL_RUN_OPTIONS_SCOPE_KEY, PLATFORM_BACKUP_DATABASE_SCOPE_KEYS, PLATFORM_BACKUP_SERVICE_KEYS, PLATFORM_EDGE_APPLIED_GIT_REF_SCOPE_KEY, PLATFORM_EDGE_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_EDGE_CLOUDFLARE_TOKEN_REF, PLATFORM_EDGE_INTERNAL_URL_SCOPE_KEY, PLATFORM_EDGE_KEY, PLATFORM_EDGE_KEY_REF, PLATFORM_EDGE_PLATFORM_KEY_SET_SCOPE_KEY, PLATFORM_EDGE_PLATFORM_KEY_UNDELIVERED_SCOPE_KEY, PLATFORM_FILES_APPLIED_GIT_REF_SCOPE_KEY, PLATFORM_FILES_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_FILES_INTERNAL_URL_SCOPE_KEY, PLATFORM_FILES_INTROSPECTION_SCOPE_KEY, PLATFORM_FILES_INTROSPECTION_UNDELIVERED_SCOPE_KEY, PLATFORM_FILES_KEY, PLATFORM_FILES_MANAGEMENT_KEY_SCOPE_KEY, PLATFORM_FILES_MANAGEMENT_UNDELIVERED_SCOPE_KEY, PLATFORM_FILES_ZITADEL_PROJECT_SCOPE_KEY, PLATFORM_GITHUB_RELEASE_HOOK_KEY, PLATFORM_HC_ACCESS_SCOPE_KEY, PLATFORM_HC_API_KEY_REF, PLATFORM_HC_APPLIED_GIT_REF_SCOPE_KEY, PLATFORM_HC_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_HC_KEY, PLATFORM_HEARTBEATS_APPLIED_SCOPE_KEY, PLATFORM_HEARTBEATS_HOSTED_API_KEY_REF, PLATFORM_INTEGRATIONS_APPLIED_GITREF_SCOPE_KEY, PLATFORM_INTEGRATIONS_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_INTEGRATIONS_KEY, PLATFORM_INTEGRATIONS_KEY_ID_SCOPE_KEY, PLATFORM_INTEGRATIONS_KEY_ROTATION_PREVIOUS_SCOPE_KEY, PLATFORM_INTEGRATIONS_KEY_ROTATION_REQUESTED_SCOPE_KEY, PLATFORM_INTEGRATIONS_KEY_ROTATION_UNDELIVERED_SCOPE_KEY, PLATFORM_INTEGRATIONS_KEY_SET_SCOPE_KEY, PLATFORM_INTEGRATIONS_KEY_UNDELIVERED_SCOPE_KEY, PLATFORM_INTEGRATIONS_RUNNER_KEYRING_DEPLOYED_SCOPE_KEY, PLATFORM_MAIL_ADMIN_KEY_SCOPE_KEY, PLATFORM_MAIL_ADMIN_UNDELIVERED_SCOPE_KEY, PLATFORM_MAIL_APPLIED_GIT_REF_SCOPE_KEY, PLATFORM_MAIL_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_MAIL_CONNECTOR_APP_SCOPE_KEY, PLATFORM_MAIL_HOST_PORTS_DIRECT, PLATFORM_MAIL_HOST_PORTS_SCOPE_KEY, PLATFORM_MAIL_INTERNAL_URL_SCOPE_KEY, PLATFORM_MAIL_KEY, PLATFORM_MAIL_RELAY_KEY, PLATFORM_MAIL_ROLE_CHECK_SCOPE_KEY, PLATFORM_MAIL_ROLE_ENSURED_SCOPE_KEY, PLATFORM_MAIL_ROLE_GRANTED_SCOPE_KEY, PLATFORM_MAIL_ROUTE_SCOPE_KEY, PLATFORM_MAIL_ROUTE_WANTED, PLATFORM_MAIL_USER_ROLE, PLATFORM_MAIL_ZITADEL_PROJECT_SCOPE_KEY, PLATFORM_OWNER_ORG_DOMAIN_BLOCKED_SCOPE_KEY, PLATFORM_OWNER_ORG_DOMAIN_SCOPE_KEY, PLATFORM_OWNER_ORG_SCOPE_KEY, PLATFORM_SFU_APPLIED_SCOPE_KEY, PLATFORM_UPTIME_ACCESS_CLIENT_ID_REF, PLATFORM_UPTIME_ACCESS_CLIENT_SECRET_REF, PLATFORM_UPTIME_ACCESS_TOKEN_ID_SCOPE_KEY, PLATFORM_UPTIME_ACCESS_TOKEN_NAME, PLATFORM_UPTIME_ACCESS_UNDELIVERED_SCOPE_KEY, PLATFORM_UPTIME_AUDIT_SEAL_TICK_ID_SCOPE_KEY, PLATFORM_UPTIME_AUDIT_SEAL_TICK_TOKEN_REF, PLATFORM_UPTIME_CHAT_ROOM_ID_SCOPE_KEY, PLATFORM_UPTIME_HEARTBEAT_CHECK_NAME, PLATFORM_UPTIME_KEY, PLATFORM_UPTIME_RELEASE_TICK_ID_SCOPE_KEY, PLATFORM_UPTIME_RELEASE_TICK_TOKEN_REF, PLATFORM_UPTIME_REMOVED_SCOPE_KEY, PLATFORM_UPTIME_SCRIPT_NAME, PLATFORM_UPTIME_TOKEN_ID_SCOPE_KEY, PLATFORM_UPTIME_TOKEN_REF, PLATFORM_ZITADEL_ORG_SCOPE_KEY, archonFilesMarker, archonFirstOwnerLink, archonOrgDomain, archonOrgName, desiredHeartbeatCheckNames, heartbeatCheckUrlRef, orgDeclaresMcp, parseBrokerDeployRecord, parseLoginClientRecord, platformBackupHeartbeatCheckName, platformMailHostPorts, platformMailMxHost, uptimeAccessTokenConflict, uptimeEffectiveVars, uptimeRecorded, uptimeTicksSyncValue, uptimeVarsDigest, } from './platform-diff.js';
|
|
37
37
|
import { PLATFORM_REGISTRY_KEY, PLATFORM_REGISTRY_LOGINS_KEY, PLATFORM_REGISTRY_PASSWORDS_SET_SCOPE_KEY, PLATFORM_REGISTRY_PULL_PASSWORD_REF, registryImageTagBlocked, } from './platform-registry-keys.js';
|
|
38
38
|
import { ensureRegistryLogins, provisionPlatformRegistry, registryLoginsDrifted, } from './platform-registry.js';
|
|
39
39
|
import { PLATFORM_ARCHON_IN_APP_SCOPE_KEY, archonInAppBack, archonInAppBind, archonInAppHost, archonInAppPoint, archonInAppUnbind, } from './sign-in-archon.js';
|
|
@@ -731,7 +731,10 @@ export class FoundryPlatformStepExecutor {
|
|
|
731
731
|
const accessHeld = Boolean((await deps.heldKeys.resolve(PLATFORM_UPTIME_ACCESS_CLIENT_ID_REF)) &&
|
|
732
732
|
(await deps.heldKeys.resolve(PLATFORM_UPTIME_ACCESS_CLIENT_SECRET_REF)));
|
|
733
733
|
blocked.uptimeHeld = {
|
|
734
|
-
token:
|
|
734
|
+
// The Worker token and both tick tokens: the step is not applied while one is missing.
|
|
735
|
+
token: Boolean((await deps.heldKeys.resolve(PLATFORM_UPTIME_TOKEN_REF)) &&
|
|
736
|
+
(await deps.heldKeys.resolve(PLATFORM_UPTIME_RELEASE_TICK_TOKEN_REF)) &&
|
|
737
|
+
(await deps.heldKeys.resolve(PLATFORM_UPTIME_AUDIT_SEAL_TICK_TOKEN_REF))),
|
|
735
738
|
access: accessHeld,
|
|
736
739
|
vars: uptimeVarsDigest(uptimeEffectiveVars(deps.vars, deps.chat && owner.chat && recordedIds[PLATFORM_UPTIME_CHAT_ROOM_ID_SCOPE_KEY]
|
|
737
740
|
? {
|
|
@@ -4888,6 +4891,21 @@ async function provisionPlatformArchon(context, deps, ownerCompany = null) {
|
|
|
4888
4891
|
const uptimeToken = uptimeTokenId ? await deps.heldKeys.resolve(PLATFORM_UPTIME_TOKEN_REF) : null;
|
|
4889
4892
|
if (uptimeToken)
|
|
4890
4893
|
envs.push({ key: 'UPTIME_TOKEN', value: uptimeToken });
|
|
4894
|
+
// The two tick tokens, the same way. Foundry's held value replaces one set by hand, so the Worker
|
|
4895
|
+
// and Archon hold the same value.
|
|
4896
|
+
const ticksWanted = platform.uptime.enabled &&
|
|
4897
|
+
Boolean(recorded[PLATFORM_UPTIME_RELEASE_TICK_ID_SCOPE_KEY]) &&
|
|
4898
|
+
Boolean(recorded[PLATFORM_UPTIME_AUDIT_SEAL_TICK_ID_SCOPE_KEY]);
|
|
4899
|
+
const releaseTickToken = ticksWanted
|
|
4900
|
+
? await deps.heldKeys.resolve(PLATFORM_UPTIME_RELEASE_TICK_TOKEN_REF)
|
|
4901
|
+
: null;
|
|
4902
|
+
const auditSealTickToken = ticksWanted
|
|
4903
|
+
? await deps.heldKeys.resolve(PLATFORM_UPTIME_AUDIT_SEAL_TICK_TOKEN_REF)
|
|
4904
|
+
: null;
|
|
4905
|
+
const ticksSyncValue = releaseTickToken && auditSealTickToken ? uptimeTicksSyncValue(recorded) : '';
|
|
4906
|
+
if (releaseTickToken && auditSealTickToken) {
|
|
4907
|
+
envs.push({ key: 'RELEASE_TICK_TOKEN', value: releaseTickToken }, { key: 'AUDIT_SEAL_TICK_TOKEN', value: auditSealTickToken });
|
|
4908
|
+
}
|
|
4891
4909
|
const changed = await envsDiffer(deps.coolify, appId, envs);
|
|
4892
4910
|
// #467: this run's intended env set, hashed before anything below writes
|
|
4893
4911
|
// it -- compared against {@link PLATFORM_ARCHON_ENVS_DEPLOYED_SCOPE_KEY}
|
|
@@ -4935,6 +4953,10 @@ async function provisionPlatformArchon(context, deps, ownerCompany = null) {
|
|
|
4935
4953
|
for (const stale of await deps.coolify.listEnvs(appId)) {
|
|
4936
4954
|
// foundry#542: a disabled Worker's token leaves Archon's env too.
|
|
4937
4955
|
const staleUptime = !platform.uptime.enabled && stale.key === 'UPTIME_TOKEN';
|
|
4956
|
+
// The tick tokens go only if foundry wrote them: a hand-set one is not ours to remove.
|
|
4957
|
+
const staleUptimeTick = !platform.uptime.enabled &&
|
|
4958
|
+
Boolean(recorded[PLATFORM_ARCHON_UPTIME_TICKS_SYNCED_SCOPE_KEY]) &&
|
|
4959
|
+
(stale.key === 'RELEASE_TICK_TOKEN' || stale.key === 'AUDIT_SEAL_TICK_TOKEN');
|
|
4938
4960
|
// People now come from the owner company's store, so a first operator set by an older apply goes.
|
|
4939
4961
|
const staleOperator = stale.key === 'AUTHZ_BOOTSTRAP_OPERATOR';
|
|
4940
4962
|
// The link is written only while both ids are known; one that is no longer wanted goes.
|
|
@@ -4943,6 +4965,7 @@ async function provisionPlatformArchon(context, deps, ownerCompany = null) {
|
|
|
4943
4965
|
const staleSignIn = !inAppWanted && stale.key === SIGN_IN_UPSTREAM_ENV;
|
|
4944
4966
|
if ((stale.key === 'FILES_MANAGEMENT_KEY' ||
|
|
4945
4967
|
staleUptime ||
|
|
4968
|
+
staleUptimeTick ||
|
|
4946
4969
|
staleOperator ||
|
|
4947
4970
|
staleLink ||
|
|
4948
4971
|
staleSignIn) &&
|
|
@@ -5252,6 +5275,16 @@ async function provisionPlatformArchon(context, deps, ownerCompany = null) {
|
|
|
5252
5275
|
(!selfRedeploy || deployState === 'finished')) {
|
|
5253
5276
|
await record(PLATFORM_ARCHON_UPTIME_TOKEN_SYNCED_SCOPE_KEY, '');
|
|
5254
5277
|
}
|
|
5278
|
+
if (!platform.uptime.enabled &&
|
|
5279
|
+
recorded[PLATFORM_ARCHON_UPTIME_TICKS_SYNCED_SCOPE_KEY] &&
|
|
5280
|
+
(!selfRedeploy || deployState === 'finished')) {
|
|
5281
|
+
await record(PLATFORM_ARCHON_UPTIME_TICKS_SYNCED_SCOPE_KEY, '');
|
|
5282
|
+
}
|
|
5283
|
+
if (ticksSyncValue &&
|
|
5284
|
+
recorded[PLATFORM_ARCHON_UPTIME_TICKS_SYNCED_SCOPE_KEY] !== ticksSyncValue &&
|
|
5285
|
+
(!selfRedeploy || deployState === 'finished')) {
|
|
5286
|
+
await record(PLATFORM_ARCHON_UPTIME_TICKS_SYNCED_SCOPE_KEY, ticksSyncValue);
|
|
5287
|
+
}
|
|
5255
5288
|
if (uptimeToken &&
|
|
5256
5289
|
uptimeTokenId &&
|
|
5257
5290
|
recorded[PLATFORM_ARCHON_UPTIME_TOKEN_SYNCED_SCOPE_KEY] !== uptimeTokenId &&
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ZitadelError } from '@wtfalch/providers/zitadel';
|
|
2
2
|
import { APP_ENTRY_KEY } from '../org.js';
|
|
3
|
+
import { ANALYTICS_MANAGED_ENV_KEYS } from './analytics.js';
|
|
3
4
|
import { BOULE_MAIL_TOKEN_ENV, BOULE_MANAGED_ENV_KEYS } from './boule.js';
|
|
4
5
|
import { previewsOn } from './coolify-previews.js';
|
|
5
6
|
import { KOSMOS_MANAGED_ENV_KEYS } from './kosmos.js';
|
|
@@ -320,6 +321,19 @@ async function withKosmosEnvConfig(client, resource, recorded) {
|
|
|
320
321
|
}
|
|
321
322
|
return { ...resource, config: { ...resource.config, env } };
|
|
322
323
|
}
|
|
324
|
+
/** The recorded analytics backend's live env (`APP_URL`, `AUTH_ORGANIZATION_ID`) as `config.env`, read like {@link withKosmosEnvConfig}. */
|
|
325
|
+
async function withAnalyticsEnvConfig(client, resource, recorded) {
|
|
326
|
+
if (resource.kind !== 'application' || !recorded[resource.key]?.startsWith('analytics:')) {
|
|
327
|
+
return resource;
|
|
328
|
+
}
|
|
329
|
+
const managed = ANALYTICS_MANAGED_ENV_KEYS;
|
|
330
|
+
const env = {};
|
|
331
|
+
for (const e of await client.listEnvs(resource.key)) {
|
|
332
|
+
if (!e.is_preview && managed.includes(e.key))
|
|
333
|
+
env[e.key] = e.value;
|
|
334
|
+
}
|
|
335
|
+
return { ...resource, config: { ...resource.config, env } };
|
|
336
|
+
}
|
|
323
337
|
/** `@wtfalch/providers`' own `LiveResource` (`resource.ts`) has no `system` field — foundry's adds it at this boundary. */
|
|
324
338
|
function toFoundryResources(system, resources) {
|
|
325
339
|
return resources.map((r) => ({ system, kind: r.kind, key: r.key, config: r.config }));
|
|
@@ -354,7 +368,8 @@ export class ProvidersCoolifyReader {
|
|
|
354
368
|
const liveKeys = new Set(withAgoraConfig.map((r) => r.key));
|
|
355
369
|
const withBouleConfig = await Promise.all(withAgoraConfig.map((r) => withBouleEnvConfig(this.client, r, recorded, liveKeys)));
|
|
356
370
|
const withKosmosConfig = await Promise.all(withBouleConfig.map((r) => withKosmosEnvConfig(this.client, r, recorded)));
|
|
357
|
-
|
|
371
|
+
const withAnalyticsConfig = await Promise.all(withKosmosConfig.map((r) => withAnalyticsEnvConfig(this.client, r, recorded)));
|
|
372
|
+
return applyRecordedIds(withAnalyticsConfig, recorded);
|
|
358
373
|
}
|
|
359
374
|
async liveEnvEquals(orgId, appKey, name, value) {
|
|
360
375
|
const recorded = await this.resolveRecordedIds(orgId);
|
|
@@ -5,7 +5,7 @@ import { type StageKind } from './stage-isolation.js';
|
|
|
5
5
|
* minted for it only. A stage key is worthless on production, and production's key never reaches stage. Nothing
|
|
6
6
|
* here returns, logs or throws a secret: errors carry fixed text, ids and refs (CLAUDE.md, Platform steps).
|
|
7
7
|
*/
|
|
8
|
-
export type StageService = 'files' | 'ai' | 'jobs';
|
|
8
|
+
export type StageService = 'files' | 'ai' | 'jobs' | 'analytics';
|
|
9
9
|
export declare const STAGE_SERVICES: readonly StageService[];
|
|
10
10
|
/** One credential a service shows once: the env name stage holds it under, and its value. */
|
|
11
11
|
export interface StageSecret {
|
|
@@ -44,6 +44,8 @@ export interface StageServicesPort {
|
|
|
44
44
|
files?: StageServicePort;
|
|
45
45
|
ai?: StageServicePort;
|
|
46
46
|
jobs?: StageServicePort;
|
|
47
|
+
/** The company's analytics backend: stage's own site key (environment `stage`), shown once with the mint. */
|
|
48
|
+
analytics?: StageServicePort;
|
|
47
49
|
/** The id of production's jobs project, so stage's jobs key can be tried against it. */
|
|
48
50
|
prodJobsProjectId?: string;
|
|
49
51
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { bgKey } from './blue-green.js';
|
|
3
3
|
import { isAllowed } from './stage-isolation.js';
|
|
4
|
-
export const STAGE_SERVICES = ['files', 'ai', 'jobs'];
|
|
4
|
+
export const STAGE_SERVICES = ['files', 'ai', 'jobs', 'analytics'];
|
|
5
5
|
/** Fixed text and ids only: safe to log, record and return. */
|
|
6
6
|
export class StageServiceError extends Error {
|
|
7
7
|
name = 'StageServiceError';
|
|
@@ -14,6 +14,7 @@ const SERVICE_ENV = {
|
|
|
14
14
|
evidence: ['JOBS_SCHEDULER_URL', 'JOBS_SYNC_KEY', 'JOBS_SIGNING_SECRET'],
|
|
15
15
|
url: 'JOBS_SCHEDULER_URL',
|
|
16
16
|
},
|
|
17
|
+
analytics: { evidence: ['ANALYTICS_URL', 'ANALYTICS_SITE_KEY'], url: 'ANALYTICS_URL' },
|
|
17
18
|
};
|
|
18
19
|
/** The fixed reasons a used service is recorded absent. */
|
|
19
20
|
export const STAGE_SERVICE_ABSENT = {
|
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import { randomBytes, randomUUID } from 'node:crypto';
|
|
2
2
|
import { EdgeApiError } from '@wtfalch/edge';
|
|
3
3
|
import { uptimeArtifactUrl } from '../platform.js';
|
|
4
|
-
import { PLATFORM_EDGE_INTERNAL_URL_SCOPE_KEY, PLATFORM_UPTIME_ACCESS_CLIENT_ID_REF, PLATFORM_UPTIME_ACCESS_CLIENT_SECRET_REF, PLATFORM_UPTIME_ACCESS_TOKEN_ID_SCOPE_KEY, PLATFORM_UPTIME_ACCESS_TOKEN_NAME, PLATFORM_UPTIME_ACCESS_UNDELIVERED_SCOPE_KEY, PLATFORM_UPTIME_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_UPTIME_APPLIED_SECRETS_SCOPE_KEY, PLATFORM_UPTIME_APPLIED_SHA256_SCOPE_KEY, PLATFORM_UPTIME_APPLIED_TAG_SCOPE_KEY, PLATFORM_UPTIME_APPLIED_VARS_SCOPE_KEY, PLATFORM_UPTIME_CHAT_ROOM_ID_SCOPE_KEY, PLATFORM_UPTIME_CHAT_TOKEN_REF, PLATFORM_UPTIME_CHAT_UNDELIVERED_SCOPE_KEY, PLATFORM_UPTIME_HEARTBEAT_URL_REF, PLATFORM_UPTIME_KEY, PLATFORM_UPTIME_KV_TITLE, PLATFORM_UPTIME_OPTIONAL_SECRETS, PLATFORM_UPTIME_REMOVED_SCOPE_KEY, PLATFORM_UPTIME_SCRIPT_NAME, PLATFORM_UPTIME_TOKEN_ID_SCOPE_KEY, PLATFORM_UPTIME_TOKEN_REF, PLATFORM_UPTIME_TOKEN_UNDELIVERED_SCOPE_KEY, uptimeAccessTokenConflict, uptimeEffectiveVars, uptimeSecretNames, uptimeVarsDigest, } from './platform-diff.js';
|
|
5
|
-
/**
|
|
6
|
-
|
|
4
|
+
import { PLATFORM_EDGE_INTERNAL_URL_SCOPE_KEY, PLATFORM_UPTIME_ACCESS_CLIENT_ID_REF, PLATFORM_UPTIME_ACCESS_CLIENT_SECRET_REF, PLATFORM_UPTIME_ACCESS_TOKEN_ID_SCOPE_KEY, PLATFORM_UPTIME_ACCESS_TOKEN_NAME, PLATFORM_UPTIME_ACCESS_UNDELIVERED_SCOPE_KEY, PLATFORM_UPTIME_APPLIED_HOSTNAME_SCOPE_KEY, PLATFORM_UPTIME_APPLIED_SECRETS_SCOPE_KEY, PLATFORM_UPTIME_APPLIED_SHA256_SCOPE_KEY, PLATFORM_UPTIME_APPLIED_TAG_SCOPE_KEY, PLATFORM_UPTIME_APPLIED_VARS_SCOPE_KEY, PLATFORM_UPTIME_AUDIT_SEAL_TICK_ID_SCOPE_KEY, PLATFORM_UPTIME_AUDIT_SEAL_TICK_TOKEN_REF, PLATFORM_UPTIME_AUDIT_SEAL_TICK_UNDELIVERED_SCOPE_KEY, PLATFORM_UPTIME_CHAT_ROOM_ID_SCOPE_KEY, PLATFORM_UPTIME_CHAT_TOKEN_REF, PLATFORM_UPTIME_CHAT_UNDELIVERED_SCOPE_KEY, PLATFORM_UPTIME_HEARTBEAT_URL_REF, PLATFORM_UPTIME_KEY, PLATFORM_UPTIME_KV_TITLE, PLATFORM_UPTIME_OPTIONAL_SECRETS, PLATFORM_UPTIME_RELEASE_TICK_ID_SCOPE_KEY, PLATFORM_UPTIME_RELEASE_TICK_TOKEN_REF, PLATFORM_UPTIME_RELEASE_TICK_UNDELIVERED_SCOPE_KEY, PLATFORM_UPTIME_REMOVED_SCOPE_KEY, PLATFORM_UPTIME_SCRIPT_NAME, PLATFORM_UPTIME_TOKEN_ID_SCOPE_KEY, PLATFORM_UPTIME_TOKEN_REF, PLATFORM_UPTIME_TOKEN_UNDELIVERED_SCOPE_KEY, uptimeAccessTokenConflict, uptimeEffectiveVars, uptimeSecretNames, uptimeVarsDigest, } from './platform-diff.js';
|
|
5
|
+
/**
|
|
6
|
+
* The Worker's cron triggers, in the order of Archon's `workers/uptime/manifest.json`: the probe, the
|
|
7
|
+
* usage tick, the charge tick, the release tick and the audit seal tick. The usage and charge ticks
|
|
8
|
+
* stay off because the step gives the Worker no `USAGE_TOKEN` or `CHARGE_TOKEN`.
|
|
9
|
+
*/
|
|
10
|
+
const UPTIME_CRONS = [
|
|
11
|
+
'* * * * *',
|
|
12
|
+
'0 * * * *',
|
|
13
|
+
'15 3 * * *',
|
|
14
|
+
'*/10 * * * *',
|
|
15
|
+
'*/15 * * * *',
|
|
16
|
+
];
|
|
7
17
|
/** The date Archon's own Worker config used. */
|
|
8
18
|
const UPTIME_COMPATIBILITY_DATE = '2026-09-27';
|
|
9
19
|
/** How many times a delivery or a secrets write is tried before the step fails. */
|
|
@@ -124,6 +134,27 @@ async function attempt(what, task) {
|
|
|
124
134
|
}
|
|
125
135
|
throw new Error(`platform:uptime: ${what} failed after ${UPTIME_ATTEMPTS} attempts`);
|
|
126
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* Holds a minted tick token under `ref`, the Worker token's pattern: the undelivered id is persisted
|
|
139
|
+
* before the value is held, the guard once it is. A guard with nothing held, or a marker without a
|
|
140
|
+
* guard, means the value is gone: a new one is minted and the old id dropped. Returns the held value
|
|
141
|
+
* and its id (the Worker secret's version label); the value goes nowhere else.
|
|
142
|
+
*/
|
|
143
|
+
async function holdTickToken(context, deps, recorded, keys) {
|
|
144
|
+
const guard = recorded[keys.idKey];
|
|
145
|
+
const heldValue = guard ? await deps.heldKeys.resolve(keys.ref) : null;
|
|
146
|
+
if (guard && heldValue)
|
|
147
|
+
return { id: guard, value: heldValue };
|
|
148
|
+
const id = randomUUID();
|
|
149
|
+
const value = randomBytes(32).toString('hex');
|
|
150
|
+
context.recordScopeId(keys.undeliveredKey, id);
|
|
151
|
+
await context.persist?.();
|
|
152
|
+
await attempt(`holding token ${id}`, () => deps.deliverSecret(keys.ref, value));
|
|
153
|
+
context.recordScopeId(keys.idKey, id);
|
|
154
|
+
context.recordScopeId(keys.undeliveredKey, '');
|
|
155
|
+
await context.persist?.();
|
|
156
|
+
return { id, value };
|
|
157
|
+
}
|
|
127
158
|
/** Removes an Access token edge made; one already gone is success. */
|
|
128
159
|
async function removeAccessToken(access, id, what) {
|
|
129
160
|
try {
|
|
@@ -165,6 +196,8 @@ export async function provisionPlatformUptime(context, deps) {
|
|
|
165
196
|
}
|
|
166
197
|
for (const ref of [
|
|
167
198
|
PLATFORM_UPTIME_TOKEN_REF,
|
|
199
|
+
PLATFORM_UPTIME_RELEASE_TICK_TOKEN_REF,
|
|
200
|
+
PLATFORM_UPTIME_AUDIT_SEAL_TICK_TOKEN_REF,
|
|
168
201
|
PLATFORM_UPTIME_ACCESS_CLIENT_ID_REF,
|
|
169
202
|
PLATFORM_UPTIME_ACCESS_CLIENT_SECRET_REF,
|
|
170
203
|
]) {
|
|
@@ -175,6 +208,10 @@ export async function provisionPlatformUptime(context, deps) {
|
|
|
175
208
|
PLATFORM_UPTIME_ACCESS_UNDELIVERED_SCOPE_KEY,
|
|
176
209
|
PLATFORM_UPTIME_TOKEN_ID_SCOPE_KEY,
|
|
177
210
|
PLATFORM_UPTIME_TOKEN_UNDELIVERED_SCOPE_KEY,
|
|
211
|
+
PLATFORM_UPTIME_RELEASE_TICK_ID_SCOPE_KEY,
|
|
212
|
+
PLATFORM_UPTIME_RELEASE_TICK_UNDELIVERED_SCOPE_KEY,
|
|
213
|
+
PLATFORM_UPTIME_AUDIT_SEAL_TICK_ID_SCOPE_KEY,
|
|
214
|
+
PLATFORM_UPTIME_AUDIT_SEAL_TICK_UNDELIVERED_SCOPE_KEY,
|
|
178
215
|
]) {
|
|
179
216
|
context.recordScopeId(key, '');
|
|
180
217
|
}
|
|
@@ -280,6 +317,17 @@ export async function provisionPlatformUptime(context, deps) {
|
|
|
280
317
|
tokenId = newId;
|
|
281
318
|
token = minted;
|
|
282
319
|
}
|
|
320
|
+
// The two tick tokens, minted and held the same way, each its own value.
|
|
321
|
+
const releaseTick = await holdTickToken(context, deps, recorded, {
|
|
322
|
+
ref: PLATFORM_UPTIME_RELEASE_TICK_TOKEN_REF,
|
|
323
|
+
idKey: PLATFORM_UPTIME_RELEASE_TICK_ID_SCOPE_KEY,
|
|
324
|
+
undeliveredKey: PLATFORM_UPTIME_RELEASE_TICK_UNDELIVERED_SCOPE_KEY,
|
|
325
|
+
});
|
|
326
|
+
const auditSealTick = await holdTickToken(context, deps, recorded, {
|
|
327
|
+
ref: PLATFORM_UPTIME_AUDIT_SEAL_TICK_TOKEN_REF,
|
|
328
|
+
idKey: PLATFORM_UPTIME_AUDIT_SEAL_TICK_ID_SCOPE_KEY,
|
|
329
|
+
undeliveredKey: PLATFORM_UPTIME_AUDIT_SEAL_TICK_UNDELIVERED_SCOPE_KEY,
|
|
330
|
+
});
|
|
283
331
|
// The Access service token pair, minted through edge. The id is recorded
|
|
284
332
|
// and persisted before the pair is held, the guard once both are held. A
|
|
285
333
|
// pair that may be lost (marker without guard, or guard with nothing held)
|
|
@@ -341,6 +389,8 @@ export async function provisionPlatformUptime(context, deps) {
|
|
|
341
389
|
const version = `pin-${uptime.sha256.slice(0, 12)}`;
|
|
342
390
|
const secrets = [
|
|
343
391
|
{ name: 'UPTIME_TOKEN', value: token, version: tokenId },
|
|
392
|
+
{ name: 'RELEASE_TICK_TOKEN', value: releaseTick.value, version: releaseTick.id },
|
|
393
|
+
{ name: 'AUDIT_SEAL_TICK_TOKEN', value: auditSealTick.value, version: auditSealTick.id },
|
|
344
394
|
{ name: 'CF_ACCESS_CLIENT_ID', value: clientId, version: accessId },
|
|
345
395
|
{ name: 'CF_ACCESS_CLIENT_SECRET', value: clientSecret, version: accessId },
|
|
346
396
|
// A ping URL works as a capability: a secret, not a plain text binding.
|
package/dist/slug.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
export declare const RESERVED_SLUGS: ReadonlySet<string>;
|
|
3
3
|
/** Every platform app, tunnel, project and database name starts `platform-`, so no company slug may. */
|
|
4
4
|
export declare const RESERVED_SLUG_PREFIX = "platform-";
|
|
5
|
-
/** Whether `label` ends in a suffix reserved for another org's own hostname (`-stage`, `-pr-<n>`, `-calls`, `-agora`, `-boule`, `-kosmos`, `-auth`, `-mail`, `-files`, `-sfu`, `-cms`). */
|
|
5
|
+
/** Whether `label` ends in a suffix reserved for another org's own hostname (`-stage`, `-pr-<n>`, `-calls`, `-agora`, `-boule`, `-kosmos`, `-analytics`, `-auth`, `-mail`, `-files`, `-sfu`, `-cms`). */
|
|
6
6
|
export declare function hasBarredSlugSuffix(label: string): boolean;
|
|
7
7
|
/**
|
|
8
8
|
* Why {@link validateSlug} refused a candidate slug, by name so a caller —
|
package/dist/slug.js
CHANGED
|
@@ -146,6 +146,8 @@ const BOULE_SUFFIX = /-boule$/;
|
|
|
146
146
|
* hostname once kosmos is on, not because every org gets one.
|
|
147
147
|
*/
|
|
148
148
|
const KOSMOS_SUFFIX = /-kosmos$/;
|
|
149
|
+
/** `<something>-analytics` — a level-1 org's own analytics backend hostname (`<slug>-analytics.oikos.host`, foundry#724). Reserved always, so a company that turns analytics on later never finds the name taken. */
|
|
150
|
+
const ANALYTICS_SUFFIX = /-analytics$/;
|
|
149
151
|
/** `<something>-auth|-mail|-files|-sfu|-cms` — an own company's service hostnames on oikos.host (`plan/own-hostnames.ts`). */
|
|
150
152
|
const OWN_SERVICE_SUFFIX = /-(auth|mail|files|sfu|cms)$/;
|
|
151
153
|
/**
|
|
@@ -159,7 +161,7 @@ const OWN_SERVICE_SUFFIX = /-(auth|mail|files|sfu|cms)$/;
|
|
|
159
161
|
* `names-`.
|
|
160
162
|
*/
|
|
161
163
|
const NAMES_PREFIX = /^names(-|$)/;
|
|
162
|
-
/** Whether `label` ends in a suffix reserved for another org's own hostname (`-stage`, `-pr-<n>`, `-calls`, `-agora`, `-boule`, `-kosmos`, `-auth`, `-mail`, `-files`, `-sfu`, `-cms`). */
|
|
164
|
+
/** Whether `label` ends in a suffix reserved for another org's own hostname (`-stage`, `-pr-<n>`, `-calls`, `-agora`, `-boule`, `-kosmos`, `-analytics`, `-auth`, `-mail`, `-files`, `-sfu`, `-cms`). */
|
|
163
165
|
export function hasBarredSlugSuffix(label) {
|
|
164
166
|
return (STAGE_SUFFIX.test(label) ||
|
|
165
167
|
PR_SUFFIX.test(label) ||
|
|
@@ -167,6 +169,7 @@ export function hasBarredSlugSuffix(label) {
|
|
|
167
169
|
AGORA_SUFFIX.test(label) ||
|
|
168
170
|
BOULE_SUFFIX.test(label) ||
|
|
169
171
|
KOSMOS_SUFFIX.test(label) ||
|
|
172
|
+
ANALYTICS_SUFFIX.test(label) ||
|
|
170
173
|
OWN_SERVICE_SUFFIX.test(label));
|
|
171
174
|
}
|
|
172
175
|
/**
|
|
@@ -198,7 +201,7 @@ export function validateSlug(slug) {
|
|
|
198
201
|
if (hasBarredSlugSuffix(slug)) {
|
|
199
202
|
return {
|
|
200
203
|
reason: 'environment-suffix',
|
|
201
|
-
message: `slug '${slug}' ends with an environment suffix ('-stage', '-pr-<digits>', '-calls', '-agora', '-boule', '-kosmos', '-auth', '-mail', '-files', '-sfu' or '-cms') reserved for another org's own hostname (#28, #145, #164, #161, #949) — 'acme-calls' is only legal as acme's calls hostname, 'acme-agora' only as acme's Agora hostname, 'acme-boule' only as acme's Boule hostname, 'acme-kosmos' only as acme's kosmos hostname, never as an org slug in its own right`,
|
|
204
|
+
message: `slug '${slug}' ends with an environment suffix ('-stage', '-pr-<digits>', '-calls', '-agora', '-boule', '-kosmos', '-analytics', '-auth', '-mail', '-files', '-sfu' or '-cms') reserved for another org's own hostname (#28, #145, #164, #161, #949) — 'acme-calls' is only legal as acme's calls hostname, 'acme-agora' only as acme's Agora hostname, 'acme-boule' only as acme's Boule hostname, 'acme-kosmos' only as acme's kosmos hostname, never as an org slug in its own right`,
|
|
202
205
|
};
|
|
203
206
|
}
|
|
204
207
|
return null;
|
|
@@ -196,9 +196,9 @@ export function validateEntryWorkerApp(entry, hosting, names) {
|
|
|
196
196
|
if (Object.keys(entry.env).length > 0) {
|
|
197
197
|
return `${at} has no Coolify app, so env does not apply: use workerApp.vars or workerApp.secrets`;
|
|
198
198
|
}
|
|
199
|
-
const unsupported = Object.keys(entry.uses ?? {}).find((use) => use !== 'auth' && use !== 'audit');
|
|
199
|
+
const unsupported = Object.keys(entry.uses ?? {}).find((use) => use !== 'auth' && use !== 'audit' && use !== 'analytics');
|
|
200
200
|
if (unsupported)
|
|
201
|
-
return `${at} supports uses.auth and uses.
|
|
201
|
+
return `${at} supports uses.auth, uses.audit and uses.analytics only, not uses.${unsupported}`;
|
|
202
202
|
if (entry.key.length > WORKER_NAME_MAX_LENGTH - WORKER_NAME_FIXED_LENGTH) {
|
|
203
203
|
return `${at} entry key is too long: the deployed Worker's name (an 8-character org hash, the key, and "${WORKER_APP_NAME_SUFFIX}") must fit Cloudflare's ${WORKER_NAME_MAX_LENGTH}-character limit`;
|
|
204
204
|
}
|