@wtfalch/foundry 0.71.0 → 0.72.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/index.d.ts +1 -1
- package/dist/modules/sign-in.js +81 -9
- package/dist/modules/types.d.ts +2 -0
- package/dist/org.d.ts +18 -1
- package/dist/org.js +40 -0
- package/dist/plan/boule.d.ts +15 -0
- package/dist/plan/boule.js +13 -0
- package/dist/plan/broker-registrar-token.d.ts +69 -0
- package/dist/plan/broker-registrar-token.js +215 -0
- package/dist/plan/broker-registrar.d.ts +2 -0
- package/dist/plan/broker-registrar.js +5 -1
- package/dist/plan/diff.d.ts +13 -0
- package/dist/plan/diff.js +26 -5
- package/dist/plan/executor.d.ts +66 -0
- package/dist/plan/executor.js +136 -8
- package/dist/plan/health-check-settings.d.ts +13 -0
- package/dist/plan/health-check-settings.js +17 -0
- package/dist/plan/own-diff.d.ts +32 -0
- package/dist/plan/own-diff.js +67 -10
- package/dist/plan/own-hostnames.d.ts +8 -0
- package/dist/plan/own-hostnames.js +14 -0
- package/dist/plan/own-services.d.ts +7 -0
- package/dist/plan/own-services.js +2 -0
- package/dist/plan/plan-apply.d.ts +15 -0
- package/dist/plan/plan-apply.js +21 -0
- package/dist/plan/platform-diff.d.ts +9 -5
- package/dist/plan/platform-diff.js +16 -10
- package/dist/plan/platform-providers.d.ts +24 -2
- package/dist/plan/platform-providers.js +155 -12
- package/dist/plan/signin-mail.d.ts +18 -5
- package/dist/plan/signin-mail.js +14 -6
- package/dist/plan/zitadel-login-policy.d.ts +1 -1
- package/dist/plan/zitadel-login-policy.js +72 -46
- package/dist/plan/zitadel-machine-user.d.ts +14 -1
- package/dist/plan/zitadel-machine-user.js +96 -13
- package/package.json +3 -3
package/dist/plan/own-diff.d.ts
CHANGED
|
@@ -52,7 +52,35 @@ export declare const OWN_AUTH_BLOCKED_KEY = "own-auth";
|
|
|
52
52
|
export { OWN_AUTH_APPLIED_HOSTNAME_SCOPE_KEY };
|
|
53
53
|
/** The auth git ref an own org's sign-in last deployed, so a changed declared ref plans the step again. */
|
|
54
54
|
export declare const OWN_AUTH_APPLIED_GIT_REF_SCOPE_KEY = "own_auth_applied_git_ref";
|
|
55
|
+
/** The Git repository the company's login app, in-app login app and broker are built from. */
|
|
56
|
+
export declare const OWN_AUTH_REPOSITORY = "wtfalch/auth";
|
|
57
|
+
/** What the host read of the declared auth branch: its head commit, or why it could not be read (fixed text). */
|
|
58
|
+
export type OwnAuthHead = {
|
|
59
|
+
commit: string;
|
|
60
|
+
} | {
|
|
61
|
+
failed: string;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* True for a declared auth ref whose head can move: not a commit and not a release tag (`v1.2.3`, `1.2`).
|
|
65
|
+
* A tag or a commit already names one build, so it is never read.
|
|
66
|
+
*/
|
|
67
|
+
export declare function isBranchRef(ref: string): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* The company's auth apps a moved branch head redeploys, with the name their `built_commit_<name>` is recorded
|
|
70
|
+
* under. The in-app login app is listed once it is recorded; the broker once it was ever deployed (before that it
|
|
71
|
+
* crash-loops on missing inputs and is not redeployed, so it never records a build).
|
|
72
|
+
*/
|
|
73
|
+
export declare function ownAuthBuildApps(org: Pick<DeclaredOrg, 'slug'>, recordedIds: Readonly<Record<string, string>>): Array<{
|
|
74
|
+
id: string;
|
|
75
|
+
name: string;
|
|
76
|
+
}>;
|
|
55
77
|
export interface PlanOwnAuthOptions {
|
|
78
|
+
/**
|
|
79
|
+
* #886: the head of the declared auth branch, read by the executor (`FoundryStepExecutor.ownAuthHead`).
|
|
80
|
+
* Absent: not read (a tag, a commit, or a host without the read), so only the declared ref decides.
|
|
81
|
+
* A failed read plans nothing from the head and is reported.
|
|
82
|
+
*/
|
|
83
|
+
authHead?: OwnAuthHead;
|
|
56
84
|
org: DeclaredOrg;
|
|
57
85
|
recordedIds: Readonly<Record<string, string>>;
|
|
58
86
|
readiness: OrgPlatformReadiness;
|
|
@@ -76,6 +104,10 @@ export interface PlanOwnAuthOptions {
|
|
|
76
104
|
signinDbBackupConfigured?: boolean;
|
|
77
105
|
/** The host's ZITADEL port can write the sign-in mail text and look. Default true. False: the marker is not required. */
|
|
78
106
|
signinMailWritable?: boolean;
|
|
107
|
+
/** The host's ZITADEL port can make the broker's registrar token. True: it is asked for. False: reported blocked. Absent: neither. */
|
|
108
|
+
registrarTokenWritable?: boolean;
|
|
109
|
+
/** The registrar token's held-key ref holds a value, so a rerun after a crash delivers it. Default false. */
|
|
110
|
+
registrarTokenHeld?: boolean;
|
|
79
111
|
}
|
|
80
112
|
/**
|
|
81
113
|
* An own org (`services.own`) runs its own sign-in: ZITADEL, the login app
|
package/dist/plan/own-diff.js
CHANGED
|
@@ -3,18 +3,20 @@ import { sfuSlotPorts } from '../calls-sfu.js';
|
|
|
3
3
|
import { SFU_SLOT_MAX, ownsServices } from '../org.js';
|
|
4
4
|
import { ZITADEL_ORG_SCOPE_KEY } from './agora.js';
|
|
5
5
|
import { brokerEntryAppliedScopeKey } from './broker-entries.js';
|
|
6
|
+
import { REGISTRAR_TOKEN_SCOPE_KEY, REGISTRAR_TOKEN_UNDELIVERED_SCOPE_KEY, registrarTokenDue, } from './broker-registrar-token.js';
|
|
7
|
+
import { builtCommitScopeKey } from './build-record.js';
|
|
6
8
|
import { ZITADEL_FILES_PROJECT_SCOPE_KEY } from './core-files.js';
|
|
7
9
|
import { OWN_AUTH_APPLIED_HOSTNAME_SCOPE_KEY, ownAuthApplied } from './login-host.js';
|
|
8
10
|
import { OWN_MAIL_SMTP_INTERNAL_SCOPE_KEY, OWN_MAIL_SMTP_PORT_BASE, OWN_MAIL_SMTP_PORT_SCOPE_KEY, isOwnMailSmtpPort, } from './mail-relay.js';
|
|
9
11
|
import { mailStoreIsLegacy, mailStoreSwitchDescription } from './mail-store.js';
|
|
10
|
-
import { ownHostnames } from './own-hostnames.js';
|
|
12
|
+
import { ownHostnames, passkeyRpIdFor } from './own-hostnames.js';
|
|
11
13
|
import { JOBS_PUBLIC_URL_SCOPE_KEY, OWN_JOBS_ROUTE_TUNNEL_SCOPE_KEY } from './own-jobs-route.js';
|
|
12
14
|
import { OWN_FILES_R2_SCOPE_KEY, OWN_SERVICES_APPLIED_SCOPE_KEY, orgTarget, } from './own-services.js';
|
|
13
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';
|
|
14
|
-
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_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_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';
|
|
15
17
|
import { OWN_FILES_RUNNER_SCOPE_KEY } from './platform-org.js';
|
|
16
|
-
import { JOBS_ADMIN_TOKEN_UNDELIVERED_SCOPE_KEY, JOBS_APPLIED_GIT_REF_SCOPE_KEY, PLATFORM_AUTH_DB_SCOPE_KEY, PLATFORM_AUTH_ZITADEL_SCOPE_KEY, PLATFORM_FILES_SHARE_DOMAINS_SCOPE_KEY, filesShareDomains, } from './platform-providers.js';
|
|
17
|
-
import { SIGN_IN_LOGIN_APP_SCOPE_KEY, inAppSignIn } from './sign-in.js';
|
|
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
|
+
import { SIGN_IN_LOGIN_APP_SCOPE_KEY, inAppSignIn, signInLoginNames } from './sign-in.js';
|
|
18
20
|
import { signinDbBackupMissing } from './signin-db-backup.js';
|
|
19
21
|
import { SIGNIN_DB_ZITADEL_PENDING_SCOPE_KEY, signinDbCertDue } from './signin-db-tls.js';
|
|
20
22
|
import { SIGNIN_DB_TLS_SCOPE_KEY } from './signin-db.js';
|
|
@@ -126,6 +128,39 @@ export const OWN_AUTH_BLOCKED_KEY = 'own-auth';
|
|
|
126
128
|
export { OWN_AUTH_APPLIED_HOSTNAME_SCOPE_KEY };
|
|
127
129
|
/** The auth git ref an own org's sign-in last deployed, so a changed declared ref plans the step again. */
|
|
128
130
|
export const OWN_AUTH_APPLIED_GIT_REF_SCOPE_KEY = 'own_auth_applied_git_ref';
|
|
131
|
+
/** The Git repository the company's login app, in-app login app and broker are built from. */
|
|
132
|
+
export const OWN_AUTH_REPOSITORY = 'wtfalch/auth';
|
|
133
|
+
const FULL_SHA = /^[0-9a-f]{40}$/;
|
|
134
|
+
/**
|
|
135
|
+
* True for a declared auth ref whose head can move: not a commit and not a release tag (`v1.2.3`, `1.2`).
|
|
136
|
+
* A tag or a commit already names one build, so it is never read.
|
|
137
|
+
*/
|
|
138
|
+
export function isBranchRef(ref) {
|
|
139
|
+
return !FULL_SHA.test(ref) && !/^v?\d+\.\d+/.test(ref);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* The company's auth apps a moved branch head redeploys, with the name their `built_commit_<name>` is recorded
|
|
143
|
+
* under. The in-app login app is listed once it is recorded; the broker once it was ever deployed (before that it
|
|
144
|
+
* crash-loops on missing inputs and is not redeployed, so it never records a build).
|
|
145
|
+
*/
|
|
146
|
+
export function ownAuthBuildApps(org, recordedIds) {
|
|
147
|
+
const target = orgTarget(org);
|
|
148
|
+
const apps = [
|
|
149
|
+
{ id: recordedIds[PLATFORM_AUTH_LOGIN_SCOPE_KEY], name: target.names.authLogin },
|
|
150
|
+
{ id: recordedIds[SIGN_IN_LOGIN_APP_SCOPE_KEY], name: signInLoginNames(target).name },
|
|
151
|
+
{
|
|
152
|
+
id: recordedIds[PLATFORM_AUTH_BROKER_DEPLOYED_SCOPE_KEY]
|
|
153
|
+
? recordedIds[PLATFORM_AUTH_BROKER_SCOPE_KEY]
|
|
154
|
+
: undefined,
|
|
155
|
+
name: target.names.authBroker,
|
|
156
|
+
},
|
|
157
|
+
];
|
|
158
|
+
return apps.filter((app) => Boolean(app.id));
|
|
159
|
+
}
|
|
160
|
+
/** True when the read head differs from the commit any of the company's auth apps was last built from. */
|
|
161
|
+
function authHeadMoved(org, recordedIds, head) {
|
|
162
|
+
return ownAuthBuildApps(org, recordedIds).some((app) => recordedIds[builtCommitScopeKey(app.name)] !== head);
|
|
163
|
+
}
|
|
129
164
|
/**
|
|
130
165
|
* An own org (`services.own`) runs its own sign-in: ZITADEL, the login app
|
|
131
166
|
* and the broker in the org's own Coolify project, at `auth.<domain>`.
|
|
@@ -197,7 +232,21 @@ export function planOwnAuth(options) {
|
|
|
197
232
|
return blocked('the host supplies no backup bucket for the sign-in database');
|
|
198
233
|
}
|
|
199
234
|
}
|
|
235
|
+
const registrarDue = options.registrarTokenWritable === true && registrarTokenDue(org, recordedIds);
|
|
236
|
+
// A token minted whose hold did not finish is never minted again: a person removes the marker.
|
|
237
|
+
if (registrarDue &&
|
|
238
|
+
recordedIds[REGISTRAR_TOKEN_UNDELIVERED_SCOPE_KEY] &&
|
|
239
|
+
!recordedIds[REGISTRAR_TOKEN_SCOPE_KEY] &&
|
|
240
|
+
!options.registrarTokenHeld) {
|
|
241
|
+
return blocked(`the broker's registrar token of ZITADEL machine user ${recordedIds[REGISTRAR_TOKEN_UNDELIVERED_SCOPE_KEY]} was minted but never confirmed held; remove the recorded key ${REGISTRAR_TOKEN_UNDELIVERED_SCOPE_KEY} to make a new one`);
|
|
242
|
+
}
|
|
200
243
|
const hostname = names.auth;
|
|
244
|
+
// #886: a branch head that moved past what the auth apps were built from plans the step again. A failed read
|
|
245
|
+
// plans nothing from the head and is reported, so a broken read never loops.
|
|
246
|
+
const head = options.authHead;
|
|
247
|
+
const headCommit = head && 'commit' in head ? head.commit : undefined;
|
|
248
|
+
const headFailed = head && 'failed' in head ? head.failed : undefined;
|
|
249
|
+
const headMoved = headCommit !== undefined && authHeadMoved(org, recordedIds, headCommit);
|
|
201
250
|
// The broker starts only on a valid `AUTH_APPS`, and the login client is minted only once it does:
|
|
202
251
|
// the first broker entry is what lets this step finish, as the platform's first registration does.
|
|
203
252
|
// Archon's registration is an entry too; the platform records it, not this org, so it comes in
|
|
@@ -219,7 +268,7 @@ export function planOwnAuth(options) {
|
|
|
219
268
|
recordedIds[PLATFORM_AUTH_TUNNEL_ROUTES_SCOPE_KEY] === AUTH_TUNNEL_ROUTES_VERSION &&
|
|
220
269
|
// The login apps' mail env (`LOGIN_MAIL_FROM`, the Lettermint key); a stack from before it is written again.
|
|
221
270
|
recordedIds[PLATFORM_AUTH_LOGIN_ENV_SCOPE_KEY] ===
|
|
222
|
-
authLoginEnvMarker(`noreply@${names.mailDomain}
|
|
271
|
+
authLoginEnvMarker(`noreply@${names.mailDomain}`, passkeyRpIdFor(org)) &&
|
|
223
272
|
// The code mail carries the company's name and design; an older apply, or a renamed company, writes it again.
|
|
224
273
|
(options.signinMailWritable === false ||
|
|
225
274
|
recordedIds[PLATFORM_AUTH_SIGNIN_MAIL_SCOPE_KEY] === signinMailMarker(org.displayName)) &&
|
|
@@ -234,15 +283,23 @@ export function planOwnAuth(options) {
|
|
|
234
283
|
!signinDbCertDue(recordedIds, options.now ?? Date.now()) &&
|
|
235
284
|
!signinDbBackupMissing(recordedIds) &&
|
|
236
285
|
// ZITADEL was started on the TLS database and never seen healthy there: the step starts it again.
|
|
237
|
-
!recordedIds[SIGNIN_DB_ZITADEL_PENDING_SCOPE_KEY]
|
|
286
|
+
!recordedIds[SIGNIN_DB_ZITADEL_PENDING_SCOPE_KEY] &&
|
|
287
|
+
!headMoved &&
|
|
288
|
+
// The broker's registrar token (a company with customers or apps): made, or adopted, and covering the recorded organisations.
|
|
289
|
+
!registrarDue) {
|
|
238
290
|
// Applied and otherwise done: say why the mail stays ZITADEL's stock text, once.
|
|
239
291
|
const mailMissing = options.signinMailWritable === false &&
|
|
240
292
|
recordedIds[PLATFORM_AUTH_SIGNIN_MAIL_SCOPE_KEY] !== signinMailMarker(org.displayName);
|
|
293
|
+
const registrarMissing = options.registrarTokenWritable === false && registrarTokenDue(org, recordedIds);
|
|
241
294
|
return {
|
|
242
295
|
step: null,
|
|
243
|
-
blocked:
|
|
244
|
-
? `${org.slug}'s
|
|
245
|
-
:
|
|
296
|
+
blocked: headFailed
|
|
297
|
+
? `${org.slug}'s auth apps were not checked for a newer auth commit: ${headFailed}`
|
|
298
|
+
: registrarMissing
|
|
299
|
+
? `${org.slug}'s sign-in broker has no registrar token: the host cannot make it, so its apps cannot register sign-in`
|
|
300
|
+
: mailMissing
|
|
301
|
+
? `${org.slug}'s sign-in mail keeps ZITADEL's default text: the host cannot write message texts`
|
|
302
|
+
: null,
|
|
246
303
|
};
|
|
247
304
|
}
|
|
248
305
|
return {
|
|
@@ -252,7 +309,7 @@ export function planOwnAuth(options) {
|
|
|
252
309
|
system: 'coolify',
|
|
253
310
|
kind: 'own-auth',
|
|
254
311
|
key: org.id,
|
|
255
|
-
desired: { hostname, gitRef },
|
|
312
|
+
desired: { hostname, gitRef, ...(headCommit ? { head: headCommit } : {}) },
|
|
256
313
|
},
|
|
257
314
|
step: {
|
|
258
315
|
description: `provision ${org.slug}'s own sign-in at ${hostname}`,
|
|
@@ -27,3 +27,11 @@ export interface OwnHostnames {
|
|
|
27
27
|
* {@link loginHostnameFor}, so the two cannot disagree.
|
|
28
28
|
*/
|
|
29
29
|
export declare function ownHostnames(org: Pick<DeclaredOrg, 'slug' | 'domain' | 'issuer' | 'mail'>): OwnHostnames;
|
|
30
|
+
/**
|
|
31
|
+
* The WebAuthn RP ID for a company's login apps (`LOGIN_PASSKEY_RP_ID`): the company's own domain, which
|
|
32
|
+
* `auth.<domain>` and the in-app hosts (`agora.<domain>`, `boule.<domain>`) all sit under, so one passkey
|
|
33
|
+
* works on each. Undefined for a company without its own sign-in domain: its `<slug>-auth.oikos.host` shares
|
|
34
|
+
* `oikos.host` with every other company, so an RP ID there would span companies. The login app refuses a
|
|
35
|
+
* host outside the RP ID, so a company's hosts stay under its domain or the variable stays unset.
|
|
36
|
+
*/
|
|
37
|
+
export declare function passkeyRpIdFor(org: Pick<DeclaredOrg, 'domain' | 'issuer'>): string | undefined;
|
|
@@ -23,3 +23,17 @@ export function ownHostnames(org) {
|
|
|
23
23
|
mailDomain: mailOnDomain && domain ? domain : `${slug}.${NO_DOMAIN_ZONE}`,
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
|
+
const BARE_DOMAIN = /^(?=.{1,253}$)[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)+$/;
|
|
27
|
+
/**
|
|
28
|
+
* The WebAuthn RP ID for a company's login apps (`LOGIN_PASSKEY_RP_ID`): the company's own domain, which
|
|
29
|
+
* `auth.<domain>` and the in-app hosts (`agora.<domain>`, `boule.<domain>`) all sit under, so one passkey
|
|
30
|
+
* works on each. Undefined for a company without its own sign-in domain: its `<slug>-auth.oikos.host` shares
|
|
31
|
+
* `oikos.host` with every other company, so an RP ID there would span companies. The login app refuses a
|
|
32
|
+
* host outside the RP ID, so a company's hosts stay under its domain or the variable stays unset.
|
|
33
|
+
*/
|
|
34
|
+
export function passkeyRpIdFor(org) {
|
|
35
|
+
const domain = org.domain?.toLowerCase();
|
|
36
|
+
if (!domain || !org.issuer.usesDomain || !BARE_DOMAIN.test(domain))
|
|
37
|
+
return undefined;
|
|
38
|
+
return domain;
|
|
39
|
+
}
|
|
@@ -61,6 +61,8 @@ export interface ServiceTarget {
|
|
|
61
61
|
};
|
|
62
62
|
readonly refs: {
|
|
63
63
|
instanceBearer: string;
|
|
64
|
+
/** The sign-in broker's ZITADEL registrar token (`ZITADEL_REGISTRAR_TOKEN`), held after its one mint. */
|
|
65
|
+
registrarToken: string;
|
|
64
66
|
authAdmin: string;
|
|
65
67
|
brokerMailKey: string;
|
|
66
68
|
/** The connection string of the database for the broker's customer workspaces: foundry holds it after making the database, or the operator holds one first. */
|
|
@@ -209,4 +211,9 @@ export interface OwnServicesPort {
|
|
|
209
211
|
/** Builds the ZITADEL port for one company instance. */
|
|
210
212
|
zitadelFor(instance: OwnAuthInstance): ZitadelPort;
|
|
211
213
|
provision: OwnServicesProvision;
|
|
214
|
+
/**
|
|
215
|
+
* #886: the head commit (40 hex characters) of `ref` in the Git repository `repository` (`owner/name`).
|
|
216
|
+
* Absent: a moved auth branch is not noticed. The executor puts a timeout on every call.
|
|
217
|
+
*/
|
|
218
|
+
readBranchHead?(repository: string, ref: string): Promise<string>;
|
|
212
219
|
}
|
|
@@ -46,6 +46,7 @@ export function platformTarget() {
|
|
|
46
46
|
},
|
|
47
47
|
refs: {
|
|
48
48
|
instanceBearer: 'zitadel:platform:instanceBearer',
|
|
49
|
+
registrarToken: 'zitadel:platform:registrarToken',
|
|
49
50
|
authAdmin: 'zitadel:platform:admin',
|
|
50
51
|
brokerMailKey: 'lettermint:platform:apiToken',
|
|
51
52
|
brokerWorkspacesDatabase: 'auth:platform:workspacesDatabaseUrl',
|
|
@@ -103,6 +104,7 @@ export function orgTarget(org) {
|
|
|
103
104
|
},
|
|
104
105
|
refs: {
|
|
105
106
|
instanceBearer: `zitadel:org:${s}:instanceBearer`,
|
|
107
|
+
registrarToken: `zitadel:org:${s}:registrarToken`,
|
|
106
108
|
authAdmin: `zitadel:org:${s}:admin`,
|
|
107
109
|
brokerMailKey: `lettermint:org:${s}:apiToken`,
|
|
108
110
|
brokerWorkspacesDatabase: `auth:org:${s}:workspacesDatabaseUrl`,
|
|
@@ -18,6 +18,7 @@ import type { GithubDeployKeysPort } from './github-deploy-keys.js';
|
|
|
18
18
|
import { type LiveReaders, type LiveResource, type PlatformLiveReaders } from './live.js';
|
|
19
19
|
import { type LoginHostDiscoveryPort } from './login-host.js';
|
|
20
20
|
import { type OrgPostgresMigrationConfirmation } from './org-postgres-migrate.js';
|
|
21
|
+
import type { OwnAuthHead } from './own-diff.js';
|
|
21
22
|
import { type EdgePortName, type HeldKeyResolver, type PlatformOwnerInput } from './platform-org.js';
|
|
22
23
|
import type { CallsSfuConnectionPort } from './platform-sfu.js';
|
|
23
24
|
import { type ReleaseContext, type ReleaseHost } from './release-host.js';
|
|
@@ -212,8 +213,12 @@ export interface StepExecutor {
|
|
|
212
213
|
}>;
|
|
213
214
|
/** Whether the executor can mint an own org's Lettermint key: a Lettermint port and a reader for the held ref. */
|
|
214
215
|
ownMailKeyMintable?(): boolean;
|
|
216
|
+
/** Whether the ZITADEL port can set the customers organisation's login policy. Absent: planned, as before. */
|
|
217
|
+
customersLoginPolicyAvailable?(): boolean;
|
|
215
218
|
/** C3: whether the ZITADEL port can mint and revoke a customers directory credential. */
|
|
216
219
|
customersDirectoryAvailable?(): boolean;
|
|
220
|
+
/** Whether the ZITADEL port can grant the directory user its customer removal role. */
|
|
221
|
+
customersDirectoryRolesAvailable?(): boolean;
|
|
217
222
|
/** C3: whether the host supplied `mintBouleArchonCredential`. */
|
|
218
223
|
bouleArchonCredentialAvailable?(): boolean;
|
|
219
224
|
/** Every Coolify server with whether it is usable and reachable; blue/green plans copies on each. Optional: an executor without it plans no blue/green. */
|
|
@@ -264,6 +269,12 @@ export interface StepExecutor {
|
|
|
264
269
|
ownSigninDbBackupConfigured?(): boolean;
|
|
265
270
|
/** Whether the host's company ZITADEL port can write the sign-in mail text and look. Absent: true. */
|
|
266
271
|
ownSigninMailWritable?(): boolean;
|
|
272
|
+
/** #886: reads the head of the declared auth branch for an own org; `undefined` when there is nothing to read. */
|
|
273
|
+
ownAuthHead?(gitRef: string): Promise<OwnAuthHead | undefined>;
|
|
274
|
+
/** Whether the host's ZITADEL port can grant the shared sign-in's registrar user a company's organisations. Absent: false. */
|
|
275
|
+
registrarGrantsAvailable?(): boolean;
|
|
276
|
+
/** Whether the host's company ZITADEL port can make the broker's registrar token. Absent: the planner neither asks for it nor reports it. */
|
|
277
|
+
ownRegistrarTokenWritable?(): boolean;
|
|
267
278
|
/**
|
|
268
279
|
* Whether the host's scoped ports for this own org include a files-organisation
|
|
269
280
|
* port. The planner plans no `files:organisation` step for an own org while
|
|
@@ -1022,8 +1033,12 @@ export declare class FoundryPlanApply implements PlanApply {
|
|
|
1022
1033
|
* only the ref.
|
|
1023
1034
|
*/
|
|
1024
1035
|
private heldEnvReadiness;
|
|
1036
|
+
/** #886: the head of an own org's declared auth branch, or nothing when the org is not own or the executor has no read. */
|
|
1037
|
+
private ownAuthHeadFor;
|
|
1025
1038
|
/** An own org's Lettermint key is held. A resolver that throws counts as not held. */
|
|
1026
1039
|
private ownMailKeyHeld;
|
|
1040
|
+
/** An own org's registrar token is held (a rerun after a crash delivers it instead of blocking). */
|
|
1041
|
+
private ownRegistrarTokenHeld;
|
|
1027
1042
|
/** Which of an own org's undelivered-secret refs hold a value now, so a rerun resumes instead of blocking. */
|
|
1028
1043
|
private ownResumableRefs;
|
|
1029
1044
|
/**
|
package/dist/plan/plan-apply.js
CHANGED
|
@@ -1718,7 +1718,9 @@ export class FoundryPlanApply {
|
|
|
1718
1718
|
oikosZoneReady: platformReadiness.oikosIngressApplied === true,
|
|
1719
1719
|
ownMailKeyHeld: await this.ownMailKeyHeld(org),
|
|
1720
1720
|
ownMailKeyMintable: this.executor.ownMailKeyMintable?.() ?? false,
|
|
1721
|
+
customersLoginPolicy: this.executor.customersLoginPolicyAvailable?.() ?? true,
|
|
1721
1722
|
customersDirectory: this.executor.customersDirectoryAvailable?.() ?? false,
|
|
1723
|
+
customersDirectoryRoles: this.executor.customersDirectoryRolesAvailable?.() ?? false,
|
|
1722
1724
|
bouleArchonCredential: this.executor.bouleArchonCredentialAvailable?.() ?? false,
|
|
1723
1725
|
...(blueGreenServers ? { blueGreenServers } : {}),
|
|
1724
1726
|
...(stageSinkDown ? { stageSinkDown } : {}),
|
|
@@ -1741,6 +1743,10 @@ export class FoundryPlanApply {
|
|
|
1741
1743
|
ownSigninDbKeysPuttable: this.executor.ownSigninDbKeysPuttable?.() ?? true,
|
|
1742
1744
|
ownSigninDbBackupConfigured: this.executor.ownSigninDbBackupConfigured?.() ?? true,
|
|
1743
1745
|
ownSigninMailWritable: this.executor.ownSigninMailWritable?.() ?? true,
|
|
1746
|
+
...(await this.ownAuthHeadFor(org, platformReadiness)),
|
|
1747
|
+
registrarGrants: this.executor.registrarGrantsAvailable?.() ?? false,
|
|
1748
|
+
ownRegistrarTokenWritable: this.executor.ownRegistrarTokenWritable?.(),
|
|
1749
|
+
ownRegistrarTokenHeld: await this.ownRegistrarTokenHeld(org),
|
|
1744
1750
|
...(this.backupGate
|
|
1745
1751
|
? {
|
|
1746
1752
|
backupStorageUuid: (await this.platformDeps?.store.getServiceIds())?.[PLATFORM_BACKUP_S3_STORAGE_SCOPE_KEY] || null,
|
|
@@ -1964,6 +1970,14 @@ export class FoundryPlanApply {
|
|
|
1964
1970
|
}
|
|
1965
1971
|
return { store: Boolean(heldKeys), refs, differs, unreadable, invalid };
|
|
1966
1972
|
}
|
|
1973
|
+
/** #886: the head of an own org's declared auth branch, or nothing when the org is not own or the executor has no read. */
|
|
1974
|
+
async ownAuthHeadFor(org, readiness) {
|
|
1975
|
+
const gitRef = readiness.serviceGitRefs?.auth;
|
|
1976
|
+
if (!ownsServices(org) || !gitRef || !this.executor.ownAuthHead)
|
|
1977
|
+
return {};
|
|
1978
|
+
const head = await this.executor.ownAuthHead(gitRef);
|
|
1979
|
+
return head ? { ownAuthHead: head } : {};
|
|
1980
|
+
}
|
|
1967
1981
|
/** An own org's Lettermint key is held. A resolver that throws counts as not held. */
|
|
1968
1982
|
async ownMailKeyHeld(org) {
|
|
1969
1983
|
const held = this.platformDeps?.heldKeys;
|
|
@@ -1971,6 +1985,13 @@ export class FoundryPlanApply {
|
|
|
1971
1985
|
return false;
|
|
1972
1986
|
return (await heldOwnKeyRefs(held, org)).has(orgTarget(org).refs.brokerMailKey);
|
|
1973
1987
|
}
|
|
1988
|
+
/** An own org's registrar token is held (a rerun after a crash delivers it instead of blocking). */
|
|
1989
|
+
async ownRegistrarTokenHeld(org) {
|
|
1990
|
+
const held = this.platformDeps?.heldKeys;
|
|
1991
|
+
if (!ownsServices(org) || !held)
|
|
1992
|
+
return false;
|
|
1993
|
+
return (await heldOwnKeyRefs(held, org)).has(orgTarget(org).refs.registrarToken);
|
|
1994
|
+
}
|
|
1974
1995
|
/** Which of an own org's undelivered-secret refs hold a value now, so a rerun resumes instead of blocking. */
|
|
1975
1996
|
async ownResumableRefs(org) {
|
|
1976
1997
|
const held = this.platformDeps?.heldKeys;
|
|
@@ -70,16 +70,18 @@ export declare const AUTH_TUNNEL_ROUTES_VERSION = "2";
|
|
|
70
70
|
export declare const PLATFORM_AUTH_TUNNEL_ROUTES_SCOPE_KEY = "auth_tunnel_routes_applied";
|
|
71
71
|
/**
|
|
72
72
|
* The version of the env `provisionAuthStack` writes to the login apps (`LOGIN_MAIL_FROM`,
|
|
73
|
-
* `LETTERMINT_API_TOKEN`). Raise it when a key is added or its meaning changes, so an applied
|
|
73
|
+
* `LETTERMINT_API_TOKEN`, `LOGIN_PASSKEY_RP_ID`). Raise it when a key is added or its meaning changes, so an applied
|
|
74
74
|
* sign-in plans its step once more and the apps get the env. Never holds a value.
|
|
75
75
|
* 1: the login apps send their own invite and verification mail (auth 0.35.0, foundry 0.68.0).
|
|
76
|
+
* 2: a company's login apps get `LOGIN_PASSKEY_RP_ID` (foundry 0.70.2).
|
|
76
77
|
*/
|
|
77
|
-
export declare const AUTH_LOGIN_ENV_VERSION = "
|
|
78
|
+
export declare const AUTH_LOGIN_ENV_VERSION = "2";
|
|
78
79
|
/**
|
|
79
|
-
* What the login apps' env is written for: the version
|
|
80
|
-
*
|
|
80
|
+
* What the login apps' env is written for: the version, the sender address (`LOGIN_MAIL_FROM`) and the
|
|
81
|
+
* passkey RP ID (`LOGIN_PASSKEY_RP_ID`, none for the platform and a company without its own domain).
|
|
82
|
+
* Neither is a secret; a new mail domain or a changed RP ID gives a new marker, so the step is planned again.
|
|
81
83
|
*/
|
|
82
|
-
export declare function authLoginEnvMarker(smtpSender: string): string;
|
|
84
|
+
export declare function authLoginEnvMarker(smtpSender: string, passkeyRpId?: string): string;
|
|
83
85
|
/** The {@link authLoginEnvMarker} the login apps' env was last written with, once live on them. */
|
|
84
86
|
export declare const PLATFORM_AUTH_LOGIN_ENV_SCOPE_KEY = "auth_login_env_applied";
|
|
85
87
|
/**
|
|
@@ -1327,6 +1329,8 @@ export interface PlanPlatformOptions {
|
|
|
1327
1329
|
authMailKeyHeld?: boolean;
|
|
1328
1330
|
/** C3: why the owner's invite cannot be made (no work address, no mail port). The rest of the step still plans. */
|
|
1329
1331
|
authOwnerInvite?: string;
|
|
1332
|
+
/** The broker's registrar token was minted and never held: reported, nothing planned for it. */
|
|
1333
|
+
authRegistrar?: string;
|
|
1330
1334
|
files?: string;
|
|
1331
1335
|
integrations?: string;
|
|
1332
1336
|
ai?: string;
|
|
@@ -77,17 +77,19 @@ export const AUTH_TUNNEL_ROUTES_VERSION = '2';
|
|
|
77
77
|
export const PLATFORM_AUTH_TUNNEL_ROUTES_SCOPE_KEY = 'auth_tunnel_routes_applied';
|
|
78
78
|
/**
|
|
79
79
|
* The version of the env `provisionAuthStack` writes to the login apps (`LOGIN_MAIL_FROM`,
|
|
80
|
-
* `LETTERMINT_API_TOKEN`). Raise it when a key is added or its meaning changes, so an applied
|
|
80
|
+
* `LETTERMINT_API_TOKEN`, `LOGIN_PASSKEY_RP_ID`). Raise it when a key is added or its meaning changes, so an applied
|
|
81
81
|
* sign-in plans its step once more and the apps get the env. Never holds a value.
|
|
82
82
|
* 1: the login apps send their own invite and verification mail (auth 0.35.0, foundry 0.68.0).
|
|
83
|
+
* 2: a company's login apps get `LOGIN_PASSKEY_RP_ID` (foundry 0.70.2).
|
|
83
84
|
*/
|
|
84
|
-
export const AUTH_LOGIN_ENV_VERSION = '
|
|
85
|
+
export const AUTH_LOGIN_ENV_VERSION = '2';
|
|
85
86
|
/**
|
|
86
|
-
* What the login apps' env is written for: the version
|
|
87
|
-
*
|
|
87
|
+
* What the login apps' env is written for: the version, the sender address (`LOGIN_MAIL_FROM`) and the
|
|
88
|
+
* passkey RP ID (`LOGIN_PASSKEY_RP_ID`, none for the platform and a company without its own domain).
|
|
89
|
+
* Neither is a secret; a new mail domain or a changed RP ID gives a new marker, so the step is planned again.
|
|
88
90
|
*/
|
|
89
|
-
export function authLoginEnvMarker(smtpSender) {
|
|
90
|
-
return `${AUTH_LOGIN_ENV_VERSION}:${smtpSender}`;
|
|
91
|
+
export function authLoginEnvMarker(smtpSender, passkeyRpId) {
|
|
92
|
+
return `${AUTH_LOGIN_ENV_VERSION}:${smtpSender}${passkeyRpId ? `:${passkeyRpId}` : ''}`;
|
|
91
93
|
}
|
|
92
94
|
/** The {@link authLoginEnvMarker} the login apps' env was last written with, once live on them. */
|
|
93
95
|
export const PLATFORM_AUTH_LOGIN_ENV_SCOPE_KEY = 'auth_login_env_applied';
|
|
@@ -1553,7 +1555,7 @@ export function planPlatform(platform, live, options = {}) {
|
|
|
1553
1555
|
// reconcile below can be planned in the *same* round hc first comes up,
|
|
1554
1556
|
// the same way files/mail/archon are planned in auth's own first round.
|
|
1555
1557
|
const hcReady = hc.step !== null || Boolean(recordedIds[resourceKey('coolify', 'app', PLATFORM_HC_KEY)]);
|
|
1556
|
-
const auth = planAuth(platform, recordedIds, blocked.auth ?? dnsBlocked?.('sign-in'), blocked.authExecutorMissing ?? false, drifted.auth ?? false, blocked.authOtherInputsMissing ?? [], blocked.authOwnerInvite, drifted.authApps ?? false, blocked.authMailKeyHeld ?? true);
|
|
1558
|
+
const auth = planAuth(platform, recordedIds, blocked.auth ?? dnsBlocked?.('sign-in'), blocked.authExecutorMissing ?? false, drifted.auth ?? false, blocked.authOtherInputsMissing ?? [], blocked.authOwnerInvite, drifted.authApps ?? false, blocked.authMailKeyHeld ?? true, blocked.authRegistrar);
|
|
1557
1559
|
if (auth.step)
|
|
1558
1560
|
plannedSteps.push(auth.step);
|
|
1559
1561
|
// #174: the integrations host. Planned after auth; neither step reads the
|
|
@@ -2229,11 +2231,15 @@ function planSfu(platform, callsOrgSlugs, recordedIds) {
|
|
|
2229
2231
|
* promising "this apply re-mints" while one of those is still missing would
|
|
2230
2232
|
* be wrong the moment it is read.
|
|
2231
2233
|
*/
|
|
2232
|
-
function planAuth(platform, recordedIds, executorBlocked, executorMissing, authDrifted, otherInputsMissing = [], ownerInviteBlocked, appsDown = false, mailKeyHeld = true) {
|
|
2234
|
+
function planAuth(platform, recordedIds, executorBlocked, executorMissing, authDrifted, otherInputsMissing = [], ownerInviteBlocked, appsDown = false, mailKeyHeld = true, registrarBlocked) {
|
|
2233
2235
|
if (!platform.auth.enabled)
|
|
2234
2236
|
return { step: null, blocked: null };
|
|
2235
|
-
// C3/S2: standing notes. The invite and the domain wait on a person; they hold nothing else back.
|
|
2236
|
-
const standing = [
|
|
2237
|
+
// C3/S2: standing notes. The invite, the registrar token and the domain wait on a person; they hold nothing else back.
|
|
2238
|
+
const standing = [
|
|
2239
|
+
ownerInviteBlocked,
|
|
2240
|
+
registrarBlocked,
|
|
2241
|
+
ownerDomainBlockedText(recordedIds),
|
|
2242
|
+
].filter((t) => Boolean(t));
|
|
2237
2243
|
const standingNote = standing.length > 0 ? standing.join('; ') : null;
|
|
2238
2244
|
const key = resourceKey('coolify', 'app', PLATFORM_AUTH_KEY);
|
|
2239
2245
|
const connectorApplied = Boolean(recordedIds[PLATFORM_AUTH_CONNECTOR_APP_SCOPE_KEY]);
|
|
@@ -145,11 +145,16 @@ export interface PlatformAuthDeps {
|
|
|
145
145
|
* and the planner reports it. The port may be a proxy that answers any method name, so the host says it.
|
|
146
146
|
*/
|
|
147
147
|
signinMailWritable?: boolean;
|
|
148
|
+
/**
|
|
149
|
+
* The host's ZITADEL port has `ensureRegistrarUser` and `mintRegistrarToken` (the broker's registrar token,
|
|
150
|
+
* `plan/broker-registrar-token.ts`). Only `true` makes it; otherwise the planner reports it blocked.
|
|
151
|
+
*/
|
|
152
|
+
registrarTokenWritable?: boolean;
|
|
148
153
|
/** Overrides the Lettermint SMTP relay ZITADEL is given. Absent, Lettermint exactly as before. */
|
|
149
154
|
smtp?: PlatformAuthSmtp;
|
|
150
155
|
coolify: Pick<CoolifyPort, 'findProjectByName' | 'createProject' | 'findOrCreateEnvironment' | 'listServers' | 'findApplicationByName' | 'getApplication' | 'requireGithubApp' | 'createGithubAppApplication' | 'createDockerImageApplication' | 'createDockerfileApplication' | 'createPostgresDatabase' | 'getDatabase' | 'bulkUpsertEnvs' | 'listEnvs' | 'updateApplication' | 'deploy' | 'raw'> &
|
|
151
156
|
/** An own org's sign-in database needs a volume and the CA file mount (#746); the platform's does not. */
|
|
152
|
-
Partial<Pick<CoolifyPort, 'listStorages' | 'createPersistentStorage' | 'createFileStorage'>>;
|
|
157
|
+
Partial<Pick<CoolifyPort, 'listStorages' | 'createPersistentStorage' | 'createFileStorage' | 'deleteEnv'>>;
|
|
153
158
|
/** F4.dns: every DNS record call, through edge at its recorded address (see {@link PlatformDnsFactory}). */
|
|
154
159
|
dns: PlatformDnsFactory;
|
|
155
160
|
/** F4.tunnels: every tunnel call, through edge at its recorded address (see {@link PlatformTunnelsFactory}). */
|
|
@@ -161,7 +166,12 @@ export interface PlatformAuthDeps {
|
|
|
161
166
|
requestHeaders?: PlatformRequestHeadersFactory;
|
|
162
167
|
cloudflare: Pick<CloudflarePort, 'findZoneByApex'>;
|
|
163
168
|
/** Must target the platform's own new instance (its issuer), or `zitadelForInstance` must be set. */
|
|
164
|
-
zitadel: Pick<ZitadelPort, 'findOrCreateOrg' | 'mintAdminMachineUser' | 'mintLoginClientMachineUser' | 'listLoginClientPats' | 'reserveUserName' | 'closeSelfRegistration' | 'enforceSecondFactor' | 'inviteOrgOwner' | 'ensureDomainPolicy' | 'addOrgDomain' | 'removeOrgDomain' | 'setPrimaryOrgDomain' | 'setUserMetadata' | 'getUserMetadata'> & PlatformZitadelSmtp & Partial<PlatformZitadelMailBranding
|
|
169
|
+
zitadel: Pick<ZitadelPort, 'findOrCreateOrg' | 'mintAdminMachineUser' | 'mintLoginClientMachineUser' | 'listLoginClientPats' | 'reserveUserName' | 'closeSelfRegistration' | 'enforceSecondFactor' | 'inviteOrgOwner' | 'ensureDomainPolicy' | 'addOrgDomain' | 'removeOrgDomain' | 'setPrimaryOrgDomain' | 'setUserMetadata' | 'getUserMetadata'> & PlatformZitadelSmtp & Partial<Pick<ZitadelPort, 'ensureRegistrarUser' | 'mintRegistrarToken'>> & Partial<PlatformZitadelMailBranding> & {
|
|
170
|
+
getOrg?: (id: string) => Promise<{
|
|
171
|
+
id: string;
|
|
172
|
+
name: string;
|
|
173
|
+
}>;
|
|
174
|
+
};
|
|
165
175
|
/**
|
|
166
176
|
* Mails the owner's first-sign-in link to their personal address (the same
|
|
167
177
|
* port as `FoundryStepExecutorPorts.ownerInviteMail`). The link carries the
|
|
@@ -411,6 +421,7 @@ export declare class FoundryPlatformStepExecutor implements PlatformStepExecutor
|
|
|
411
421
|
authOtherInputsMissing?: string[];
|
|
412
422
|
authMailKeyHeld?: boolean;
|
|
413
423
|
authOwnerInvite?: string;
|
|
424
|
+
authRegistrar?: string;
|
|
414
425
|
files?: string;
|
|
415
426
|
integrations?: string;
|
|
416
427
|
ai?: string;
|
|
@@ -575,6 +586,7 @@ export declare const AUTH_BROKER_MCP_PATH_PATTERN = "^/(\\.well-known/oauth-auth
|
|
|
575
586
|
export declare const PLATFORM_AUTH_DB_SCOPE_KEY = "coolify_platform_auth_database";
|
|
576
587
|
export declare const PLATFORM_AUTH_ZITADEL_SCOPE_KEY = "coolify_platform_auth_zitadel_app";
|
|
577
588
|
export declare const PLATFORM_AUTH_LOGIN_SCOPE_KEY = "coolify_platform_auth_login_app";
|
|
589
|
+
export declare const PLATFORM_AUTH_ORG_NAME = "foundry";
|
|
578
590
|
/**
|
|
579
591
|
* #283: the ref the new instance's IAM_OWNER machine token (a PAT) is
|
|
580
592
|
* delivered under, once. A host reads it back to build `zitadelForInstance`
|
|
@@ -600,6 +612,11 @@ export interface AuthStackInput {
|
|
|
600
612
|
network: ServiceNetwork;
|
|
601
613
|
/** The ZITADEL SMTP sender address. */
|
|
602
614
|
smtpSender: string;
|
|
615
|
+
/**
|
|
616
|
+
* `LOGIN_PASSKEY_RP_ID` for both login apps: a bare domain every host they serve sits under (the login
|
|
617
|
+
* refuses any other). Absent: unset, and each passkey belongs to the host it was made on.
|
|
618
|
+
*/
|
|
619
|
+
passkeyRpId?: string;
|
|
603
620
|
/** Where the login app sends a user with no sign-in request in flight, or null to leave it unset. */
|
|
604
621
|
defaultRedirectUri: string | null;
|
|
605
622
|
/** The company whose owner is invited into its own sign-in org; null for a company's own stack. */
|
|
@@ -620,6 +637,11 @@ export interface AuthStackInput {
|
|
|
620
637
|
mailBranding?: {
|
|
621
638
|
companyName: string;
|
|
622
639
|
};
|
|
640
|
+
/**
|
|
641
|
+
* The broker gets a registrar token (`ZITADEL_REGISTRAR_TOKEN`, `plan/broker-registrar-token.ts`) covering the
|
|
642
|
+
* instance's first organisation and these (the company's sign-in and customers organisations). Absent: none.
|
|
643
|
+
*/
|
|
644
|
+
registrarOrgIds?: readonly string[];
|
|
623
645
|
}
|
|
624
646
|
/**
|
|
625
647
|
* #168: makes `coolify:app` keyed {@link PLATFORM_AUTH_KEY} true -- a Coolify
|