@wtfalch/foundry 0.74.0 → 0.76.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/plan/broker-mcp.d.ts +7 -0
- package/dist/plan/broker-mcp.js +16 -0
- package/dist/plan/diff.js +4 -2
- package/dist/plan/own-diff.js +4 -2
- package/dist/plan/platform-diff.d.ts +16 -1
- package/dist/plan/platform-diff.js +18 -1
- package/dist/plan/platform-providers.d.ts +16 -0
- package/dist/plan/platform-providers.js +71 -8
- package/package.json +1 -1
- package/versions.digests.json +1 -1
- package/versions.json +1 -1
|
@@ -8,6 +8,13 @@ import { type JobRuntime } from './job-app.js';
|
|
|
8
8
|
*/
|
|
9
9
|
export declare const MCP_DATABASE_URL_ENV = "MCP_DATABASE_URL";
|
|
10
10
|
export declare const MCP_OAUTH_SECRET_ENV = "MCP_OAUTH_SECRET";
|
|
11
|
+
/**
|
|
12
|
+
* The key that seals the broker's shared auth state (`wtfalch/auth` broker/src/auth/state-config.ts). The
|
|
13
|
+
* broker runs in shared mode whenever `MCP_DATABASE_URL` is set and exits at boot without this key: at
|
|
14
|
+
* least 32 bytes, the same on every replica, never regenerated. `AUTH_STATE_SECRET_PREVIOUS` (rotation)
|
|
15
|
+
* is the operator's and is never touched here.
|
|
16
|
+
*/
|
|
17
|
+
export declare const AUTH_STATE_SECRET_ENV = "AUTH_STATE_SECRET";
|
|
11
18
|
/** The broker's own role and database on the sign-in Postgres. */
|
|
12
19
|
export declare const BROKER_DB_NAME = "broker";
|
|
13
20
|
export declare const BROKER_DB_USER = "broker";
|
package/dist/plan/broker-mcp.js
CHANGED
|
@@ -11,6 +11,13 @@ import { SIGNIN_DB_CA_MOUNT, boundedCall } from './signin-db.js';
|
|
|
11
11
|
*/
|
|
12
12
|
export const MCP_DATABASE_URL_ENV = 'MCP_DATABASE_URL';
|
|
13
13
|
export const MCP_OAUTH_SECRET_ENV = 'MCP_OAUTH_SECRET';
|
|
14
|
+
/**
|
|
15
|
+
* The key that seals the broker's shared auth state (`wtfalch/auth` broker/src/auth/state-config.ts). The
|
|
16
|
+
* broker runs in shared mode whenever `MCP_DATABASE_URL` is set and exits at boot without this key: at
|
|
17
|
+
* least 32 bytes, the same on every replica, never regenerated. `AUTH_STATE_SECRET_PREVIOUS` (rotation)
|
|
18
|
+
* is the operator's and is never touched here.
|
|
19
|
+
*/
|
|
20
|
+
export const AUTH_STATE_SECRET_ENV = 'AUTH_STATE_SECRET';
|
|
14
21
|
/** The broker's own role and database on the sign-in Postgres. */
|
|
15
22
|
export const BROKER_DB_NAME = 'broker';
|
|
16
23
|
export const BROKER_DB_USER = 'broker';
|
|
@@ -84,9 +91,18 @@ export async function ensureBrokerMcpEnv(input) {
|
|
|
84
91
|
throw new Error(`${input.label}: the broker lost its ${MCP_OAUTH_SECRET_ENV} between two reads`);
|
|
85
92
|
}
|
|
86
93
|
runtime.onSecret?.(secret);
|
|
94
|
+
// The same rule as the OAuth secret: minted once, a live value (hand-set too) is kept, and it is written
|
|
95
|
+
// wherever `MCP_DATABASE_URL` is, so a broker never has the one without the other.
|
|
96
|
+
const mintedState = await ensureGeneratedEnv(coolify, brokerAppId, AUTH_STATE_SECRET_ENV, () => randomBytes(32).toString('hex'), MCP_OAUTH_SECRET_MIN_LENGTH);
|
|
97
|
+
const stateSecret = mintedState ?? (await currentEnvValue(coolify, brokerAppId, AUTH_STATE_SECRET_ENV));
|
|
98
|
+
if (!stateSecret) {
|
|
99
|
+
throw new Error(`${input.label}: the broker lost its ${AUTH_STATE_SECRET_ENV} between two reads`);
|
|
100
|
+
}
|
|
101
|
+
runtime.onSecret?.(stateSecret);
|
|
87
102
|
return [
|
|
88
103
|
{ key: MCP_DATABASE_URL_ENV, value: url },
|
|
89
104
|
{ key: MCP_OAUTH_SECRET_ENV, value: secret },
|
|
105
|
+
{ key: AUTH_STATE_SECRET_ENV, value: stateSecret },
|
|
90
106
|
];
|
|
91
107
|
}
|
|
92
108
|
/** Runs the job once, waits for it, deletes it. Every error is fixed text with the app id, never a cause. */
|
package/dist/plan/diff.js
CHANGED
|
@@ -48,7 +48,7 @@ import { OWN_AUTH_BLOCKED_KEY, OWN_FILES_BLOCKED_KEY, OWN_FILES_R2_BLOCKED_KEY,
|
|
|
48
48
|
import { ownDnsHostnames } from './own-dns.js';
|
|
49
49
|
import { ownHostnames } from './own-hostnames.js';
|
|
50
50
|
import { platformMailMxHost } from './platform-diff.js';
|
|
51
|
-
import { CALLS_MAS_APPLIED_IMAGE_TAG_SCOPE_KEY, CALLS_SYNAPSE_APPLIED_IMAGE_TAG_SCOPE_KEY, ORG_MAIL_APPLIED_APEX_SCOPE_KEY, ORG_MAIL_APPLIED_HOST_SCOPE_KEY, ORG_MAIL_APPLIED_MODE_SCOPE_KEY, ORG_MAIL_APPLIED_USES_DOMAIN_SCOPE_KEY, PLATFORM_AUTH_MCP_SCOPE_KEY, PLATFORM_FILES_INTERNAL_URL_SCOPE_KEY, appEnvAppliedScopeKey, appFolderDiffers, appImageAppliedScopeKey, appSecretAvailability, appSettingsOf, appStepKey, appliedAppDomains, appliedAppSettings, dbEnvAppliedScopeKey, entryImageRef, resolveAppEnv, serviceDbEnvAppliedScopeKey, versionNotAppliedLine, zitadelAppIdTokenUserinfoAppliedScopeKey, } from './platform-diff.js';
|
|
51
|
+
import { AUTH_MCP_ENV_VERSION, CALLS_MAS_APPLIED_IMAGE_TAG_SCOPE_KEY, CALLS_SYNAPSE_APPLIED_IMAGE_TAG_SCOPE_KEY, ORG_MAIL_APPLIED_APEX_SCOPE_KEY, ORG_MAIL_APPLIED_HOST_SCOPE_KEY, ORG_MAIL_APPLIED_MODE_SCOPE_KEY, ORG_MAIL_APPLIED_USES_DOMAIN_SCOPE_KEY, PLATFORM_AUTH_MCP_SCOPE_KEY, PLATFORM_FILES_INTERNAL_URL_SCOPE_KEY, appEnvAppliedScopeKey, appFolderDiffers, appImageAppliedScopeKey, appSecretAvailability, appSettingsOf, appStepKey, appliedAppDomains, appliedAppSettings, dbEnvAppliedScopeKey, entryImageRef, resolveAppEnv, serviceDbEnvAppliedScopeKey, versionNotAppliedLine, zitadelAppIdTokenUserinfoAppliedScopeKey, } from './platform-diff.js';
|
|
52
52
|
import { DEFAULT_ORG_PLATFORM_READINESS, } from './platform-org.js';
|
|
53
53
|
import { PLATFORM_AUTH_BROKER_MAIL_KEY_REF } from './platform-providers.js';
|
|
54
54
|
import { companyAppPostLogoutUris, postLogoutUpdateOwed, zitadelAppPostLogoutAppliedScopeKey, } from './post-logout-uris.js';
|
|
@@ -4611,7 +4611,9 @@ export function planBrokerEntries(org, recordedIds, plannedSteps, brokerWired, b
|
|
|
4611
4611
|
// Auth ADR 0002: a broker given an `mcp` entry before it holds its database and secret exits at boot.
|
|
4612
4612
|
// A company's own sign-in step sets them and records it; a shared org's broker is the platform's, whose
|
|
4613
4613
|
// marker this plan cannot see, so `writeBrokerEntry` refuses there until the platform step has run.
|
|
4614
|
-
if (auth.mcp &&
|
|
4614
|
+
if (auth.mcp &&
|
|
4615
|
+
ownsServices(org) &&
|
|
4616
|
+
recordedIds[PLATFORM_AUTH_MCP_SCOPE_KEY] !== AUTH_MCP_ENV_VERSION) {
|
|
4615
4617
|
blocked.push({
|
|
4616
4618
|
description: `${at} is blocked, not planned: the broker's MCP database and secret are not set yet (the sign-in step sets them)`,
|
|
4617
4619
|
key,
|
package/dist/plan/own-diff.js
CHANGED
|
@@ -13,7 +13,7 @@ import { ownHostnames, passkeyRpIdFor } from './own-hostnames.js';
|
|
|
13
13
|
import { JOBS_PUBLIC_URL_SCOPE_KEY, OWN_JOBS_ROUTE_TUNNEL_SCOPE_KEY } from './own-jobs-route.js';
|
|
14
14
|
import { OWN_FILES_R2_SCOPE_KEY, OWN_SERVICES_APPLIED_SCOPE_KEY, orgTarget, } from './own-services.js';
|
|
15
15
|
import { OWN_SFU_APPLIED_HOSTNAME_SCOPE_KEY, OWN_SFU_APPLIED_NODE_IP_SCOPE_KEY, OWN_SFU_APPLIED_SLOT_SCOPE_KEY, OWN_SFU_APPLIED_VERSION_SCOPE_KEY, OWN_SFU_DEPLOY_PENDING_SCOPE_KEY, OWN_SFU_KEYS_UNDELIVERED_SCOPE_KEY, SFU_INTERNAL_URL_SCOPE_KEY, SFU_PUBLIC_URL_SCOPE_KEY, } from './own-sfu.js';
|
|
16
|
-
import { AUTH_TUNNEL_ROUTES_VERSION, AUTH_ZITADEL_RUN_OPTIONS, JOBS_DEPLOY_PENDING_SCOPE_KEY, JOBS_INTERNAL_URL_SCOPE_KEY, PLATFORM_AUTH_ADMIN_UNDELIVERED_SCOPE_KEY, PLATFORM_AUTH_ADMIN_USER_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_LOGIN_CLIENT_UNDELIVERED_SCOPE_KEY, PLATFORM_AUTH_LOGIN_CLIENT_USER_SCOPE_KEY, PLATFORM_AUTH_LOGIN_ENV_SCOPE_KEY, PLATFORM_AUTH_MCP_SCOPE_KEY, PLATFORM_AUTH_SMTP_SCOPE_KEY, PLATFORM_AUTH_TUNNEL_ROUTES_SCOPE_KEY, PLATFORM_AUTH_ZITADEL_RUN_OPTIONS_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_MANAGEMENT_KEY_SCOPE_KEY, PLATFORM_FILES_MANAGEMENT_UNDELIVERED_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_INTERNAL_URL_SCOPE_KEY, authLoginEnvMarker, orgDeclaresMcp, platformMailMxHost, } from './platform-diff.js';
|
|
16
|
+
import { AUTH_ISSUER_SOURCE_ENV_VERSION, AUTH_MCP_ENV_VERSION, AUTH_TUNNEL_ROUTES_VERSION, AUTH_ZITADEL_RUN_OPTIONS, JOBS_DEPLOY_PENDING_SCOPE_KEY, JOBS_INTERNAL_URL_SCOPE_KEY, PLATFORM_AUTH_ADMIN_UNDELIVERED_SCOPE_KEY, PLATFORM_AUTH_ADMIN_USER_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_ISSUER_SOURCE_ENV_SCOPE_KEY, PLATFORM_AUTH_LOGIN_CLIENT_UNDELIVERED_SCOPE_KEY, PLATFORM_AUTH_LOGIN_CLIENT_USER_SCOPE_KEY, PLATFORM_AUTH_LOGIN_ENV_SCOPE_KEY, PLATFORM_AUTH_MCP_SCOPE_KEY, PLATFORM_AUTH_SMTP_SCOPE_KEY, PLATFORM_AUTH_TUNNEL_ROUTES_SCOPE_KEY, PLATFORM_AUTH_ZITADEL_RUN_OPTIONS_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_MANAGEMENT_KEY_SCOPE_KEY, PLATFORM_FILES_MANAGEMENT_UNDELIVERED_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_INTERNAL_URL_SCOPE_KEY, authLoginEnvMarker, orgDeclaresMcp, platformMailMxHost, } from './platform-diff.js';
|
|
17
17
|
import { OWN_FILES_RUNNER_SCOPE_KEY } from './platform-org.js';
|
|
18
18
|
import { JOBS_ADMIN_TOKEN_UNDELIVERED_SCOPE_KEY, JOBS_APPLIED_GIT_REF_SCOPE_KEY, PLATFORM_AUTH_DB_SCOPE_KEY, PLATFORM_AUTH_LOGIN_SCOPE_KEY, PLATFORM_AUTH_ZITADEL_SCOPE_KEY, PLATFORM_FILES_SHARE_DOMAINS_SCOPE_KEY, filesShareDomains, } from './platform-providers.js';
|
|
19
19
|
import { SIGN_IN_LOGIN_APP_SCOPE_KEY, inAppSignIn, signInLoginNames } from './sign-in.js';
|
|
@@ -266,6 +266,8 @@ export function planOwnAuth(options) {
|
|
|
266
266
|
recordedIds[PLATFORM_AUTH_SMTP_SCOPE_KEY] &&
|
|
267
267
|
recordedIds[PLATFORM_AUTH_ZITADEL_RUN_OPTIONS_SCOPE_KEY] === AUTH_ZITADEL_RUN_OPTIONS &&
|
|
268
268
|
recordedIds[PLATFORM_AUTH_TUNNEL_ROUTES_SCOPE_KEY] === AUTH_TUNNEL_ROUTES_VERSION &&
|
|
269
|
+
// The login apps' source link settings; a stack from before them is written once.
|
|
270
|
+
recordedIds[PLATFORM_AUTH_ISSUER_SOURCE_ENV_SCOPE_KEY] === AUTH_ISSUER_SOURCE_ENV_VERSION &&
|
|
269
271
|
// The login apps' mail env (`LOGIN_MAIL_FROM`, the Lettermint key); a stack from before it is written again.
|
|
270
272
|
recordedIds[PLATFORM_AUTH_LOGIN_ENV_SCOPE_KEY] ===
|
|
271
273
|
authLoginEnvMarker(`noreply@${names.mailDomain}`, passkeyRpIdFor(org)) &&
|
|
@@ -273,7 +275,7 @@ export function planOwnAuth(options) {
|
|
|
273
275
|
(options.signinMailWritable === false ||
|
|
274
276
|
recordedIds[PLATFORM_AUTH_SIGNIN_MAIL_SCOPE_KEY] === signinMailMarker(org.displayName)) &&
|
|
275
277
|
// An entry with `mcp` needs the broker's database and secret (auth ADR 0002).
|
|
276
|
-
(!orgDeclaresMcp(org) || recordedIds[PLATFORM_AUTH_MCP_SCOPE_KEY] ===
|
|
278
|
+
(!orgDeclaresMcp(org) || recordedIds[PLATFORM_AUTH_MCP_SCOPE_KEY] === AUTH_MCP_ENV_VERSION) &&
|
|
277
279
|
(Boolean(recordedIds[PLATFORM_AUTH_LOGIN_CLIENT_USER_SCOPE_KEY]) || !brokerHoldsAnEntry) &&
|
|
278
280
|
// In-app sign-in: the second login app is recorded once it is deployed and holds the token, which
|
|
279
281
|
// needs the login client; until the broker holds an entry that cannot be, so the step waits.
|
|
@@ -87,11 +87,26 @@ export declare function authLoginEnvMarker(smtpSender: string, passkeyRpId?: str
|
|
|
87
87
|
/** The {@link authLoginEnvMarker} the login apps' env was last written with, once live on them. */
|
|
88
88
|
export declare const PLATFORM_AUTH_LOGIN_ENV_SCOPE_KEY = "auth_login_env_applied";
|
|
89
89
|
/**
|
|
90
|
-
*
|
|
90
|
+
* The version of the two settings the sign-in app's source link reads (`ISSUER_SOURCE_URL`,
|
|
91
|
+
* `ISSUER_SOURCE_LINK_SECRET`). Raise it to write them again. They are kept apart from
|
|
92
|
+
* {@link AUTH_LOGIN_ENV_VERSION} on purpose: a change to the marker redeploys both login apps, and
|
|
93
|
+
* these two settings must not.
|
|
94
|
+
* 1: the login apps get both settings (foundry 0.76.0).
|
|
95
|
+
*/
|
|
96
|
+
export declare const AUTH_ISSUER_SOURCE_ENV_VERSION = "1";
|
|
97
|
+
/** The {@link AUTH_ISSUER_SOURCE_ENV_VERSION} the login apps' source link settings were last written with. */
|
|
98
|
+
export declare const PLATFORM_AUTH_ISSUER_SOURCE_ENV_SCOPE_KEY = "auth_issuer_source_env_applied";
|
|
99
|
+
/**
|
|
100
|
+
* `AUTH_MCP_ENV_VERSION` once the broker holds `MCP_DATABASE_URL`, `MCP_OAUTH_SECRET` and `AUTH_STATE_SECRET` (auth ADR 0002), recorded by the
|
|
91
101
|
* sign-in step when an entry of that broker declares `uses.auth.mcp`. A broker entry with `mcp` is not
|
|
92
102
|
* written before it, and an applied sign-in that wants it and lacks it plans its step again.
|
|
93
103
|
*/
|
|
94
104
|
export declare const PLATFORM_AUTH_MCP_SCOPE_KEY = "auth_mcp_env_applied";
|
|
105
|
+
/**
|
|
106
|
+
* The value of that marker. `'2'` since the broker also needs `AUTH_STATE_SECRET` (auth PR 510), so a
|
|
107
|
+
* broker applied under `'1'` plans its sign-in step once more and gets the key without a hand step.
|
|
108
|
+
*/
|
|
109
|
+
export declare const AUTH_MCP_ENV_VERSION = "2";
|
|
95
110
|
/** True when a hosting entry of the org declares `uses.auth.mcp`: its broker needs the MCP database and secret. */
|
|
96
111
|
export declare function orgDeclaresMcp(org: Pick<DeclaredOrg, 'hosting'>): boolean;
|
|
97
112
|
/**
|
|
@@ -96,11 +96,26 @@ export function authLoginEnvMarker(smtpSender, passkeyRpId) {
|
|
|
96
96
|
/** The {@link authLoginEnvMarker} the login apps' env was last written with, once live on them. */
|
|
97
97
|
export const PLATFORM_AUTH_LOGIN_ENV_SCOPE_KEY = 'auth_login_env_applied';
|
|
98
98
|
/**
|
|
99
|
-
*
|
|
99
|
+
* The version of the two settings the sign-in app's source link reads (`ISSUER_SOURCE_URL`,
|
|
100
|
+
* `ISSUER_SOURCE_LINK_SECRET`). Raise it to write them again. They are kept apart from
|
|
101
|
+
* {@link AUTH_LOGIN_ENV_VERSION} on purpose: a change to the marker redeploys both login apps, and
|
|
102
|
+
* these two settings must not.
|
|
103
|
+
* 1: the login apps get both settings (foundry 0.76.0).
|
|
104
|
+
*/
|
|
105
|
+
export const AUTH_ISSUER_SOURCE_ENV_VERSION = '1';
|
|
106
|
+
/** The {@link AUTH_ISSUER_SOURCE_ENV_VERSION} the login apps' source link settings were last written with. */
|
|
107
|
+
export const PLATFORM_AUTH_ISSUER_SOURCE_ENV_SCOPE_KEY = 'auth_issuer_source_env_applied';
|
|
108
|
+
/**
|
|
109
|
+
* `AUTH_MCP_ENV_VERSION` once the broker holds `MCP_DATABASE_URL`, `MCP_OAUTH_SECRET` and `AUTH_STATE_SECRET` (auth ADR 0002), recorded by the
|
|
100
110
|
* sign-in step when an entry of that broker declares `uses.auth.mcp`. A broker entry with `mcp` is not
|
|
101
111
|
* written before it, and an applied sign-in that wants it and lacks it plans its step again.
|
|
102
112
|
*/
|
|
103
113
|
export const PLATFORM_AUTH_MCP_SCOPE_KEY = 'auth_mcp_env_applied';
|
|
114
|
+
/**
|
|
115
|
+
* The value of that marker. `'2'` since the broker also needs `AUTH_STATE_SECRET` (auth PR 510), so a
|
|
116
|
+
* broker applied under `'1'` plans its sign-in step once more and gets the key without a hand step.
|
|
117
|
+
*/
|
|
118
|
+
export const AUTH_MCP_ENV_VERSION = '2';
|
|
104
119
|
/** True when a hosting entry of the org declares `uses.auth.mcp`: its broker needs the MCP database and secret. */
|
|
105
120
|
export function orgDeclaresMcp(org) {
|
|
106
121
|
return org.hosting.enabled && org.hosting.entries.some((e) => Boolean(e.uses?.auth?.mcp));
|
|
@@ -2295,6 +2310,8 @@ function planAuth(platform, recordedIds, executorBlocked, executorMissing, authD
|
|
|
2295
2310
|
// step's redeploy, so a platform recorded before them still plans it.
|
|
2296
2311
|
recordedIds[PLATFORM_AUTH_ZITADEL_RUN_OPTIONS_SCOPE_KEY] === AUTH_ZITADEL_RUN_OPTIONS &&
|
|
2297
2312
|
recordedIds[PLATFORM_AUTH_TUNNEL_ROUTES_SCOPE_KEY] === AUTH_TUNNEL_ROUTES_VERSION &&
|
|
2313
|
+
// The login apps' source link settings: a platform from before them plans the step once.
|
|
2314
|
+
recordedIds[PLATFORM_AUTH_ISSUER_SOURCE_ENV_SCOPE_KEY] === AUTH_ISSUER_SOURCE_ENV_VERSION &&
|
|
2298
2315
|
// The login apps' mail env: a platform from before it, or with a new apex, is written again.
|
|
2299
2316
|
// Without the held key the apply cannot record it, so it is asked for only once the key is held.
|
|
2300
2317
|
(!mailKeyHeld ||
|
|
@@ -1420,6 +1420,22 @@ export declare function brokerDeployNeeded(missingInputs: readonly string[], bro
|
|
|
1420
1420
|
* wrote there. Paired with {@link brokerDeployNeeded}, which reads it back.
|
|
1421
1421
|
*/
|
|
1422
1422
|
export declare function brokerDeployRecordValue(brokerAppId: string, authAppsRaw: string | undefined): string;
|
|
1423
|
+
/**
|
|
1424
|
+
* The held master key of the sign-in app's source link: one value the platform holds. It is read in foundry
|
|
1425
|
+
* only and never reaches a container, plan line, log or error; each company gets a key derived from it
|
|
1426
|
+
* ({@link issuerSourceLinkKey}). Never minted here; a person (or the host) puts it under this ref, and a
|
|
1427
|
+
* missing value only hides the link.
|
|
1428
|
+
*/
|
|
1429
|
+
export declare const PLATFORM_AUTH_ISSUER_SOURCE_LINK_SECRET_REF = "issuer-source-link-secret";
|
|
1430
|
+
/** The sign-in app's source link settings. The URL is public; the key never reaches a plan line, log or error. */
|
|
1431
|
+
export declare const LOGIN_ISSUER_SOURCE_URL_ENV = "ISSUER_SOURCE_URL";
|
|
1432
|
+
export declare const LOGIN_ISSUER_SOURCE_LINK_SECRET_ENV = "ISSUER_SOURCE_LINK_SECRET";
|
|
1433
|
+
/** The company id of a source link: the target's id (`platform`, or the org's slug), the one its internal names use. */
|
|
1434
|
+
export declare const ISSUER_SOURCE_COMPANY_ID: RegExp;
|
|
1435
|
+
/** The source link of one company. */
|
|
1436
|
+
export declare function issuerSourceUrl(company: string): string;
|
|
1437
|
+
/** A company's source link key: lowercase hex of HMAC-SHA256(master, company). */
|
|
1438
|
+
export declare function issuerSourceLinkKey(master: string, company: string): string;
|
|
1423
1439
|
/** Whether any desired env is missing from the app, or set to another value. */
|
|
1424
1440
|
export declare function envsDiffer(coolify: Pick<CoolifyPort, 'listEnvs'>, appId: string, desired: Array<{
|
|
1425
1441
|
key: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createHash, createPrivateKey, generateKeyPairSync, randomBytes, randomUUID, sign, } from 'node:crypto';
|
|
1
|
+
import { createHash, createHmac, createPrivateKey, generateKeyPairSync, randomBytes, randomUUID, sign, } from 'node:crypto';
|
|
2
2
|
import { isIPv4 } from 'node:net';
|
|
3
3
|
import { applyObjects, openInstance, platformMailSettingsObjects } from '@wtfalch/email/postmaster';
|
|
4
4
|
import { VERSIONS, coolifyImageFor, floatingTagBlocked, pinnedRef } from '../bootstrap/versions.js';
|
|
@@ -34,7 +34,7 @@ import { MAIL_APP_SCOPE_KEY, MAIL_DATABASE_NAME, MAIL_DATABASE_PASSWORD_ENV, MAI
|
|
|
34
34
|
import { OIKOS_INGRESS_BLOCKED, PLATFORM_OIKOS_INGRESS_KEY, oikosIngressApplied, oikosIngressZoneBlocked, provisionOikosIngress, } from './oikos-ingress.js';
|
|
35
35
|
import { recordOwnDnsHostname } from './own-dns.js';
|
|
36
36
|
import { platformNetwork, platformTarget, scopeFromPlatform, } from './own-services.js';
|
|
37
|
-
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_LOGIN_ENV_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_APPLIED_USAGE_TAIL_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_USAGE_TAIL_KEY, PLATFORM_USAGE_TAIL_REMOVED_SCOPE_KEY, PLATFORM_USAGE_TAIL_SCRIPT_NAME, PLATFORM_ZITADEL_ORG_SCOPE_KEY, archonFilesMarker, archonFirstOwnerLink, archonOrgDomain, archonOrgName, authLoginEnvMarker, desiredHeartbeatCheckNames, heartbeatCheckUrlRef, orgDeclaresMcp, parseBrokerDeployRecord, parseLoginClientRecord, platformBackupHeartbeatCheckName, platformMailHostPorts, platformMailMxHost, uptimeAccessTokenConflict, uptimeEffectiveVars, uptimeRecorded, uptimeTicksSyncValue, uptimeVarsDigest, usageTailEdgeEnv, usageTailEdgeEnvValue, usageTailRecorded, } from './platform-diff.js';
|
|
37
|
+
import { AUTH_ISSUER_SOURCE_ENV_VERSION, AUTH_MCP_ENV_VERSION, 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_ISSUER_SOURCE_ENV_SCOPE_KEY, PLATFORM_AUTH_KEY, PLATFORM_AUTH_LOGIN_CLIENT_UNDELIVERED_SCOPE_KEY, PLATFORM_AUTH_LOGIN_CLIENT_USER_SCOPE_KEY, PLATFORM_AUTH_LOGIN_ENV_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_APPLIED_USAGE_TAIL_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_USAGE_TAIL_KEY, PLATFORM_USAGE_TAIL_REMOVED_SCOPE_KEY, PLATFORM_USAGE_TAIL_SCRIPT_NAME, PLATFORM_ZITADEL_ORG_SCOPE_KEY, archonFilesMarker, archonFirstOwnerLink, archonOrgDomain, archonOrgName, authLoginEnvMarker, desiredHeartbeatCheckNames, heartbeatCheckUrlRef, orgDeclaresMcp, parseBrokerDeployRecord, parseLoginClientRecord, platformBackupHeartbeatCheckName, platformMailHostPorts, platformMailMxHost, uptimeAccessTokenConflict, uptimeEffectiveVars, uptimeRecorded, uptimeTicksSyncValue, uptimeVarsDigest, usageTailEdgeEnv, usageTailEdgeEnvValue, usageTailRecorded, } from './platform-diff.js';
|
|
38
38
|
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';
|
|
39
39
|
import { ensureRegistryLogins, provisionPlatformRegistry, registryLoginsDrifted, } from './platform-registry.js';
|
|
40
40
|
import { PLATFORM_ARCHON_IN_APP_SCOPE_KEY, archonInAppBack, archonInAppBind, archonInAppHost, archonInAppPoint, archonInAppUnbind, } from './sign-in-archon.js';
|
|
@@ -914,7 +914,8 @@ export class FoundryPlatformStepExecutor {
|
|
|
914
914
|
if (platform.auth.enabled && this.auth) {
|
|
915
915
|
const loginClientDrifted = await authLoginClientDrifted(platform, this.auth, recordedIds);
|
|
916
916
|
// The broker lacks the MCP database and secret a shared org's entry needs (auth ADR 0002).
|
|
917
|
-
const mcpDrifted = recordedIds[PLATFORM_AUTH_MCP_SCOPE_KEY] !==
|
|
917
|
+
const mcpDrifted = recordedIds[PLATFORM_AUTH_MCP_SCOPE_KEY] !== AUTH_MCP_ENV_VERSION &&
|
|
918
|
+
(await this.mcpWanted());
|
|
918
919
|
// The broker has no registrar token yet (`ZITADEL_REGISTRAR_TOKEN`).
|
|
919
920
|
// A token minted and never held is blocked (`blockedSteps`), not drift: no apply could change it.
|
|
920
921
|
const registrarDrifted = this.auth.registrarTokenWritable === true &&
|
|
@@ -1503,8 +1504,8 @@ export const PLATFORM_AUTH_PROJECT = 'platform';
|
|
|
1503
1504
|
* header and so cannot give two different apps the same public hostname.
|
|
1504
1505
|
*/
|
|
1505
1506
|
export const PLATFORM_AUTH_LOGIN_INTERNAL_NAME = 'platform-auth-login';
|
|
1506
|
-
/**
|
|
1507
|
-
const AUTH_ZITADEL_IMAGE = 'ghcr.io/
|
|
1507
|
+
/** The patched sign-in server (stock ZITADEL plus our patches; it keeps `/app/zitadel`). Private package: the Coolify server pulls it with its own login. Pinned in `versions.json` (#247). */
|
|
1508
|
+
const AUTH_ZITADEL_IMAGE = 'ghcr.io/wtfalch/auth-issuer';
|
|
1508
1509
|
const AUTH_ZITADEL_IMAGE_TAG = VERSIONS.zitadel;
|
|
1509
1510
|
/** `wtfalch/auth`'s own `login/Dockerfile` -- `docs/deploy.md`'s "One compose service, four containers... Two images are built from this repository: the proxy... and the login". */
|
|
1510
1511
|
const AUTH_REPO = 'wtfalch/auth';
|
|
@@ -2263,6 +2264,36 @@ export async function provisionAuthStack(scope, deps, target, input) {
|
|
|
2263
2264
|
loginEnvs.push({ key: 'LOGIN_MAIL_FROM', value: input.smtpSender });
|
|
2264
2265
|
if (input.passkeyRpId)
|
|
2265
2266
|
loginEnvs.push({ key: LOGIN_PASSKEY_RP_ID_ENV, value: input.passkeyRpId });
|
|
2267
|
+
// The sign-in app's source link: both settings, or neither. A master that is not held, or cannot be read,
|
|
2268
|
+
// hides the link and fails nothing; the marker stays unset, so the step is planned again once it is held.
|
|
2269
|
+
let issuerSourceSecret = null;
|
|
2270
|
+
let issuerSourceReadFailed = false;
|
|
2271
|
+
try {
|
|
2272
|
+
issuerSourceSecret =
|
|
2273
|
+
(await deps.heldKeys?.resolve(PLATFORM_AUTH_ISSUER_SOURCE_LINK_SECRET_REF)) ?? null;
|
|
2274
|
+
}
|
|
2275
|
+
catch {
|
|
2276
|
+
// No cause in the line: a failed read can carry anything.
|
|
2277
|
+
issuerSourceReadFailed = true;
|
|
2278
|
+
}
|
|
2279
|
+
let issuerSourceWritten = false;
|
|
2280
|
+
if (issuerSourceSecret && !ISSUER_SOURCE_COMPANY_ID.test(target.id)) {
|
|
2281
|
+
console.info(`${label}: ${target.id} is not a valid source link company id, so the sign-in app hides the source link`);
|
|
2282
|
+
}
|
|
2283
|
+
else if (issuerSourceSecret) {
|
|
2284
|
+
// The master key stays in foundry: the container gets a key derived for this company only.
|
|
2285
|
+
runtime.onSecret?.(issuerSourceSecret);
|
|
2286
|
+
const companyKey = issuerSourceLinkKey(issuerSourceSecret, target.id);
|
|
2287
|
+
runtime.onSecret?.(companyKey);
|
|
2288
|
+
loginEnvs.push({ key: LOGIN_ISSUER_SOURCE_URL_ENV, value: issuerSourceUrl(target.id) }, { key: LOGIN_ISSUER_SOURCE_LINK_SECRET_ENV, value: companyKey });
|
|
2289
|
+
issuerSourceWritten = true;
|
|
2290
|
+
}
|
|
2291
|
+
else if (issuerSourceReadFailed) {
|
|
2292
|
+
console.info(`${label}: reading the held key ${PLATFORM_AUTH_ISSUER_SOURCE_LINK_SECRET_REF} failed, so the sign-in app hides the source link for now`);
|
|
2293
|
+
}
|
|
2294
|
+
else {
|
|
2295
|
+
console.info(`${label}: the held key ${PLATFORM_AUTH_ISSUER_SOURCE_LINK_SECRET_REF} is not held, so the sign-in app hides the source link`);
|
|
2296
|
+
}
|
|
2266
2297
|
// `AUTH_APPS` and `ZITADEL_SERVICE_USER_TOKEN`/`_FILE` are not pushed here
|
|
2267
2298
|
// -- this function's own doc comment, "Known gaps". `LETTERMINT_API_TOKEN`
|
|
2268
2299
|
// is, once held (#210): pushing it costs nothing even while the broker
|
|
@@ -2299,7 +2330,7 @@ export async function provisionAuthStack(scope, deps, target, input) {
|
|
|
2299
2330
|
// container on its next deploy (`diff.ts`'s `coolifyCalls` follows the
|
|
2300
2331
|
// identical rule).
|
|
2301
2332
|
const zitadelEnvsChanged = await envsDiffer(deps.coolify, zitadelAppId, zitadelEnvs);
|
|
2302
|
-
const loginEnvsChanged = await envsDiffer(deps.coolify, loginAppId, loginEnvs);
|
|
2333
|
+
const loginEnvsChanged = await envsDiffer(deps.coolify, loginAppId, withoutIssuerSourceEnvs(loginEnvs));
|
|
2303
2334
|
const brokerEnvsChanged = await envsDiffer(deps.coolify, brokerAppId, brokerEnvs);
|
|
2304
2335
|
await deps.coolify.bulkUpsertEnvs(zitadelAppId, zitadelEnvs);
|
|
2305
2336
|
await deps.coolify.bulkUpsertEnvs(loginAppId, loginEnvs);
|
|
@@ -2331,7 +2362,7 @@ export async function provisionAuthStack(scope, deps, target, input) {
|
|
|
2331
2362
|
{ key: SIGN_IN_BIND_KEY_ENV, value: bindKey },
|
|
2332
2363
|
...(bindings ? [{ key: LOGIN_HOST_BINDINGS_ENV, value: bindings }] : []),
|
|
2333
2364
|
];
|
|
2334
|
-
signInEnvsChanged = await envsDiffer(deps.coolify, signInApp.appId, signInEnvs);
|
|
2365
|
+
signInEnvsChanged = await envsDiffer(deps.coolify, signInApp.appId, withoutIssuerSourceEnvs(signInEnvs));
|
|
2335
2366
|
await deps.coolify.bulkUpsertEnvs(signInApp.appId, signInEnvs);
|
|
2336
2367
|
staleRpIdSignIn = input.passkeyRpId
|
|
2337
2368
|
? false
|
|
@@ -2387,6 +2418,11 @@ export async function provisionAuthStack(scope, deps, target, input) {
|
|
|
2387
2418
|
if (input.signInLogin)
|
|
2388
2419
|
await record(SIGN_IN_BIND_KEY_SCOPE_KEY, '1');
|
|
2389
2420
|
}
|
|
2421
|
+
// The source link settings are written on both login apps and need no deploy of their own. Recorded only
|
|
2422
|
+
// when they were written, so a company without the master is planned again once it is held.
|
|
2423
|
+
if (issuerSourceWritten) {
|
|
2424
|
+
await record(PLATFORM_AUTH_ISSUER_SOURCE_ENV_SCOPE_KEY, AUTH_ISSUER_SOURCE_ENV_VERSION);
|
|
2425
|
+
}
|
|
2390
2426
|
// The mail env is live on the login apps: written above and deployed with them. Without the held
|
|
2391
2427
|
// key the marker stays unset, so the step is planned again once the key is held.
|
|
2392
2428
|
if (heldMailKey && input.smtpSender) {
|
|
@@ -2420,7 +2456,7 @@ export async function provisionAuthStack(scope, deps, target, input) {
|
|
|
2420
2456
|
}
|
|
2421
2457
|
// The env is live on the broker (and deployed when it changed): the entry write may now carry `mcp`.
|
|
2422
2458
|
if (input.mcp)
|
|
2423
|
-
await record(PLATFORM_AUTH_MCP_SCOPE_KEY,
|
|
2459
|
+
await record(PLATFORM_AUTH_MCP_SCOPE_KEY, AUTH_MCP_ENV_VERSION);
|
|
2424
2460
|
// ---- tunnel and DNS ------------------------------------------------------
|
|
2425
2461
|
const tunnels = await network.tunnels();
|
|
2426
2462
|
const existingTunnel = await tunnels.findTunnelByName(target.names.authTunnel);
|
|
@@ -7652,6 +7688,33 @@ export function brokerDeployRecordValue(brokerAppId, authAppsRaw) {
|
|
|
7652
7688
|
return JSON.stringify(record);
|
|
7653
7689
|
}
|
|
7654
7690
|
const LOGIN_PASSKEY_RP_ID_ENV = 'LOGIN_PASSKEY_RP_ID';
|
|
7691
|
+
/**
|
|
7692
|
+
* The held master key of the sign-in app's source link: one value the platform holds. It is read in foundry
|
|
7693
|
+
* only and never reaches a container, plan line, log or error; each company gets a key derived from it
|
|
7694
|
+
* ({@link issuerSourceLinkKey}). Never minted here; a person (or the host) puts it under this ref, and a
|
|
7695
|
+
* missing value only hides the link.
|
|
7696
|
+
*/
|
|
7697
|
+
export const PLATFORM_AUTH_ISSUER_SOURCE_LINK_SECRET_REF = 'issuer-source-link-secret';
|
|
7698
|
+
/** The sign-in app's source link settings. The URL is public; the key never reaches a plan line, log or error. */
|
|
7699
|
+
export const LOGIN_ISSUER_SOURCE_URL_ENV = 'ISSUER_SOURCE_URL';
|
|
7700
|
+
export const LOGIN_ISSUER_SOURCE_LINK_SECRET_ENV = 'ISSUER_SOURCE_LINK_SECRET';
|
|
7701
|
+
/** The company id of a source link: the target's id (`platform`, or the org's slug), the one its internal names use. */
|
|
7702
|
+
export const ISSUER_SOURCE_COMPANY_ID = /^[a-z0-9][a-z0-9_-]{0,62}$/;
|
|
7703
|
+
/** The source link of one company. */
|
|
7704
|
+
export function issuerSourceUrl(company) {
|
|
7705
|
+
return `https://auth-issuer-source.wtfalch-business.workers.dev/c/${company}/sha256/1bc635d35b65f87d8c3224d76aecf301cb01730cd31e1612a5e4e6f78a8d59ed/auth-issuer-source.tar.gz`;
|
|
7706
|
+
}
|
|
7707
|
+
/** A company's source link key: lowercase hex of HMAC-SHA256(master, company). */
|
|
7708
|
+
export function issuerSourceLinkKey(master, company) {
|
|
7709
|
+
return createHmac('sha256', master).update(company).digest('hex');
|
|
7710
|
+
}
|
|
7711
|
+
/**
|
|
7712
|
+
* Drops the source link settings from an env list before it is compared with the live env. Changing them
|
|
7713
|
+
* must not redeploy a login app: they reach a running app on its next deploy, and a new app gets them at create.
|
|
7714
|
+
*/
|
|
7715
|
+
function withoutIssuerSourceEnvs(envs) {
|
|
7716
|
+
return envs.filter((e) => e.key !== LOGIN_ISSUER_SOURCE_URL_ENV && e.key !== LOGIN_ISSUER_SOURCE_LINK_SECRET_ENV);
|
|
7717
|
+
}
|
|
7655
7718
|
/** Removes one env from an app when it is live. True when it was removed; false with nothing live or no delete on the port. */
|
|
7656
7719
|
async function removeLoginEnv(coolify, appId, key) {
|
|
7657
7720
|
if (typeof coolify.deleteEnv !== 'function')
|
package/package.json
CHANGED
package/versions.digests.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"zitadel": "sha256:
|
|
2
|
+
"zitadel": "sha256:67af7d205cbbae4c60ce90c5034521fbb73a711a49bffec6aa719927ca77a14f",
|
|
3
3
|
"synapse": "sha256:6b95dd129e35e97e8032cb759e5db4a516a5f7ef6c98d34d716fea1ae239b62a",
|
|
4
4
|
"mas": "sha256:52c18ffcc940220a3b6aa5985b7e09d24ac27f5ed10a4d660c312e48f73ff105",
|
|
5
5
|
"livekit": "sha256:6fd3b7088874c4d119160dd688798dfec852bc014786d392caad15f6f63912a3",
|
package/versions.json
CHANGED