@wtfalch/foundry 0.65.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/apps/cloudflare.d.ts +5 -0
- package/dist/apps/deploy.js +33 -1
- package/dist/apps/migrations.d.ts +3 -0
- package/dist/apps/migrations.js +32 -7
- package/dist/catalogue/copy-en.js +5 -0
- package/dist/catalogue/integrations.js +5 -0
- package/dist/entry-rules.d.ts +11 -0
- package/dist/entry-rules.js +70 -0
- package/dist/index.d.ts +9 -4
- package/dist/index.js +5 -2
- package/dist/migrations/0069_release_events.sql +16 -0
- package/dist/modules/analytics.d.ts +8 -0
- package/dist/modules/analytics.js +28 -0
- package/dist/modules/database.js +22 -3
- package/dist/modules/index.js +2 -0
- package/dist/native-build.d.ts +16 -12
- package/dist/native-build.js +16 -12
- 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/boule.js +2 -0
- package/dist/plan/company-backups.js +2 -0
- package/dist/plan/diff.d.ts +4 -0
- package/dist/plan/diff.js +103 -3
- 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 +79 -6
- package/dist/plan/executor.js +379 -19
- 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/login-host.d.ts +8 -0
- package/dist/plan/login-host.js +8 -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 +12 -0
- package/dist/plan/plan-apply.js +40 -1
- package/dist/plan/platform-diff.d.ts +17 -1
- package/dist/plan/platform-diff.js +40 -2
- package/dist/plan/platform-org.d.ts +6 -0
- package/dist/plan/platform-providers.d.ts +8 -6
- package/dist/plan/platform-providers.js +56 -10
- package/dist/plan/providers-readers.js +16 -1
- package/dist/plan/release-host.d.ts +30 -1
- package/dist/plan/release-host.js +84 -41
- package/dist/plan/release-series-reader.d.ts +26 -0
- package/dist/plan/release-series-reader.js +40 -0
- package/dist/plan/sign-in-archon.d.ts +1 -1
- package/dist/plan/sign-in-archon.js +2 -2
- 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/plan/worker-app-database.d.ts +12 -1
- package/dist/plan/worker-app-database.js +13 -0
- package/dist/plan/worker-app.d.ts +27 -2
- package/dist/plan/worker-app.js +52 -7
- package/dist/plan/workspace.d.ts +2 -2
- package/dist/plan/workspace.js +3 -3
- package/dist/release/decide.d.ts +2 -0
- package/dist/release/decide.js +67 -10
- package/dist/release/events.d.ts +13 -0
- package/dist/release/events.js +67 -0
- package/dist/release/health.d.ts +5 -0
- package/dist/release/health.js +3 -3
- package/dist/release/run.d.ts +7 -2
- package/dist/release/run.js +8 -0
- package/dist/release/series.d.ts +58 -0
- package/dist/release/series.js +65 -0
- package/dist/release/state.d.ts +58 -1
- package/dist/release/state.js +45 -0
- package/dist/release/types.d.ts +44 -0
- package/dist/slug.d.ts +1 -1
- package/dist/slug.js +5 -2
- package/dist/store/postgres.js +29 -3
- package/dist/store/schema.d.ts +112 -0
- package/dist/store/schema.js +9 -0
- package/dist/worker-app-config.d.ts +22 -1
- package/dist/worker-app-config.js +73 -15
- package/dist/worker-app-migrations.d.ts +6 -2
- package/dist/worker-app-migrations.js +1 -0
- package/package.json +1 -1
package/dist/plan/org-names.d.ts
CHANGED
|
@@ -6,13 +6,13 @@ export type OrgNameSource = 'own' | 'hosting';
|
|
|
6
6
|
* hosting entries: Agora, Boule, the tunnel and its connector, and chat,
|
|
7
7
|
* calls and the cms site while they are on.
|
|
8
8
|
*/
|
|
9
|
-
export declare function companyServiceNames(org: Pick<DeclaredOrg, 'slug' | 'chat' | 'calls' | 'site' | 'kosmos'>): string[];
|
|
9
|
+
export declare function companyServiceNames(org: Pick<DeclaredOrg, 'slug' | 'chat' | 'calls' | 'site' | 'kosmos' | 'boule' | 'hosting'>): string[];
|
|
10
10
|
/**
|
|
11
11
|
* Every container name, network alias, database, tunnel and bucket name that
|
|
12
12
|
* foundry derives for an org: its own-service names when it owns services,
|
|
13
13
|
* and its hosting-entry and database names.
|
|
14
14
|
*/
|
|
15
|
-
export declare function orgDerivedNames(org: Pick<DeclaredOrg, 'slug' | 'domain' | 'services' | 'hosting' | 'chat' | 'calls' | 'site' | 'kosmos'>): {
|
|
15
|
+
export declare function orgDerivedNames(org: Pick<DeclaredOrg, 'slug' | 'domain' | 'services' | 'hosting' | 'chat' | 'calls' | 'site' | 'kosmos' | 'boule'>): {
|
|
16
16
|
name: string;
|
|
17
17
|
source: OrgNameSource;
|
|
18
18
|
}[];
|
|
@@ -26,5 +26,5 @@ export interface NameClash {
|
|
|
26
26
|
source: OrgNameSource;
|
|
27
27
|
}
|
|
28
28
|
/** Every name `org` derives that another org or the platform also derives. */
|
|
29
|
-
export declare function findNameClashes(org: Pick<DeclaredOrg, 'slug' | 'domain' | 'services' | 'hosting' | 'chat' | 'calls' | 'site' | 'kosmos'>, others: readonly Pick<DeclaredOrg, 'slug' | 'domain' | 'services' | 'hosting' | 'chat' | 'calls' | 'site' | 'kosmos'>[]): NameClash[];
|
|
29
|
+
export declare function findNameClashes(org: Pick<DeclaredOrg, 'slug' | 'domain' | 'services' | 'hosting' | 'chat' | 'calls' | 'site' | 'kosmos' | 'boule'>, others: readonly Pick<DeclaredOrg, 'slug' | 'domain' | 'services' | 'hosting' | 'chat' | 'calls' | 'site' | 'kosmos' | 'boule'>[]): NameClash[];
|
|
30
30
|
export declare function nameClashMessage(slug: string, clash: NameClash): string;
|
package/dist/plan/org-names.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { kosmosOn, ownsServices } from '../org.js';
|
|
2
2
|
import { entryHasDatabase } from '../worker-app-config.js';
|
|
3
|
+
import { analyticsWanted } from './analytics.js';
|
|
3
4
|
import { chatInternalNames, chatRekeyedInternalNames } from './chat-matrix.js';
|
|
4
5
|
import { workerAppHostname, workspaceHost, workspaceInboxHost } from './hostnames.js';
|
|
5
6
|
import { orgTarget, platformTarget } from './own-services.js';
|
|
@@ -20,6 +21,8 @@ export function companyServiceNames(org) {
|
|
|
20
21
|
`${s}-tunnel`,
|
|
21
22
|
`${s}-cloudflared`,
|
|
22
23
|
];
|
|
24
|
+
if (analyticsWanted(org))
|
|
25
|
+
names.push(`${s}-analytics`, `${s}-analytics-db`);
|
|
23
26
|
if (kosmosOn(org))
|
|
24
27
|
names.push(`${s}-kosmos`, `${s}-kosmos-db`);
|
|
25
28
|
if (org.chat?.enabled) {
|
|
@@ -113,6 +113,12 @@ export declare const LIMITS: {
|
|
|
113
113
|
autoDeploy: false | "declared";
|
|
114
114
|
capabilities: CapabilityTier;
|
|
115
115
|
};
|
|
116
|
+
readonly analytics: {
|
|
117
|
+
memory: string;
|
|
118
|
+
dbMemory: string;
|
|
119
|
+
autoDeploy: false | "declared";
|
|
120
|
+
capabilities: CapabilityTier;
|
|
121
|
+
};
|
|
116
122
|
readonly companyOther: {
|
|
117
123
|
memory: string;
|
|
118
124
|
dbMemory: string;
|
package/dist/plan/own-limits.js
CHANGED
|
@@ -583,8 +583,12 @@ 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;
|
|
590
|
+
/** True when that port has `deletableTables`. A `workerApp.database.noDelete` is blocked without it. */
|
|
591
|
+
workerAppNoDeleteCheck?: boolean;
|
|
588
592
|
/** ADR 0002: the platform's Cloudflare account id. A container image outside its registry is blocked. */
|
|
589
593
|
workerAppRegistryAccountId?: string;
|
|
590
594
|
/**
|
|
@@ -745,7 +749,9 @@ export declare class FoundryPlanApply implements PlanApply {
|
|
|
745
749
|
private readonly workerApps;
|
|
746
750
|
private readonly workerAppKeysUrl;
|
|
747
751
|
private readonly keysRegistrarUrl;
|
|
752
|
+
private readonly analyticsPort;
|
|
748
753
|
private readonly workerAppDatabase;
|
|
754
|
+
private readonly workerAppNoDeleteCheck;
|
|
749
755
|
private readonly workerAppRegistryAccountId;
|
|
750
756
|
private readonly ownerInviteMail;
|
|
751
757
|
private readonly hostFiles;
|
|
@@ -980,6 +986,12 @@ export declare class FoundryPlanApply implements PlanApply {
|
|
|
980
986
|
private repoKeysReadiness;
|
|
981
987
|
/** Which refs of the platform GitHub App hold no value now. A resolver that throws counts as not held; only refs are kept. */
|
|
982
988
|
private githubAppKeysReadiness;
|
|
989
|
+
/**
|
|
990
|
+
* The Worker apps' `heldSecret` secrets that hold a value now, by `<entry key>/<NAME>`, with the version of
|
|
991
|
+
* the value. A resolver that throws, or a name that fits no ref, counts as not held. The value is hashed and
|
|
992
|
+
* dropped here; only the version leaves.
|
|
993
|
+
*/
|
|
994
|
+
private workerAppSecretsReadiness;
|
|
983
995
|
/**
|
|
984
996
|
* The org's `heldSecret` envs: the refs that hold a value now, which of those differ from the live
|
|
985
997
|
* production env, which could not be read, and which fit no ref. A resolver that throws counts as
|
package/dist/plan/plan-apply.js
CHANGED
|
@@ -40,7 +40,7 @@ import { CLOUDFLARE_TUNNEL_APPS_HOSTNAMES_SCOPE_KEY, CLOUDFLARE_TUNNEL_ENTRY_ROU
|
|
|
40
40
|
import { finishStageCopy, runSubjectRelease, } from './release-host.js';
|
|
41
41
|
import { remoteDatabaseFactsFrom, remoteDatabaseKeys } from './remote-database.js';
|
|
42
42
|
import { STAGE_COPY_LOCK_MS, claimStageCopyLock, parseStageCopyLock, stageCopyLockValue, } from './stage-copy-host.js';
|
|
43
|
-
import { liveWorkerApps, workerAppTraceScopeKeys } from './worker-app.js';
|
|
43
|
+
import { heldSecretId, heldSecretVersion, liveWorkerApps, workerAppTraceScopeKeys, } from './worker-app.js';
|
|
44
44
|
import { liveWorkers, workerTraceScopeKeys } from './worker.js';
|
|
45
45
|
/** #441: the lease scope that serializes every mutation of one company, and the one for the shared platform. */
|
|
46
46
|
const companyScope = (orgId) => `company:${orgId}`;
|
|
@@ -188,7 +188,9 @@ export class FoundryPlanApply {
|
|
|
188
188
|
workerApps;
|
|
189
189
|
workerAppKeysUrl;
|
|
190
190
|
keysRegistrarUrl;
|
|
191
|
+
analyticsPort;
|
|
191
192
|
workerAppDatabase;
|
|
193
|
+
workerAppNoDeleteCheck;
|
|
192
194
|
workerAppRegistryAccountId;
|
|
193
195
|
ownerInviteMail;
|
|
194
196
|
hostFiles;
|
|
@@ -232,7 +234,9 @@ export class FoundryPlanApply {
|
|
|
232
234
|
this.workerApps = options.workerApps ?? false;
|
|
233
235
|
this.workerAppKeysUrl = options.workerAppKeysUrl;
|
|
234
236
|
this.keysRegistrarUrl = options.keysRegistrarUrl;
|
|
237
|
+
this.analyticsPort = options.analyticsPort ?? false;
|
|
235
238
|
this.workerAppDatabase = options.workerAppDatabase ?? false;
|
|
239
|
+
this.workerAppNoDeleteCheck = options.workerAppNoDeleteCheck ?? false;
|
|
236
240
|
this.workerAppRegistryAccountId = options.workerAppRegistryAccountId;
|
|
237
241
|
this.ownerInviteMail = options.ownerInviteMail ?? false;
|
|
238
242
|
this.hostFiles = options.filesOrganisation ?? false;
|
|
@@ -1625,7 +1629,9 @@ export class FoundryPlanApply {
|
|
|
1625
1629
|
workerApps: this.workerApps,
|
|
1626
1630
|
workerAppKeysUrl: this.workerAppKeysUrl,
|
|
1627
1631
|
keysRegistrarUrl: this.keysRegistrarUrl,
|
|
1632
|
+
analyticsPort: this.analyticsPort,
|
|
1628
1633
|
workerAppDatabase: this.workerAppDatabase,
|
|
1634
|
+
workerAppNoDeleteCheck: this.workerAppNoDeleteCheck,
|
|
1629
1635
|
workerAppRegistryAccountId: this.workerAppRegistryAccountId,
|
|
1630
1636
|
ownerInviteMail: this.ownerInviteMail,
|
|
1631
1637
|
otherSlugs,
|
|
@@ -1758,6 +1764,7 @@ export class FoundryPlanApply {
|
|
|
1758
1764
|
const base = {
|
|
1759
1765
|
...(await this.sharedPlatformReadiness()),
|
|
1760
1766
|
heldEnv: await this.heldEnvReadiness(org),
|
|
1767
|
+
workerAppSecrets: await this.workerAppSecretsReadiness(org),
|
|
1761
1768
|
...(org.hosting.github ? { githubAppKeys: await this.githubAppKeysReadiness() } : {}),
|
|
1762
1769
|
...(repoKeysInPlay(org, orgRecordedIds)
|
|
1763
1770
|
? { repoKeys: await this.repoKeysReadiness(org, orgRecordedIds) }
|
|
@@ -1805,6 +1812,32 @@ export class FoundryPlanApply {
|
|
|
1805
1812
|
}
|
|
1806
1813
|
return { store: true, missing };
|
|
1807
1814
|
}
|
|
1815
|
+
/**
|
|
1816
|
+
* The Worker apps' `heldSecret` secrets that hold a value now, by `<entry key>/<NAME>`, with the version of
|
|
1817
|
+
* the value. A resolver that throws, or a name that fits no ref, counts as not held. The value is hashed and
|
|
1818
|
+
* dropped here; only the version leaves.
|
|
1819
|
+
*/
|
|
1820
|
+
async workerAppSecretsReadiness(org) {
|
|
1821
|
+
const heldKeys = this.platformDeps?.heldKeys;
|
|
1822
|
+
const held = {};
|
|
1823
|
+
if (!heldKeys)
|
|
1824
|
+
return held;
|
|
1825
|
+
for (const entry of org.hosting.entries) {
|
|
1826
|
+
for (const [name, source] of Object.entries(entry.workerApp?.secrets ?? {})) {
|
|
1827
|
+
if (source.from !== 'heldSecret')
|
|
1828
|
+
continue;
|
|
1829
|
+
try {
|
|
1830
|
+
const value = await heldKeys.resolve(heldEnvRef(org.id, entry.key, name));
|
|
1831
|
+
if (value)
|
|
1832
|
+
held[heldSecretId(entry.key, name)] = heldSecretVersion(value);
|
|
1833
|
+
}
|
|
1834
|
+
catch {
|
|
1835
|
+
// Not held: the plan says the secret has no value yet, and the error is dropped.
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
return held;
|
|
1840
|
+
}
|
|
1808
1841
|
/**
|
|
1809
1842
|
* The org's `heldSecret` envs: the refs that hold a value now, which of those differ from the live
|
|
1810
1843
|
* production env, which could not be read, and which fit no ref. A resolver that throws counts as
|
|
@@ -2635,6 +2668,8 @@ const STEP_FIELDS = {
|
|
|
2635
2668
|
'cloudflare:worker': 'hosting.entries.worker',
|
|
2636
2669
|
'cloudflare:worker-app': 'hosting.entries.workerApp',
|
|
2637
2670
|
// F4 (#925): both hang off the company's Apps switch.
|
|
2671
|
+
'coolify:analytics': 'hosting.entries.uses.analytics',
|
|
2672
|
+
'coolify:analytics-boule': 'boule.apps',
|
|
2638
2673
|
'coolify:org-apps-admin': 'boule.apps',
|
|
2639
2674
|
'cloudflare:apps-db-route': 'boule.apps',
|
|
2640
2675
|
'cloudflare:apps-zones': 'boule.apps',
|
|
@@ -2658,6 +2693,10 @@ function stepField(action) {
|
|
|
2658
2693
|
// An entry's jobs project: always `<orgId>:<entry>`.
|
|
2659
2694
|
if (action.system === 'jobs' && action.kind === 'project')
|
|
2660
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
|
+
}
|
|
2661
2700
|
// An entry's audit ledger registration: always `<orgId>:<entry>`.
|
|
2662
2701
|
if (action.system === 'audit' && action.kind === 'ledger')
|
|
2663
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";
|
|
@@ -8,6 +8,7 @@ import { buildDriftNotes } from './build-record.js';
|
|
|
8
8
|
import { versionChangeNeedsConfirmation } from './confirm-upgrade.js';
|
|
9
9
|
import { ACCESS_PROVIDER_RECORD_KEY } from './gates.js';
|
|
10
10
|
import { legacyAppHostname } from './hostnames.js';
|
|
11
|
+
import { LOGIN_BASE_URI_SWEEP_SCOPE_KEY, LOGIN_BASE_URI_SWEEP_VERSION } from './login-host.js';
|
|
11
12
|
import { PLATFORM_MAIL_RELAY_KEY, planMailRelay } from './mail-relay.js';
|
|
12
13
|
import { MAIL_DATABASE_SCOPE_KEY, mailStoreIsLegacy, mailStoreSwitchChange, mailStoreSwitchDescription, } from './mail-store.js';
|
|
13
14
|
import { planOikosIngress } from './oikos-ingress.js';
|
|
@@ -1271,6 +1272,13 @@ export const PLATFORM_UPTIME_KV_TITLE = 'platform-uptime';
|
|
|
1271
1272
|
* a blocked item, never a throw.
|
|
1272
1273
|
*/
|
|
1273
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';
|
|
1274
1282
|
export const PLATFORM_UPTIME_ACCESS_CLIENT_ID_REF = 'uptime:worker:access-client-id';
|
|
1275
1283
|
export const PLATFORM_UPTIME_ACCESS_CLIENT_SECRET_REF = 'uptime:worker:access-client-secret';
|
|
1276
1284
|
export const PLATFORM_UPTIME_CHAT_TOKEN_REF = 'uptime:worker:chat-token';
|
|
@@ -1295,6 +1303,19 @@ export const PLATFORM_UPTIME_OPTIONAL_SECRETS = {
|
|
|
1295
1303
|
*/
|
|
1296
1304
|
export const PLATFORM_UPTIME_TOKEN_UNDELIVERED_SCOPE_KEY = 'uptime_token_undelivered';
|
|
1297
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
|
+
}
|
|
1298
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}. */
|
|
1299
1320
|
export const PLATFORM_ARCHON_UPTIME_TOKEN_SYNCED_SCOPE_KEY = 'archon_uptime_token_synced';
|
|
1300
1321
|
/** The tag, sha256 and Archon hostname the last successful run applied; a change plans the step again and mints nothing. */
|
|
@@ -1338,9 +1359,16 @@ export function uptimeVarsDigest(vars = {}) {
|
|
|
1338
1359
|
return '';
|
|
1339
1360
|
return createHash('sha256').update(JSON.stringify(set)).digest('hex').slice(0, 16);
|
|
1340
1361
|
}
|
|
1341
|
-
/** The Worker secret names the step sets: the
|
|
1362
|
+
/** The Worker secret names the step sets: the five it mints, and each held one. */
|
|
1342
1363
|
export function uptimeSecretNames(held) {
|
|
1343
|
-
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
|
+
]
|
|
1344
1372
|
.sort()
|
|
1345
1373
|
.join(',');
|
|
1346
1374
|
}
|
|
@@ -3249,6 +3277,7 @@ function planUptime(platform, recordedIds, executorBlocked, heldOptional, archon
|
|
|
3249
3277
|
const applied = deployed &&
|
|
3250
3278
|
Boolean(recordedIds[PLATFORM_UPTIME_TOKEN_ID_SCOPE_KEY]) &&
|
|
3251
3279
|
uptimeHeld.token &&
|
|
3280
|
+
Boolean(uptimeTicksSyncValue(recordedIds)) &&
|
|
3252
3281
|
Boolean(recordedIds[PLATFORM_UPTIME_ACCESS_TOKEN_ID_SCOPE_KEY]) &&
|
|
3253
3282
|
uptimeHeld.access &&
|
|
3254
3283
|
recordedIds[PLATFORM_UPTIME_APPLIED_TAG_SCOPE_KEY] === uptime.tag &&
|
|
@@ -3482,6 +3511,10 @@ function planArchon(platform, recordedIds, executorBlocked, ownerSignIn, ownerFi
|
|
|
3482
3511
|
// In-app sign-in: Archon's own address is the sign-in address only once the owner company has the
|
|
3483
3512
|
// switch on and its in-app login is applied, and goes back to the company's login host when it does not.
|
|
3484
3513
|
archonInAppHost(recordedIds) === (ownerSignIn.signInInApp ? platform.archon.hostname : '') &&
|
|
3514
|
+
// With in-app sign-in on, `loginV2.required` is off and every app of the instance owes a base URI
|
|
3515
|
+
// until the sweep ran (an instance applied before it plans the step once more).
|
|
3516
|
+
(!ownerSignIn.signInInApp ||
|
|
3517
|
+
recordedIds[LOGIN_BASE_URI_SWEEP_SCOPE_KEY] === LOGIN_BASE_URI_SWEEP_VERSION) &&
|
|
3485
3518
|
recordedIds[PLATFORM_ARCHON_APP_KEY_ORG_SCOPE_KEY] === archonOrgId &&
|
|
3486
3519
|
!recordedIds[PLATFORM_ARCHON_RETIRED_CLIENT_SCOPE_KEY] &&
|
|
3487
3520
|
Boolean(recordedIds[PLATFORM_ARCHON_ACCESS_PIN_SCOPE_KEY]) &&
|
|
@@ -3506,6 +3539,11 @@ function planArchon(platform, recordedIds, executorBlocked, ownerSignIn, ownerFi
|
|
|
3506
3539
|
recordedIds[PLATFORM_UPTIME_TOKEN_ID_SCOPE_KEY] ===
|
|
3507
3540
|
recordedIds[PLATFORM_ARCHON_UPTIME_TOKEN_SYNCED_SCOPE_KEY]
|
|
3508
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]) &&
|
|
3509
3547
|
// #398: a rotated integrations key id Archon's env was not yet written
|
|
3510
3548
|
// with (see PLATFORM_ARCHON_INTEGRATIONS_KEY_SYNCED_SCOPE_KEY). Absent
|
|
3511
3549
|
// before the first rotation on a platform applied before #398.
|
|
@@ -194,6 +194,12 @@ export interface OrgPlatformReadiness {
|
|
|
194
194
|
unreadable?: readonly string[];
|
|
195
195
|
invalid?: readonly string[];
|
|
196
196
|
};
|
|
197
|
+
/**
|
|
198
|
+
* The Worker apps' `heldSecret` secrets that hold a value now: `<entry key>/<NAME>` -> the version of that
|
|
199
|
+
* value (a hash, `heldSecretVersion`; never the value). A secret with no entry here is unset. Absent reads as
|
|
200
|
+
* none held.
|
|
201
|
+
*/
|
|
202
|
+
workerAppSecrets?: Readonly<Record<string, string>>;
|
|
197
203
|
/**
|
|
198
204
|
* The platform GitHub App's held keys (`PLATFORM_GITHUB_APP_REFS`), read only for a company that declares
|
|
199
205
|
* `hosting.github`. `store` is false when the host wired no held-keys reader; `missing` are the refs that hold
|
|
@@ -542,13 +542,15 @@ export declare const PLATFORM_AUTH_LOGIN_INTERNAL_NAME = "platform-auth-login";
|
|
|
542
542
|
export declare function zitadelDockerfile(): string;
|
|
543
543
|
export declare const AUTH_LOGIN_PORT = "3000";
|
|
544
544
|
/**
|
|
545
|
-
* Every ZITADEL OIDC/management/
|
|
546
|
-
*
|
|
545
|
+
* Every ZITADEL OIDC/management/asset path -- `deploy/proxy/Caddyfile`'s own
|
|
546
|
+
* explicit list, restated here as one regex since the tunnel routes by a
|
|
547
547
|
* single `path` per ingress rule rather than a set. Anything not matching
|
|
548
548
|
* falls through to the login app's own ingress rule below (login v2 answers
|
|
549
|
-
* everything else, including the root).
|
|
549
|
+
* everything else, including the root). `/ui/console` and `/ui/login/*` (the
|
|
550
|
+
* v1 login) are left out on purpose: nobody reaches ZITADEL's own screens, so
|
|
551
|
+
* they land on the login app, which sends them back to the app.
|
|
550
552
|
*/
|
|
551
|
-
export declare const AUTH_ZITADEL_PATH_PATTERN = "^/(oidc|oauth|saml|management|admin|system|auth|assets|debug/healthz
|
|
553
|
+
export declare const AUTH_ZITADEL_PATH_PATTERN = "^/(oidc|oauth|saml|management|admin|system|auth|assets|debug/healthz|\\.well-known|v2|v2beta|zitadel\\.[A-Za-z0-9.]+)(/|$)";
|
|
552
554
|
/**
|
|
553
555
|
* The broker's MCP authorization server (auth ADR 0002): its metadata and `/mcp-oauth/*`.
|
|
554
556
|
* Checked before {@link AUTH_ZITADEL_PATH_PATTERN}, which takes every other `/.well-known` path.
|
|
@@ -1000,7 +1002,7 @@ export interface PlatformArchonDeps {
|
|
|
1000
1002
|
releasePreviewMembers?: ReleasePreviewMembers;
|
|
1001
1003
|
cloudflare: Pick<CloudflarePort, 'findZoneByApex'>;
|
|
1002
1004
|
/** Must target the platform's own ZITADEL instance, or `zitadelForInstance` must be set. */
|
|
1003
|
-
zitadel: Pick<ZitadelPort, 'findOrCreateOrg' | 'findOrCreateProject' | 'createOidcApp' | 'listApps' | 'deleteApp' | 'updateOidcAppIdTokenUserinfoAssertion'> & Partial<Pick<ZitadelPort, 'ensureTrustedDomain' | 'deleteTrustedDomain' | 'updateOidcAppLoginBaseUri' | 'allowPerAppLoginBaseUri' | 'updateOidcAppPostLogoutRedirectUris' | 'getOidcAppRedirectUris' | 'updateOidcAppRedirectUris'>>;
|
|
1005
|
+
zitadel: Pick<ZitadelPort, 'findOrCreateOrg' | 'findOrCreateProject' | 'createOidcApp' | 'listApps' | 'deleteApp' | 'updateOidcAppIdTokenUserinfoAssertion'> & Partial<Pick<ZitadelPort, 'ensureTrustedDomain' | 'deleteTrustedDomain' | 'updateOidcAppLoginBaseUri' | 'allowPerAppLoginBaseUri' | 'listOrgs' | 'listProjects' | 'updateOidcAppPostLogoutRedirectUris' | 'getOidcAppRedirectUris' | 'updateOidcAppRedirectUris'>>;
|
|
1004
1006
|
/**
|
|
1005
1007
|
* {@link PlatformAuthDeps.zitadelForInstance}, for Archon's sign-in client:
|
|
1006
1008
|
* the owner company's own ZITADEL, by its issuer and the held-key reference of
|
|
@@ -1641,7 +1643,7 @@ export interface PlatformAccessProviderDeps {
|
|
|
1641
1643
|
/** Edge's Access calls, at its recorded address (see {@link PlatformAccessFactory}). */
|
|
1642
1644
|
access: (edgeUrl: string) => PlatformAccessProviderPort | Promise<PlatformAccessProviderPort>;
|
|
1643
1645
|
/** Must target the platform's own ZITADEL instance, or `zitadelForInstance` must be set. */
|
|
1644
|
-
zitadel: Pick<ZitadelPort, 'findOrCreateOrg' | 'findOrCreateProject' | 'listApps' | 'deleteApp'> & Required<Pick<ZitadelPort, 'createOidcAppWithSecret'>>;
|
|
1646
|
+
zitadel: Pick<ZitadelPort, 'findOrCreateOrg' | 'findOrCreateProject' | 'listApps' | 'deleteApp'> & Required<Pick<ZitadelPort, 'createOidcAppWithSecret'>> & Partial<Pick<ZitadelPort, 'updateOidcAppLoginBaseUri'>>;
|
|
1645
1647
|
/** {@link PlatformArchonDeps.zitadelForInstance}: the ZITADEL client for the sign-in at `issuerUrl`. */
|
|
1646
1648
|
zitadelForInstance?: (issuerUrl: string, bearerRef?: string) => PlatformAccessProviderDeps['zitadel'];
|
|
1647
1649
|
/** Sleep and delivery tries for the one call that carries the secret; real ones by default. */
|
|
@@ -13,7 +13,7 @@ import { previewsOn } from './coolify-previews.js';
|
|
|
13
13
|
import { waitForDeploymentFinished, waitForHealthyVersion, waitForNewDeployment, } from './deploy-health.js';
|
|
14
14
|
import { desktopCallbackUri, unionRedirectUris } from './desktop-callback.js';
|
|
15
15
|
import { placedOnPlatform } from './destinations.js';
|
|
16
|
-
import { ACCESS_POLICY_NAME, LOG_DIR, coolifyProjectMark, getDatabaseStarted, isNotFoundError, isRunningOrStarting, } from './executor.js';
|
|
16
|
+
import { ACCESS_POLICY_NAME, LOG_DIR, coolifyProjectMark, getDatabaseStarted, isNotFoundError, isRunningOrStarting, setOidcAppLoginBaseUri, } from './executor.js';
|
|
17
17
|
import { ACCESS_PROVIDER_RECORD_KEY, GATE_CLAIM_NAME } from './gates.js';
|
|
18
18
|
import { IMAGE_HEALTHCHECK_SETTINGS, commandHealthCheckSettings, healthCheckSettings, tcpProbe, } from './health-check-settings.js';
|
|
19
19
|
import { FILES_MANAGEMENT_KEY_REF, JOBS_ADMIN_TOKEN_REF, PLATFORM_AI_MANAGEMENT_KEY_REF, } from './held-key-refs.js';
|
|
@@ -27,12 +27,13 @@ import { BROKER_DB_NAME, BROKER_DB_USER, ensureBrokerMcpEnv } from './broker-mcp
|
|
|
27
27
|
import { withTimeout } from './call-timeout.js';
|
|
28
28
|
import { SIGN_IN_BIND_KEY_ENV, SIGN_IN_BIND_KEY_MIN_LENGTH, SIGN_IN_BIND_KEY_SCOPE_KEY, } from './cookie-domain.js';
|
|
29
29
|
import { coolifyDomains, coolifyTunnelDomains, filesShareHostname } from './hostnames.js';
|
|
30
|
+
import { LOGIN_BASE_URI_SWEEP_SCOPE_KEY, LOGIN_BASE_URI_SWEEP_VERSION } from './login-host.js';
|
|
30
31
|
import { MAIL_RELAY_BLOCKED, provisionMailRelay } from './mail-relay.js';
|
|
31
32
|
import { MAIL_APP_SCOPE_KEY, MAIL_DATABASE_NAME, MAIL_DATABASE_PASSWORD_ENV, MAIL_DATABASE_SCOPE_KEY, MAIL_STORE_POSTGRES, MAIL_STORE_SCOPE_KEY, mailStoreIsLegacy, } from './mail-store.js';
|
|
32
33
|
import { OIKOS_INGRESS_BLOCKED, PLATFORM_OIKOS_INGRESS_KEY, oikosIngressApplied, oikosIngressZoneBlocked, provisionOikosIngress, } from './oikos-ingress.js';
|
|
33
34
|
import { recordOwnDnsHostname } from './own-dns.js';
|
|
34
35
|
import { platformNetwork, platformTarget, scopeFromPlatform, } from './own-services.js';
|
|
35
|
-
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';
|
|
36
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';
|
|
37
38
|
import { ensureRegistryLogins, provisionPlatformRegistry, registryLoginsDrifted, } from './platform-registry.js';
|
|
38
39
|
import { PLATFORM_ARCHON_IN_APP_SCOPE_KEY, archonInAppBack, archonInAppBind, archonInAppHost, archonInAppPoint, archonInAppUnbind, } from './sign-in-archon.js';
|
|
@@ -730,7 +731,10 @@ export class FoundryPlatformStepExecutor {
|
|
|
730
731
|
const accessHeld = Boolean((await deps.heldKeys.resolve(PLATFORM_UPTIME_ACCESS_CLIENT_ID_REF)) &&
|
|
731
732
|
(await deps.heldKeys.resolve(PLATFORM_UPTIME_ACCESS_CLIENT_SECRET_REF)));
|
|
732
733
|
blocked.uptimeHeld = {
|
|
733
|
-
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))),
|
|
734
738
|
access: accessHeld,
|
|
735
739
|
vars: uptimeVarsDigest(uptimeEffectiveVars(deps.vars, deps.chat && owner.chat && recordedIds[PLATFORM_UPTIME_CHAT_ROOM_ID_SCOPE_KEY]
|
|
736
740
|
? {
|
|
@@ -1473,17 +1477,18 @@ export const AUTH_LOGIN_PORT = '3000';
|
|
|
1473
1477
|
/** `broker/Dockerfile`'s own `ENV PORT=3000` / `EXPOSE 3000`. */
|
|
1474
1478
|
const AUTH_BROKER_PORT = '3000';
|
|
1475
1479
|
/**
|
|
1476
|
-
* Every ZITADEL OIDC/management/
|
|
1477
|
-
*
|
|
1480
|
+
* Every ZITADEL OIDC/management/asset path -- `deploy/proxy/Caddyfile`'s own
|
|
1481
|
+
* explicit list, restated here as one regex since the tunnel routes by a
|
|
1478
1482
|
* single `path` per ingress rule rather than a set. Anything not matching
|
|
1479
1483
|
* falls through to the login app's own ingress rule below (login v2 answers
|
|
1480
|
-
* everything else, including the root).
|
|
1484
|
+
* everything else, including the root). `/ui/console` and `/ui/login/*` (the
|
|
1485
|
+
* v1 login) are left out on purpose: nobody reaches ZITADEL's own screens, so
|
|
1486
|
+
* they land on the login app, which sends them back to the app.
|
|
1481
1487
|
*/
|
|
1482
1488
|
// `v2`, `v2beta` and `zitadel.<service>` are ZITADEL's v2 REST and connect
|
|
1483
1489
|
// APIs (providers' `inviteOrgOwner` calls `POST /v2/users`); without them those
|
|
1484
|
-
// calls reach the login app and answer 404.
|
|
1485
|
-
|
|
1486
|
-
export const AUTH_ZITADEL_PATH_PATTERN = '^/(oidc|oauth|saml|management|admin|system|auth|assets|debug/healthz|ui/console|\\.well-known|v2|v2beta|zitadel\\.[A-Za-z0-9.]+)(/|$)';
|
|
1490
|
+
// calls reach the login app and answer 404. None of these match `/ui/*`.
|
|
1491
|
+
export const AUTH_ZITADEL_PATH_PATTERN = '^/(oidc|oauth|saml|management|admin|system|auth|assets|debug/healthz|\\.well-known|v2|v2beta|zitadel\\.[A-Za-z0-9.]+)(/|$)';
|
|
1487
1492
|
/**
|
|
1488
1493
|
* `@wtfalch/auth`'s own `brokerUrl` default, `<issuer>/api`
|
|
1489
1494
|
* (`packages/auth/src/config.ts`) -- `deploy/proxy/Caddyfile`'s own
|
|
@@ -4671,6 +4676,8 @@ async function provisionPlatformArchon(context, deps, ownerCompany = null) {
|
|
|
4671
4676
|
// `docs/operator-deployment.md`, step 4).
|
|
4672
4677
|
redirectUris: [`https://${hostname}/auth/callback`, desktopCallbackUri(hostname)],
|
|
4673
4678
|
postLogoutRedirectUris: postLogoutUrisFor([hostname]),
|
|
4679
|
+
// The owner company's login host: with `loginV2.required` off a client with none lands on the v1 login.
|
|
4680
|
+
loginBaseUri: `https://${ownerSignIn.hostname}/`,
|
|
4674
4681
|
});
|
|
4675
4682
|
context.recordScopeId(PLATFORM_ARCHON_ZITADEL_APP_SCOPE_KEY, created.appId);
|
|
4676
4683
|
context.recordScopeId(PLATFORM_ARCHON_POST_LOGOUT_APPLIED_SCOPE_KEY, postLogoutAppliedValue(created.appId, postLogoutUrisFor([hostname])));
|
|
@@ -4693,7 +4700,7 @@ async function provisionPlatformArchon(context, deps, ownerCompany = null) {
|
|
|
4693
4700
|
const archonAppZitadelId = context.recordedIds()[PLATFORM_ARCHON_ZITADEL_APP_SCOPE_KEY];
|
|
4694
4701
|
const inAppOwed = archonInAppHost(context.recordedIds()) !== '';
|
|
4695
4702
|
const inAppCalls = () => {
|
|
4696
|
-
const { ensureTrustedDomain, deleteTrustedDomain, updateOidcAppLoginBaseUri, allowPerAppLoginBaseUri, } = zitadel;
|
|
4703
|
+
const { ensureTrustedDomain, deleteTrustedDomain, updateOidcAppLoginBaseUri, allowPerAppLoginBaseUri, listOrgs, listProjects, } = zitadel;
|
|
4697
4704
|
if (!ensureTrustedDomain || !deleteTrustedDomain || !updateOidcAppLoginBaseUri) {
|
|
4698
4705
|
throw new Error("platform:archon: the owner company's ZITADEL port lacks ensureTrustedDomain, deleteTrustedDomain or updateOidcAppLoginBaseUri, so Archon cannot sign in on its own address");
|
|
4699
4706
|
}
|
|
@@ -4701,9 +4708,12 @@ async function provisionPlatformArchon(context, deps, ownerCompany = null) {
|
|
|
4701
4708
|
ensureTrustedDomain: ensureTrustedDomain.bind(zitadel),
|
|
4702
4709
|
deleteTrustedDomain: deleteTrustedDomain.bind(zitadel),
|
|
4703
4710
|
updateOidcAppLoginBaseUri: updateOidcAppLoginBaseUri.bind(zitadel),
|
|
4711
|
+
listApps: zitadel.listApps.bind(zitadel),
|
|
4704
4712
|
...(allowPerAppLoginBaseUri
|
|
4705
4713
|
? { allowPerAppLoginBaseUri: allowPerAppLoginBaseUri.bind(zitadel) }
|
|
4706
4714
|
: {}),
|
|
4715
|
+
...(listOrgs ? { listOrgs: listOrgs.bind(zitadel) } : {}),
|
|
4716
|
+
...(listProjects ? { listProjects: listProjects.bind(zitadel) } : {}),
|
|
4707
4717
|
};
|
|
4708
4718
|
};
|
|
4709
4719
|
const inAppTarget = {
|
|
@@ -4881,6 +4891,21 @@ async function provisionPlatformArchon(context, deps, ownerCompany = null) {
|
|
|
4881
4891
|
const uptimeToken = uptimeTokenId ? await deps.heldKeys.resolve(PLATFORM_UPTIME_TOKEN_REF) : null;
|
|
4882
4892
|
if (uptimeToken)
|
|
4883
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
|
+
}
|
|
4884
4909
|
const changed = await envsDiffer(deps.coolify, appId, envs);
|
|
4885
4910
|
// #467: this run's intended env set, hashed before anything below writes
|
|
4886
4911
|
// it -- compared against {@link PLATFORM_ARCHON_ENVS_DEPLOYED_SCOPE_KEY}
|
|
@@ -4928,6 +4953,10 @@ async function provisionPlatformArchon(context, deps, ownerCompany = null) {
|
|
|
4928
4953
|
for (const stale of await deps.coolify.listEnvs(appId)) {
|
|
4929
4954
|
// foundry#542: a disabled Worker's token leaves Archon's env too.
|
|
4930
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');
|
|
4931
4960
|
// People now come from the owner company's store, so a first operator set by an older apply goes.
|
|
4932
4961
|
const staleOperator = stale.key === 'AUTHZ_BOOTSTRAP_OPERATOR';
|
|
4933
4962
|
// The link is written only while both ids are known; one that is no longer wanted goes.
|
|
@@ -4936,6 +4965,7 @@ async function provisionPlatformArchon(context, deps, ownerCompany = null) {
|
|
|
4936
4965
|
const staleSignIn = !inAppWanted && stale.key === SIGN_IN_UPSTREAM_ENV;
|
|
4937
4966
|
if ((stale.key === 'FILES_MANAGEMENT_KEY' ||
|
|
4938
4967
|
staleUptime ||
|
|
4968
|
+
staleUptimeTick ||
|
|
4939
4969
|
staleOperator ||
|
|
4940
4970
|
staleLink ||
|
|
4941
4971
|
staleSignIn) &&
|
|
@@ -5217,6 +5247,7 @@ async function provisionPlatformArchon(context, deps, ownerCompany = null) {
|
|
|
5217
5247
|
});
|
|
5218
5248
|
}
|
|
5219
5249
|
await record(PLATFORM_ARCHON_IN_APP_SCOPE_KEY, `${hostname}=${clientId}`);
|
|
5250
|
+
await record(LOGIN_BASE_URI_SWEEP_SCOPE_KEY, LOGIN_BASE_URI_SWEEP_VERSION);
|
|
5220
5251
|
}
|
|
5221
5252
|
else if (inAppOwed) {
|
|
5222
5253
|
if (!ownerSignIn.signInLoginAppId) {
|
|
@@ -5244,6 +5275,16 @@ async function provisionPlatformArchon(context, deps, ownerCompany = null) {
|
|
|
5244
5275
|
(!selfRedeploy || deployState === 'finished')) {
|
|
5245
5276
|
await record(PLATFORM_ARCHON_UPTIME_TOKEN_SYNCED_SCOPE_KEY, '');
|
|
5246
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
|
+
}
|
|
5247
5288
|
if (uptimeToken &&
|
|
5248
5289
|
uptimeTokenId &&
|
|
5249
5290
|
recorded[PLATFORM_ARCHON_UPTIME_TOKEN_SYNCED_SCOPE_KEY] !== uptimeTokenId &&
|
|
@@ -8320,6 +8361,11 @@ async function provisionPlatformAccessProvider(context, deps) {
|
|
|
8320
8361
|
context.recordScopeId(PLATFORM_ACCESS_PROVIDER_UNDELIVERED_SCOPE_KEY, client.appId);
|
|
8321
8362
|
context.recordScopeId(PLATFORM_ACCESS_ZITADEL_APP_SCOPE_KEY, client.appId);
|
|
8322
8363
|
await context.persist?.();
|
|
8364
|
+
// The owner company's login host, so the client never lands on the v1 login when `loginV2.required` is off.
|
|
8365
|
+
// After the ids are persisted: a failure here leaves the app recorded and costs no second secret.
|
|
8366
|
+
if (zitadel.updateOidcAppLoginBaseUri) {
|
|
8367
|
+
await setOidcAppLoginBaseUri({ updateOidcAppLoginBaseUri: zitadel.updateOidcAppLoginBaseUri.bind(zitadel) }, org.id, project.id, client.appId, `https://${ownerSignIn.hostname}/`);
|
|
8368
|
+
}
|
|
8323
8369
|
let providerId = '';
|
|
8324
8370
|
await deliverWithRetry(async (_ref, secret) => {
|
|
8325
8371
|
providerId = (await access.ensureProvider({
|
|
@@ -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);
|