@wtfalch/foundry 0.32.0 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bootstrap/bootstrap.d.ts +18 -1
- package/dist/bootstrap/bootstrap.js +167 -28
- package/dist/bootstrap/declare.d.ts +5 -1
- package/dist/bootstrap/declare.js +3 -1
- package/dist/bootstrap/index.d.ts +1 -1
- package/dist/bootstrap/versions.d.ts +4 -0
- package/dist/calls-sfu.d.ts +10 -0
- package/dist/calls-sfu.js +41 -0
- package/dist/index.d.ts +18 -8
- package/dist/index.js +8 -3
- package/dist/migrations/0051_org_services.sql +13 -0
- package/dist/migrations/0052_platform_mail_relay.sql +8 -0
- package/dist/org.d.ts +24 -2
- package/dist/org.js +42 -0
- package/dist/plan/boule.d.ts +24 -1
- package/dist/plan/boule.js +29 -1
- package/dist/plan/calls-matrix.d.ts +9 -0
- package/dist/plan/calls-matrix.js +17 -0
- package/dist/plan/cloudflared-connector.d.ts +7 -1
- package/dist/plan/cloudflared-connector.js +14 -1
- package/dist/plan/diff.d.ts +32 -1
- package/dist/plan/diff.js +383 -26
- package/dist/plan/executor.d.ts +177 -7
- package/dist/plan/executor.js +738 -51
- package/dist/plan/health-check-settings.d.ts +28 -0
- package/dist/plan/health-check-settings.js +36 -0
- package/dist/plan/health-gaps.d.ts +33 -0
- package/dist/plan/health-gaps.js +89 -0
- package/dist/plan/health-targets.js +1 -1
- package/dist/plan/held-key-refs.d.ts +10 -0
- package/dist/plan/held-key-refs.js +10 -0
- package/dist/plan/index.d.ts +16 -6
- package/dist/plan/index.js +8 -3
- package/dist/plan/live.d.ts +5 -2
- package/dist/plan/live.js +15 -2
- package/dist/plan/locked-hostnames.d.ts +5 -2
- package/dist/plan/locked-hostnames.js +9 -1
- package/dist/plan/login-host.d.ts +62 -2
- package/dist/plan/login-host.js +104 -1
- package/dist/plan/mail-relay.d.ts +120 -0
- package/dist/plan/mail-relay.js +323 -0
- package/dist/plan/org-names.d.ts +24 -0
- package/dist/plan/org-names.js +52 -0
- package/dist/plan/own-diff.d.ts +169 -0
- package/dist/plan/own-diff.js +432 -0
- package/dist/plan/own-dns.d.ts +6 -0
- package/dist/plan/own-dns.js +15 -0
- package/dist/plan/own-limits.d.ts +22 -0
- package/dist/plan/own-limits.js +101 -0
- package/dist/plan/own-services.d.ts +193 -0
- package/dist/plan/own-services.js +167 -0
- package/dist/plan/own-sfu.d.ts +69 -0
- package/dist/plan/own-sfu.js +255 -0
- package/dist/plan/plan-apply.d.ts +101 -7
- package/dist/plan/plan-apply.js +216 -12
- package/dist/plan/platform-diff.d.ts +61 -12
- package/dist/plan/platform-diff.js +140 -34
- package/dist/plan/platform-org.d.ts +36 -0
- package/dist/plan/platform-org.js +96 -2
- package/dist/plan/platform-providers.d.ts +407 -12
- package/dist/plan/platform-providers.js +947 -422
- package/dist/plan/sign-in-org.d.ts +99 -0
- package/dist/plan/sign-in-org.js +151 -0
- package/dist/plan/zitadel-login-policy.js +1 -1
- package/dist/plan/zitadel-system-api.d.ts +22 -1
- package/dist/plan/zitadel-system-api.js +49 -8
- package/dist/plan.d.ts +18 -0
- package/dist/platform.d.ts +8 -0
- package/dist/slug.js +1 -0
- package/dist/store/postgres.js +51 -2
- package/dist/store/schema.d.ts +51 -0
- package/dist/store/schema.js +5 -0
- package/package.json +1 -1
- package/versions.json +3 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Plan } from '../plan.js';
|
|
2
|
-
import type { PlatformLiveReaders, PlatformStepExecutor, StepSystem } from '../plan/index.js';
|
|
2
|
+
import type { FoundryPlanApplyOptions, LiveReaders, PlatformLiveReaders, PlatformStepExecutor, StepExecutor, StepSystem } from '../plan/index.js';
|
|
3
3
|
import type { FoundryPlatformStepExecutorOptions } from '../plan/platform-providers.js';
|
|
4
4
|
import type { FoundryStoreDb } from '../store/postgres.js';
|
|
5
5
|
import type { BootstrapCompany, BootstrapOwner } from './declare.js';
|
|
@@ -38,6 +38,17 @@ export interface BootstrapProgress {
|
|
|
38
38
|
}
|
|
39
39
|
/** Everything foundry's per-step executors take besides the two clients and `deliverSecret`. */
|
|
40
40
|
export type BootstrapExecutorDeps = Omit<FoundryPlatformStepExecutorOptions, 'coolify' | 'deliverSecret'>;
|
|
41
|
+
/**
|
|
42
|
+
* What phase two needs to apply the platform's own org (Company Included):
|
|
43
|
+
* the org-side readers and step executor a host already builds for any org.
|
|
44
|
+
* Without it the run applies the platform only.
|
|
45
|
+
*/
|
|
46
|
+
export interface BootstrapCompanyDeps {
|
|
47
|
+
readers: LiveReaders;
|
|
48
|
+
executor: StepExecutor;
|
|
49
|
+
/** The same flags `FoundryPlanApply` takes: a step whose port the host did not build is reported blocked. */
|
|
50
|
+
options?: Pick<FoundryPlanApplyOptions, 'ownerInviteMail' | 'broker'>;
|
|
51
|
+
}
|
|
41
52
|
export interface BootstrapPlatformDeps {
|
|
42
53
|
/** Foundry's tables live here, already migrated by the host; the store, the audit log and the plan all use it. */
|
|
43
54
|
db: FoundryStoreDb;
|
|
@@ -54,6 +65,12 @@ export interface BootstrapPlatformDeps {
|
|
|
54
65
|
};
|
|
55
66
|
/** Per-step dependencies (auth, mail, files, ai, integrations, archon, sfu). A step without its own is reported blocked. */
|
|
56
67
|
executor?: BootstrapExecutorDeps;
|
|
68
|
+
/**
|
|
69
|
+
* Phase two: after the platform, apply the platform's own org's database,
|
|
70
|
+
* sign-in client, owner and Boule, then the platform once more so Archon
|
|
71
|
+
* picks up the keys that slice produced. Absent: platform only.
|
|
72
|
+
*/
|
|
73
|
+
company?: BootstrapCompanyDeps;
|
|
57
74
|
/**
|
|
58
75
|
* Passed to `createPostgresAuditLog`. Omit it in a real deployment: `db` is
|
|
59
76
|
* the runtime role the host made with `ensureRuntimeRole`, and the default
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { CloudflareClient } from '@wtfalch/providers/cloudflare';
|
|
2
2
|
import { CoolifyClient } from '@wtfalch/providers/coolify';
|
|
3
|
+
import { validateMail } from '../org.js';
|
|
4
|
+
import { BOULE_BROKER_ENTRY_KEY } from '../plan/boule.js';
|
|
5
|
+
import { brokerEntryAppliedScopeKey } from '../plan/broker-entries.js';
|
|
3
6
|
import { FoundryPlanApply, FoundryPlatformStepExecutor, ProvidersPlatformCloudflareReader, ProvidersPlatformCoolifyReader, } from '../plan/index.js';
|
|
4
7
|
import { createPostgresAuditLog } from '../store/audit.js';
|
|
5
8
|
import { createPostgresDeclaredOrgStore, createPostgresPlatformStore } from '../store/postgres.js';
|
|
6
|
-
import { COMPANY_INCLUDED_ORG_ID, declaredCompanyOrg, declaredPlatformFor
|
|
9
|
+
import { COMPANY_INCLUDED_ORG_ID, declaredCompanyOrg, declaredPlatformFor } from './declare.js';
|
|
7
10
|
import { driveApply } from './drive.js';
|
|
8
11
|
const REDACTED = '<redacted>';
|
|
9
12
|
/** Shorter strings in `deps.executor` are settings ("main", ports), not secrets, and would mangle every message. */
|
|
@@ -41,17 +44,47 @@ export class Redactor {
|
|
|
41
44
|
return out;
|
|
42
45
|
}
|
|
43
46
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
47
|
+
/** The platform, and the platform's own org when the host gave phase two its dependencies. */
|
|
48
|
+
function allowBootstrapApply(withCompany) {
|
|
49
|
+
return {
|
|
50
|
+
async canApply(_actor, orgId) {
|
|
51
|
+
return withCompany && orgId === COMPANY_INCLUDED_ORG_ID;
|
|
52
|
+
},
|
|
53
|
+
async canDestroy() {
|
|
54
|
+
return false;
|
|
55
|
+
},
|
|
56
|
+
async canApplyPlatform() {
|
|
57
|
+
return true;
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The org steps phase two applies: the sign-in org and its domain, the owner
|
|
63
|
+
* (whose user id Boule's `AUTHZ_FIRST_OWNER` carries), Boule's sign-in client,
|
|
64
|
+
* and Boule with its database. Everything else the company declares (chat,
|
|
65
|
+
* mail, Boule's broker entry, which waits for the org's mail domain, ...) is
|
|
66
|
+
* for a later, ordinary org apply.
|
|
67
|
+
*/
|
|
68
|
+
const COMPANY_SLICE_STEPS = new Set([
|
|
69
|
+
'zitadel:issuer',
|
|
70
|
+
'zitadel:registration-closed',
|
|
71
|
+
'zitadel:domain',
|
|
72
|
+
'zitadel:owner',
|
|
73
|
+
'zitadel:app:boule',
|
|
74
|
+
'coolify:boule',
|
|
75
|
+
]);
|
|
76
|
+
const companySliceOnly = {
|
|
77
|
+
step: (action) => COMPANY_SLICE_STEPS.has(`${action.system}:${action.kind}`) ||
|
|
78
|
+
COMPANY_SLICE_STEPS.has(`${action.system}:${action.kind}:${action.key}`),
|
|
79
|
+
// `coolify:boule:mail` only says Boule runs without mail for now.
|
|
80
|
+
blocked: (key) => key !== 'coolify:boule:mail' &&
|
|
81
|
+
[...COMPANY_SLICE_STEPS].some((step) => key === step || key.startsWith(`${step}:`)),
|
|
54
82
|
};
|
|
83
|
+
/** Shown instead of a phase-two error that could carry a value; the failing step's own name and redacted text are in `failed`. */
|
|
84
|
+
const COMPANY_NOT_FINISHED = "the company's own slice did not finish, so Archon has no people-store keys yet; run onboarding again";
|
|
85
|
+
/** The step key and fixed text of the blocked item for Boule's missing broker entry. */
|
|
86
|
+
const BOULE_BROKER_ENTRY_BLOCKED_STEP = 'coolify:boule-broker-entry';
|
|
87
|
+
const BOULE_SIGN_IN_BLOCKED = "Boule is deployed but nobody can sign in to it yet: its broker entry waits for the company's mail domain, which the company's mail step records. Apply the company's mail, then its Boule broker entry (an ordinary org apply)";
|
|
55
88
|
/** `FoundryPlanApply` needs org-scoped parts this run never uses; each refuses if called. */
|
|
56
89
|
const unusedOrgExecutor = {
|
|
57
90
|
async execute() {
|
|
@@ -68,6 +101,25 @@ export function roundSystem(plan) {
|
|
|
68
101
|
return systems.size === 1 && only ? only : 'foundry';
|
|
69
102
|
}
|
|
70
103
|
const unusedOrgReader = { listResources: async () => [] };
|
|
104
|
+
/** Counts the org steps a round starts and ends, as the platform executor is wrapped below. */
|
|
105
|
+
function trackedOrgExecutor(inner, progress, redactor) {
|
|
106
|
+
return {
|
|
107
|
+
execute: async (action, context) => {
|
|
108
|
+
const p = progress();
|
|
109
|
+
p.started++;
|
|
110
|
+
try {
|
|
111
|
+
const result = await inner.execute(action, context);
|
|
112
|
+
p.completed++;
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
finally {
|
|
116
|
+
// After a failure too: the secrets it minted before failing must be masked in its error.
|
|
117
|
+
redactor.add(...(inner.secretsHandled?.() ?? []));
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
...(inner.ownEdgePorts ? { ownEdgePorts: () => inner.ownEdgePorts?.() ?? {} } : {}),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
71
123
|
/** The audit log with every free-text field run through the redactor before it is stored. */
|
|
72
124
|
function redactingAudit(inner, redactor) {
|
|
73
125
|
const plan = (p) => ({
|
|
@@ -115,6 +167,16 @@ export function guardStepDeps(executor, redactor, deliverSecret, heldKeys) {
|
|
|
115
167
|
const copy = { ...deps };
|
|
116
168
|
if ('deliverSecret' in copy)
|
|
117
169
|
copy.deliverSecret = deliverSecret;
|
|
170
|
+
const inviteMail = copy.ownerInviteMail;
|
|
171
|
+
if (inviteMail) {
|
|
172
|
+
// The link carries the invite code; both are masked from the first moment they exist.
|
|
173
|
+
copy.ownerInviteMail = {
|
|
174
|
+
send: (to, link) => {
|
|
175
|
+
redactor.add(link, new URL(link).searchParams.get('code'));
|
|
176
|
+
return inviteMail.send(to, link);
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
}
|
|
118
180
|
// #201: ai's own default minter needs heldKeys too, to re-sign a mint
|
|
119
181
|
// request against its signing key when an earlier run pinned it but
|
|
120
182
|
// crashed before the management key itself was minted and delivered.
|
|
@@ -170,8 +232,14 @@ export async function bootstrapPlatform(input, deps) {
|
|
|
170
232
|
});
|
|
171
233
|
};
|
|
172
234
|
try {
|
|
173
|
-
|
|
235
|
+
// Refused here, before a credential is read or a row written: the work and
|
|
236
|
+
// personal addresses must be valid and not clash on the company's domain.
|
|
237
|
+
const mailProblem = validateMail(declaredCompanyOrg(input.company, input.owner));
|
|
238
|
+
if (mailProblem)
|
|
239
|
+
throw new Error(`bootstrapPlatform: ${mailProblem}`);
|
|
174
240
|
redactor.addDeep(deps.executor);
|
|
241
|
+
// The company executor's own tokens (its ports hold the Coolify and ZITADEL credentials).
|
|
242
|
+
redactor.addDeep(deps.company?.executor);
|
|
175
243
|
const credentials = await deps.rootCredentials();
|
|
176
244
|
redactor.add(credentials.coolifyToken, credentials.cloudflareApiToken, credentials.cloudflareAccountId, ...Object.values(credentials.accessHeaders ?? {}));
|
|
177
245
|
const deliverSecret = async (ref, secret) => {
|
|
@@ -272,9 +340,11 @@ export async function bootstrapPlatform(input, deps) {
|
|
|
272
340
|
? { driftedSteps: (platform, ids) => inner.driftedSteps?.(platform, ids) ?? {} }
|
|
273
341
|
: {}),
|
|
274
342
|
};
|
|
343
|
+
let orgProgress = { started: 0, completed: 0 };
|
|
344
|
+
const companyDeps = deps.company;
|
|
275
345
|
const planApply = new FoundryPlanApply({
|
|
276
346
|
store: orgStore,
|
|
277
|
-
readers: {
|
|
347
|
+
readers: companyDeps?.readers ?? {
|
|
278
348
|
zitadel: unusedOrgReader,
|
|
279
349
|
coolify: unusedOrgReader,
|
|
280
350
|
cloudflare: unusedOrgReader,
|
|
@@ -282,14 +352,17 @@ export async function bootstrapPlatform(input, deps) {
|
|
|
282
352
|
ai: unusedOrgReader,
|
|
283
353
|
cms: unusedOrgReader,
|
|
284
354
|
},
|
|
285
|
-
executor:
|
|
355
|
+
executor: companyDeps
|
|
356
|
+
? trackedOrgExecutor(companyDeps.executor, () => orgProgress, redactor)
|
|
357
|
+
: unusedOrgExecutor,
|
|
286
358
|
destroyer: unusedOrgExecutor,
|
|
287
359
|
audit: redactingAudit(createPostgresAuditLog(deps.db, { checkRuntimeRole: deps.checkRuntimeRole }), redactor),
|
|
288
|
-
authz:
|
|
360
|
+
authz: allowBootstrapApply(Boolean(companyDeps)),
|
|
289
361
|
actor: { id: 'bootstrap-platform', display: 'bootstrapPlatform' },
|
|
290
362
|
platform: { store: platformStore, readers: runtime.readers, executor },
|
|
363
|
+
...(companyDeps ? { ...companyDeps.options, onlySteps: companySliceOnly } : {}),
|
|
291
364
|
});
|
|
292
|
-
const
|
|
365
|
+
const drivePlatform = (label) => driveApply({
|
|
293
366
|
plan: () => planApply.planPlatform(),
|
|
294
367
|
apply: (plan, stepProgress) => {
|
|
295
368
|
progress = stepProgress;
|
|
@@ -300,32 +373,98 @@ export async function bootstrapPlatform(input, deps) {
|
|
|
300
373
|
phase: 'plan',
|
|
301
374
|
system: roundSystem(plan),
|
|
302
375
|
round,
|
|
303
|
-
message:
|
|
376
|
+
message: `${label}round ${round}: ${plan.steps.length} step(s) planned`,
|
|
304
377
|
steps: plan.steps.map((s) => s.description),
|
|
305
378
|
}),
|
|
306
379
|
onApplied: (round, plan) => emit({
|
|
307
380
|
phase: 'apply',
|
|
308
381
|
system: roundSystem(plan),
|
|
309
382
|
round,
|
|
310
|
-
message:
|
|
383
|
+
message: `${label}round ${round}: applied`,
|
|
311
384
|
steps: plan.steps.map((s) => s.description),
|
|
312
385
|
}),
|
|
313
386
|
});
|
|
314
|
-
|
|
387
|
+
// Phase one: the platform.
|
|
388
|
+
const driven = await drivePlatform('');
|
|
389
|
+
const applied = [...driven.applied];
|
|
390
|
+
let failed = driven.failed;
|
|
391
|
+
let lastPlan = driven.lastPlan;
|
|
392
|
+
const blockedRaw = (driven.failed ? [] : (driven.lastPlan?.unmanaged ?? []))
|
|
315
393
|
.filter((item) => item.key !== undefined)
|
|
316
|
-
.map((item) => ({
|
|
317
|
-
|
|
318
|
-
|
|
394
|
+
.map((item) => ({ step: item.key, message: item.description }));
|
|
395
|
+
if (companyDeps && !failed) {
|
|
396
|
+
// Phase two: the platform's own org. Its Boule needs the platform's
|
|
397
|
+
// sign-in, and Archon's people-store keys need that Boule.
|
|
398
|
+
const company = await driveApply({
|
|
399
|
+
plan: () => planApply.plan(COMPANY_INCLUDED_ORG_ID),
|
|
400
|
+
apply: (plan, stepProgress) => {
|
|
401
|
+
orgProgress = stepProgress;
|
|
402
|
+
return planApply.apply(plan, '');
|
|
403
|
+
},
|
|
404
|
+
maxRounds: deps.maxRounds,
|
|
405
|
+
onPlan: (round, plan) => emit({
|
|
406
|
+
phase: 'plan',
|
|
407
|
+
system: roundSystem(plan),
|
|
408
|
+
round,
|
|
409
|
+
message: `company round ${round}: ${plan.steps.length} step(s) planned`,
|
|
410
|
+
steps: plan.steps.map((s) => s.description),
|
|
411
|
+
}),
|
|
412
|
+
onApplied: (round, plan) => emit({
|
|
413
|
+
phase: 'apply',
|
|
414
|
+
system: roundSystem(plan),
|
|
415
|
+
round,
|
|
416
|
+
message: `company round ${round}: applied`,
|
|
417
|
+
steps: plan.steps.map((s) => s.description),
|
|
418
|
+
}),
|
|
419
|
+
});
|
|
420
|
+
applied.push(...company.applied);
|
|
421
|
+
if (company.failed) {
|
|
422
|
+
failed = company.failed;
|
|
423
|
+
blockedRaw.push({ step: 'company', message: COMPANY_NOT_FINISHED });
|
|
424
|
+
}
|
|
425
|
+
else {
|
|
426
|
+
for (const item of company.lastPlan?.unmanaged ?? []) {
|
|
427
|
+
if (item.key !== undefined)
|
|
428
|
+
blockedRaw.push({ step: item.key, message: item.description });
|
|
429
|
+
}
|
|
430
|
+
// Boule's broker entry needs the org's mail domain, which only the org's mail step
|
|
431
|
+
// records, so it is outside this slice. Until it is applied nobody can sign in to Boule:
|
|
432
|
+
// say so, and never report the onboarding complete.
|
|
433
|
+
const companyApplied = await orgStore.getAppliedState(COMPANY_INCLUDED_ORG_ID);
|
|
434
|
+
if (!companyApplied?.serviceIds[brokerEntryAppliedScopeKey(BOULE_BROKER_ENTRY_KEY)]) {
|
|
435
|
+
blockedRaw.push({
|
|
436
|
+
step: BOULE_BROKER_ENTRY_BLOCKED_STEP,
|
|
437
|
+
message: BOULE_SIGN_IN_BLOCKED,
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
// Phase three: the same platform plan again. Archon's step now sees
|
|
441
|
+
// the keys Boule holds and writes its own copies.
|
|
442
|
+
const again = await drivePlatform('after the company: ');
|
|
443
|
+
applied.push(...again.applied);
|
|
444
|
+
failed = again.failed;
|
|
445
|
+
lastPlan = again.lastPlan;
|
|
446
|
+
if (!again.failed) {
|
|
447
|
+
for (const item of again.lastPlan?.unmanaged ?? []) {
|
|
448
|
+
if (item.key !== undefined && !blockedRaw.some((b) => b.step === item.key)) {
|
|
449
|
+
blockedRaw.push({ step: item.key, message: item.description });
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
const blocked = blockedRaw.map((item) => ({
|
|
456
|
+
step: item.step,
|
|
457
|
+
message: redactor.text(item.message),
|
|
319
458
|
}));
|
|
320
459
|
const result = {
|
|
321
|
-
applied:
|
|
460
|
+
applied: applied.map((s) => redactor.text(s)),
|
|
322
461
|
blocked,
|
|
323
|
-
complete: !
|
|
324
|
-
...(
|
|
462
|
+
complete: !failed && blocked.length === 0 && lastPlan?.steps.length === 0,
|
|
463
|
+
...(failed
|
|
325
464
|
? {
|
|
326
465
|
failed: {
|
|
327
|
-
step: redactor.text(
|
|
328
|
-
message: redactor.text(
|
|
466
|
+
step: redactor.text(failed.step),
|
|
467
|
+
message: redactor.text(failed.message),
|
|
329
468
|
},
|
|
330
469
|
}
|
|
331
470
|
: {}),
|
|
@@ -333,7 +472,7 @@ export async function bootstrapPlatform(input, deps) {
|
|
|
333
472
|
emit({
|
|
334
473
|
phase: 'done',
|
|
335
474
|
system: 'foundry',
|
|
336
|
-
message:
|
|
475
|
+
message: failed
|
|
337
476
|
? `stopped: ${result.failed?.message}`
|
|
338
477
|
: `${result.applied.length} step(s) applied, ${blocked.length} blocked`,
|
|
339
478
|
});
|
|
@@ -42,11 +42,15 @@ export interface BootstrapCompany {
|
|
|
42
42
|
export interface BootstrapOwner {
|
|
43
43
|
givenName: string;
|
|
44
44
|
familyName: string;
|
|
45
|
+
/** The owner's personal address: the first-sign-in link is mailed here. */
|
|
45
46
|
email: string;
|
|
47
|
+
/** The owner's work address on the company's domain; their sign-in name is its local part. */
|
|
48
|
+
mailboxAddress: string;
|
|
46
49
|
}
|
|
47
50
|
/**
|
|
48
51
|
* The owner as `OwnerIdentity`, trimmed. Throws a plain error naming the
|
|
49
|
-
* field when one is empty or
|
|
52
|
+
* field when one is empty or an address is not an address. The work address
|
|
53
|
+
* is required: the owner signs in as its local part.
|
|
50
54
|
*/
|
|
51
55
|
export declare function ownerIdentity(owner: BootstrapOwner): OwnerIdentity;
|
|
52
56
|
/**
|
|
@@ -59,13 +59,15 @@ const SERVICES_ENABLED_BY_DEFAULT = new Set([
|
|
|
59
59
|
]);
|
|
60
60
|
/**
|
|
61
61
|
* The owner as `OwnerIdentity`, trimmed. Throws a plain error naming the
|
|
62
|
-
* field when one is empty or
|
|
62
|
+
* field when one is empty or an address is not an address. The work address
|
|
63
|
+
* is required: the owner signs in as its local part.
|
|
63
64
|
*/
|
|
64
65
|
export function ownerIdentity(owner) {
|
|
65
66
|
const identity = {
|
|
66
67
|
email: owner.email?.trim() ?? '',
|
|
67
68
|
givenName: owner.givenName?.trim() ?? '',
|
|
68
69
|
familyName: owner.familyName?.trim() ?? '',
|
|
70
|
+
mailboxAddress: owner.mailboxAddress?.trim() ?? '',
|
|
69
71
|
};
|
|
70
72
|
const problem = validateOwners([identity]);
|
|
71
73
|
if (problem)
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export type { BootstrapCompany, BootstrapOwner, } from './declare.js';
|
|
2
|
-
export type { BootstrapExecutorDeps, BootstrapPlatformDeps, BootstrapPlatformInput, BootstrapProgress, BootstrapResult, RootCredentials, } from './bootstrap.js';
|
|
2
|
+
export type { BootstrapCompanyDeps, BootstrapExecutorDeps, BootstrapPlatformDeps, BootstrapPlatformInput, BootstrapProgress, BootstrapResult, RootCredentials, } from './bootstrap.js';
|
|
3
3
|
export { bootstrapPlatform } from './bootstrap.js';
|
|
@@ -13,6 +13,10 @@ export interface PinnedVersions {
|
|
|
13
13
|
cloudflared: string;
|
|
14
14
|
stalwart: string;
|
|
15
15
|
hc: string;
|
|
16
|
+
/** The mail relay's base image (an exact Alpine release); Postfix comes from its package repository. */
|
|
17
|
+
alpine: string;
|
|
18
|
+
/** A static busybox, copied into the MAS image so its health check has an HTTP client. */
|
|
19
|
+
busybox: string;
|
|
16
20
|
}
|
|
17
21
|
export declare function loadVersions(): PinnedVersions;
|
|
18
22
|
export declare const VERSIONS: PinnedVersions;
|
package/dist/calls-sfu.d.ts
CHANGED
|
@@ -129,6 +129,16 @@ export declare const LIVEKIT_SIGNALLING_PORT = 7880;
|
|
|
129
129
|
export declare const LIVEKIT_MEDIA_TCP_PORT = 7881;
|
|
130
130
|
/** RTP media. Matches #122's "Decided" section exactly; not the wider range `docs/research/2026-09-19-meetings-research.md` lists for embedded TURN, which #122 does not turn on. */
|
|
131
131
|
export declare const LIVEKIT_MEDIA_UDP_RANGE = "50000:60000";
|
|
132
|
+
/**
|
|
133
|
+
* The ports of one slice of the Coolify server's media ports (`services.sfuSlot`):
|
|
134
|
+
* slot n holds UDP `50000 + (n-1)*500` to `50000 + n*500 - 1` and TCP `7881 + n`.
|
|
135
|
+
* Throws for a slot outside 1..{@link SFU_SLOT_MAX}.
|
|
136
|
+
*/
|
|
137
|
+
export declare function sfuSlotPorts(slot: number): {
|
|
138
|
+
udpStart: number;
|
|
139
|
+
udpEnd: number;
|
|
140
|
+
tcp: number;
|
|
141
|
+
};
|
|
132
142
|
/**
|
|
133
143
|
* The host's private network on `vmbr1`, where the calls service reaches
|
|
134
144
|
* LiveKit's API directly (#127's "Human steps": VM 105 gets a second NIC on
|
package/dist/calls-sfu.js
CHANGED
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
* from any error `calls-sfu-deploy.mjs` prints.
|
|
54
54
|
*/
|
|
55
55
|
import { VERSIONS } from './bootstrap/versions.js';
|
|
56
|
+
import { SFU_SLOT_MAX } from './org.js';
|
|
56
57
|
// ---------------------------------------------------------------------------
|
|
57
58
|
// The guard
|
|
58
59
|
// ---------------------------------------------------------------------------
|
|
@@ -119,6 +120,27 @@ export const LIVEKIT_SIGNALLING_PORT = 7880;
|
|
|
119
120
|
export const LIVEKIT_MEDIA_TCP_PORT = 7881;
|
|
120
121
|
/** RTP media. Matches #122's "Decided" section exactly; not the wider range `docs/research/2026-09-19-meetings-research.md` lists for embedded TURN, which #122 does not turn on. */
|
|
121
122
|
export const LIVEKIT_MEDIA_UDP_RANGE = '50000:60000';
|
|
123
|
+
/** The first UDP port of slot 1 and the width of every slot's UDP range (contract section 1). */
|
|
124
|
+
const SFU_SLOT_UDP_BASE = 50000;
|
|
125
|
+
const SFU_SLOT_UDP_WIDTH = 500;
|
|
126
|
+
/** Slot n's TCP fallback port is this plus n. */
|
|
127
|
+
const SFU_SLOT_TCP_BASE = 7881;
|
|
128
|
+
/**
|
|
129
|
+
* The ports of one slice of the Coolify server's media ports (`services.sfuSlot`):
|
|
130
|
+
* slot n holds UDP `50000 + (n-1)*500` to `50000 + n*500 - 1` and TCP `7881 + n`.
|
|
131
|
+
* Throws for a slot outside 1..{@link SFU_SLOT_MAX}.
|
|
132
|
+
*/
|
|
133
|
+
export function sfuSlotPorts(slot) {
|
|
134
|
+
if (!Number.isInteger(slot) || slot < 1 || slot > SFU_SLOT_MAX) {
|
|
135
|
+
throw new Error(`sfu slot must be an integer from 1 to ${SFU_SLOT_MAX}`);
|
|
136
|
+
}
|
|
137
|
+
const udpStart = SFU_SLOT_UDP_BASE + (slot - 1) * SFU_SLOT_UDP_WIDTH;
|
|
138
|
+
return {
|
|
139
|
+
udpStart,
|
|
140
|
+
udpEnd: udpStart + SFU_SLOT_UDP_WIDTH - 1,
|
|
141
|
+
tcp: SFU_SLOT_TCP_BASE + slot,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
122
144
|
/**
|
|
123
145
|
* The host's private network on `vmbr1`, where the calls service reaches
|
|
124
146
|
* LiveKit's API directly (#127's "Human steps": VM 105 gets a second NIC on
|
|
@@ -176,6 +198,11 @@ const UNSCOPED_SIGNALLING_RULE_PATTERN = new RegExp(`^${LIVEKIT_SIGNALLING_PORT}
|
|
|
176
198
|
export function hasUnscopedSignallingRule(status) {
|
|
177
199
|
return UNSCOPED_SIGNALLING_RULE_PATTERN.test(status);
|
|
178
200
|
}
|
|
201
|
+
/**
|
|
202
|
+
* Where `cloudflared` serves its metrics, which include `/ready`. The image has no shell, so its check is
|
|
203
|
+
* the binary's own `tunnel ready` in exec form, and that command asks this address.
|
|
204
|
+
*/
|
|
205
|
+
const CLOUDFLARED_METRICS_ADDRESS = '127.0.0.1:20241';
|
|
179
206
|
/**
|
|
180
207
|
* `network_mode: host`: LiveKit's own self-hosting guide recommends this
|
|
181
208
|
* over mapping ten thousand individual UDP ports, and the SFU VM has no
|
|
@@ -193,13 +220,27 @@ export function dockerComposeYaml() {
|
|
|
193
220
|
volumes:
|
|
194
221
|
- ${LIVEKIT_CONFIG_PATH}:/etc/livekit.yaml:ro
|
|
195
222
|
command: --config /etc/livekit.yaml
|
|
223
|
+
healthcheck:
|
|
224
|
+
test: ["CMD-SHELL", "wget -q -O /dev/null http://127.0.0.1:${LIVEKIT_SIGNALLING_PORT}/"]
|
|
225
|
+
interval: 30s
|
|
226
|
+
timeout: 5s
|
|
227
|
+
retries: 3
|
|
228
|
+
start_period: 30s
|
|
196
229
|
cloudflared:
|
|
197
230
|
image: ${CLOUDFLARED_IMAGE_REF}
|
|
198
231
|
restart: unless-stopped
|
|
199
232
|
network_mode: host
|
|
200
233
|
env_file:
|
|
201
234
|
- ${CLOUDFLARED_ENV_PATH}
|
|
235
|
+
environment:
|
|
236
|
+
TUNNEL_METRICS: ${CLOUDFLARED_METRICS_ADDRESS}
|
|
202
237
|
command: tunnel --no-autoupdate run
|
|
238
|
+
healthcheck:
|
|
239
|
+
test: ["CMD", "cloudflared", "tunnel", "--metrics", "${CLOUDFLARED_METRICS_ADDRESS}", "ready"]
|
|
240
|
+
interval: 30s
|
|
241
|
+
timeout: 5s
|
|
242
|
+
retries: 3
|
|
243
|
+
start_period: 30s
|
|
203
244
|
`;
|
|
204
245
|
}
|
|
205
246
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export type { OrgId, MailMode, MailConfig, ChatConfig, CallsConfig, IssuerConfig, SiteConfig, IssuerAppKind, IssuerApp, HostingConfig, HostingEntry, EntryGate, HostingEntryUses, EntryAuth, EntryFiles, EntryAi, EnvValue, SsoConfig, GuestsConfig, SoloConfig, StorageConfig, AiConfig, BuyerAddress, BuyerIdentity, AdoptedAppForm, AdoptedApp, OwnerIdentity, OwnerMailbox, DeclaredOrg, OrgServiceSchema, } from './org.js';
|
|
2
|
-
export { APP_ENTRY_KEY, DEFAULT_GATE_LIMIT, FILES_ENTRY_PERMISSIONS, AI_ENTRY_PERMISSIONS, DEFAULT_MAIL_APPS, ORG_SERVICE_SCHEMAS, appEntry, ownerMailboxes, validateBuyerIdentity, validateHosting, validateMail, validateOwners, validateSite, } from './org.js';
|
|
1
|
+
export type { OrgId, MailMode, MailConfig, ChatConfig, CallsConfig, IssuerConfig, SiteConfig, IssuerAppKind, IssuerApp, HostingConfig, HostingEntry, EntryGate, HostingEntryUses, EntryAuth, EntryFiles, EntryAi, EnvValue, SsoConfig, GuestsConfig, SoloConfig, StorageConfig, AiConfig, BuyerAddress, BuyerIdentity, AdoptedAppForm, AdoptedApp, OwnerIdentity, OwnerMailbox, DeclaredOrg, OrgServiceSchema, ServicesConfig, } from './org.js';
|
|
2
|
+
export { APP_ENTRY_KEY, DEFAULT_GATE_LIMIT, FILES_ENTRY_PERMISSIONS, AI_ENTRY_PERMISSIONS, DEFAULT_MAIL_APPS, ORG_SERVICE_SCHEMAS, SFU_SLOT_MAX, appEntry, ownerMailboxes, ownsServices, validateBuyerIdentity, validateHosting, validateMail, validateOwners, validateServices, validateSite, } from './org.js';
|
|
3
3
|
export type { DeclaredPlatform, PlatformService, PlatformServiceKey, PlatformSfuService, } from './platform.js';
|
|
4
4
|
export { PLATFORM_SERVICE_KEYS, defaultPlatformHostname, validatePlatform, } from './platform.js';
|
|
5
5
|
import { type PinnedVersions } from './bootstrap/versions.js';
|
|
@@ -10,7 +10,7 @@ export { REQUESTABLE_FIELDS, classifyChange, RequestedChangeError } from './requ
|
|
|
10
10
|
export type { SlugRejectionReason, SlugRejection } from './slug.js';
|
|
11
11
|
export { validateSlug, slugFromName, RESERVED_SLUGS } from './slug.js';
|
|
12
12
|
export type { Checkpoint, LastApplied, AppliedState, DestroyRecord, DestroyStepRecord, } from './state.js';
|
|
13
|
-
export type { PlanStep, UnmanagedItem, Plan, VersionChangeItem } from './plan.js';
|
|
13
|
+
export type { PlanStep, UnmanagedItem, PlanNote, Plan, VersionChangeItem } from './plan.js';
|
|
14
14
|
export type { AppliedVersion, AppliedVersionsStore, DeclaredOrgStore, HeldLease, MutationLease, PlatformStore, RequestedChangeStore, StaleUpdate, UpdateOptions, } from './store.js';
|
|
15
15
|
export { StaleLeaseError } from './store.js';
|
|
16
16
|
export type { PlanApply, DestroyProgress } from './apply.js';
|
|
@@ -18,17 +18,25 @@ export { createPostgresAppliedVersionsStore, createPostgresDeclaredOrgStore, cre
|
|
|
18
18
|
export { createPostgresAuditLog } from './store/audit.js';
|
|
19
19
|
export type { FoundryStoreDb } from './store/postgres.js';
|
|
20
20
|
export { declaredOrgs, foundryAppliedState, foundryAppliedVersions, foundryAuditEvents, foundryDeclaredPlatform, foundryRequestedChanges, type AuditEventRow, type DeclaredOrgRow, type NewDeclaredOrgRow, type AppliedStateRow, type NewAppliedStateRow, type AppliedVersionRow, type NewAppliedVersionRow, type DeclaredPlatformRow, type NewDeclaredPlatformRow, type RequestedChangeRow, type NewRequestedChangeRow, } from './store/schema.js';
|
|
21
|
-
export type { LiveResource, ZitadelReader, CoolifyReader, CloudflareReader, FilesReader, AiReader, CmsReader, LiveReaders, PlatformCloudflareReader, PlatformCoolifyReader, PlatformLiveReaders, StepAction, PlannedStep, StepSystem, PlanDetail, OrgPostgresPlan, PlatformPlanDetail, AdoptCandidate, AdoptInput, AdoptPlan, AdoptSnapshot, AuditActor, AuditLog, AuthzGate, StepExecutor, StepDestroyer, DestroyTarget, StepExecutionContext, PlatformStepExecutor, PlatformStepExecutionContext, FoundryPlanApplyOptions, FoundryApplyOptions, UnconfirmedUpgrade, } from './plan/index.js';
|
|
21
|
+
export type { LiveResource, ZitadelReader, CoolifyReader, CloudflareReader, FilesReader, AiReader, CmsReader, LiveReaders, PlatformCloudflareReader, PlatformCoolifyReader, PlatformLiveReaders, StepAction, PlannedStep, StepSystem, PlanDetail, OrgPostgresPlan, PlatformPlanDetail, MailRoute, AdoptCandidate, AdoptInput, AdoptPlan, AdoptSnapshot, AuditActor, AuditLog, AuthzGate, StepExecutor, StepDestroyer, DestroyTarget, StepExecutionContext, PlatformStepExecutor, PlatformStepExecutionContext, FoundryPlanApplyOptions, FoundryApplyOptions, UnconfirmedUpgrade, } from './plan/index.js';
|
|
22
22
|
export { AdoptRefusedError } from './plan/index.js';
|
|
23
23
|
export { createEdgeAccess, createEdgeDns, edgeDnsRecordOwners, createEdgeRateLimits, createEdgeTunnels, createEdgeWorkers, createEdgeZones, } from './plan/index.js';
|
|
24
24
|
export type { AccessAppSummary, AccessDecision, AccessPolicyRule, AccessPort, DnsPort, EdgePlatformAccess, EdgePlatformDns, EdgePlatformRateLimits, EdgePlatformWorkers, EdgePlatformZones, EdgeZonesOptions, ZonePort, EdgeRateLimitsOptions, RateLimitPort, RateLimitRuleInput, EdgeWorkersOptions, EdgePlatformTunnels, TunnelPort, } from './plan/index.js';
|
|
25
25
|
export { readLiveResources, readLivePlatformResources, computePlanDetail, toPlan, planPlatform, toPlatformPlan, JOBS_INTERNAL_URL_SCOPE_KEY, JOBS_SCHEDULER_KEY, PLATFORM_AI_KEY, PLATFORM_AI_INTERNAL_URL_SCOPE_KEY, PLATFORM_AI_MANAGEMENT_KEY_SCOPE_KEY, PLATFORM_AI_ORGANISATION_SCOPE_KEY, PLATFORM_FILES_KEY, PLATFORM_FILES_INTERNAL_URL_SCOPE_KEY, PLATFORM_FILES_ZITADEL_PROJECT_SCOPE_KEY, PLATFORM_MAIL_INTERNAL_URL_SCOPE_KEY, PLATFORM_MAIL_ZITADEL_PROJECT_SCOPE_KEY, PLATFORM_EDGE_KEY, PLATFORM_EDGE_INTERNAL_URL_SCOPE_KEY, PLATFORM_EDGE_KEY_REF, PLATFORM_EDGE_CLOUDFLARE_TOKEN_REF, PLATFORM_SFU_APPLIED_SCOPE_KEY, FoundryPlanApply, DeclaredOrgNotFoundError, StalePlanError, ConfirmationRequiredError, NotAuthorizedError, NotATestOrgError, DeclaredPlatformNotFoundError, PlatformStalePlanError, PlatformConfirmationRequiredError, UpgradeConfirmationRequiredError, planNeedsConfirmation, } from './plan/index.js';
|
|
26
26
|
export type { HeldKeyResolver, OrgPlatformReadiness } from './plan/index.js';
|
|
27
|
-
export { DEFAULT_ORG_PLATFORM_READINESS, ORG_PLATFORM_KEY_REFS, agoraHostValuesFromPlatform, heldPlatformKeyRefs, orgPlatformReadinessFrom, } from './plan/index.js';
|
|
27
|
+
export { DEFAULT_ORG_PLATFORM_READINESS, ORG_PLATFORM_KEY_REFS, agoraHostValuesForOwn, agoraHostValuesFromPlatform, heldOwnKeyRefs, heldPlatformKeyRefs, orgPlatformReadinessFor, orgPlatformReadinessFrom, } from './plan/index.js';
|
|
28
28
|
export type { PlatformOrgClientFactories } from './plan/index.js';
|
|
29
29
|
export { PlatformOrgBlockedError, PlatformOrgPorts } from './plan/index.js';
|
|
30
|
-
export type {
|
|
30
|
+
export type { FilesKeysMint, FilesKeysMinted, OwnFilesDeps, OwnFilesR2Key, OwnFilesR2Keys, OwnServicePorts, OwnServicesPort, OwnServicesProvision, OwnSfuDeps, ServiceTarget, } from './plan/index.js';
|
|
31
|
+
export { OWN_FILES_R2_SCOPE_KEY, OWN_FILES_R2_UNDELIVERED_SCOPE_KEY, OWN_SERVICES_APPLIED_SCOPE_KEY, OwnServicesBlockedError, orgTarget, } from './plan/index.js';
|
|
32
|
+
export type { FilesHostInput, JobsHostDeps, JobsHostInput, OwnAuthInstance, PlanOwnAuthOptions, PlanOwnFilesOptions, PlanOwnFilesR2Options, PlanOwnJobsOptions, } from './plan/index.js';
|
|
33
|
+
export { OWN_AUTH_APPLIED_GIT_REF_SCOPE_KEY, OWN_AUTH_APPLIED_HOSTNAME_SCOPE_KEY, OWN_AUTH_BLOCKED_KEY, OWN_FILES_BLOCKED_KEY, OWN_FILES_R2_BLOCKED_KEY, OWN_JOBS_BLOCKED_KEY, OWN_MAIL_BLOCKED_KEY, planOwnAuth, planOwnFiles, planOwnFilesR2, planOwnJobs, planOwnMail, provisionFilesHost, provisionJobsHost, provisionMailServer, } from './plan/index.js';
|
|
34
|
+
export type { PlanOwnSfuOptions, SfuAppDeps, SfuAppInput } from './plan/index.js';
|
|
35
|
+
export { OWN_SFU_BLOCKED_KEY, OWN_SFU_FIREWALL_KEY, planOwnSfu, provisionSfuApp, sfuConfig, sfuPortsMappings, } from './plan/index.js';
|
|
36
|
+
export type { CoolifyPort, CloudflarePort, ZitadelPort, CallsSfuPort, JobsSchedulerPort, FilesOrganisationPort, AiOrganisationPort, FoundryStepExecutorPorts, PlatformOrgPortSource, AgoraHostPort, AgoraHostValues, LoginHostBinding, LoginHostBindingsPort, LoginHostDiscoveryPort, } from './plan/index.js';
|
|
31
37
|
export { FoundryStepExecutor, NotBuiltError, healthUrls, loginHostnameFor } from './plan/index.js';
|
|
38
|
+
export type { EnsureSignInOrgOptions } from './plan/index.js';
|
|
39
|
+
export { ZITADEL_ORG_DOMAIN_SCOPE_KEY, ensureSignInOrg, registerSignInDomain, signInOrgDomain, } from './plan/index.js';
|
|
32
40
|
export { HttpJobsSchedulerClient } from './plan/index.js';
|
|
33
41
|
export type { HealthTarget, HealthTargetDepth, HealthTargetExpect } from './plan/index.js';
|
|
34
42
|
export { healthTargets, platformHealthTargets } from './plan/index.js';
|
|
@@ -40,7 +48,7 @@ export type { MailIdentityApp, MailIdentityAudienceCheck } from './plan/index.js
|
|
|
40
48
|
export { checkMailIdentityAudience } from './plan/index.js';
|
|
41
49
|
export type { MailAllowlistApp } from './plan/index.js';
|
|
42
50
|
export { checkMailAppsAllowlist } from './plan/index.js';
|
|
43
|
-
export { FoundryPlatformStepExecutor, ProvidersPlatformCloudflareReader, ProvidersPlatformCoolifyReader, createZitadelAdminMinter, createZitadelLoginPolicy, createZitadelProjectRoles, PLATFORM_AUTH_KEY, PLATFORM_MAIL_KEY, PLATFORM_MAIL_ADMIN_KEY_DESCRIPTION, } from './plan/index.js';
|
|
51
|
+
export { FoundryPlatformStepExecutor, ProvidersPlatformCloudflareReader, ProvidersPlatformCoolifyReader, createZitadelAdminMinter, createZitadelLoginPolicy, createZitadelProjectRoles, PLATFORM_AUTH_KEY, PLATFORM_MAIL_KEY, PLATFORM_MAIL_RELAY_KEY, PLATFORM_MAIL_RELAY_ROUTES_SCOPE_KEY, PLATFORM_MAIL_ADMIN_KEY_DESCRIPTION, } from './plan/index.js';
|
|
44
52
|
export type { PlatformAuthDeps, PlatformAuthSmtp, PlatformAuthRuntime, PlatformFilesDeps, PlatformFilesRuntime, PlatformEdgeDeps, PlatformDnsFactory, PlatformAccessFactory, PlatformUptimeAccessFactory, PlatformUptimeAccessPort, PlatformUptimeDeps, PlatformWorkersFactory, PlatformWorkersPort, PlatformAccessPort, PlatformTunnelsFactory, PlatformEdgeRuntime, } from './plan/index.js';
|
|
45
53
|
export type { PlatformMailDeps, PlatformMailAdminPort, PlatformMailRuntime, } from './plan/index.js';
|
|
46
54
|
export { FILES_INTROSPECTION_SECRET_REF, FILES_MANAGEMENT_ISSUER, FILES_MANAGEMENT_KEY_ID, FILES_MANAGEMENT_KEY_REF, PLATFORM_AUTH_INSTANCE_BEARER_REF, } from './plan/index.js';
|
|
@@ -58,7 +66,7 @@ export { mintLivekitCredentials } from './calls-sfu-keys.js';
|
|
|
58
66
|
export type { MailCopyJob, MailCopyPort, MailCopyStepResult } from './plan/index.js';
|
|
59
67
|
export { MAIL_COPY_COMPLETED_SCOPE_KEY, MAIL_COPY_DONE_SCOPE_KEY, MissingMailCopyAccountsError, MissingMailCopySourceTokenError, MissingMailCopyStalwartTokenError, parseMailCopyCompleted, runMailCopyStep, } from './plan/index.js';
|
|
60
68
|
export { migrationsDir as MIGRATIONS_DIR } from './migrations-dir.js';
|
|
61
|
-
export type { BootstrapCompany, BootstrapExecutorDeps, BootstrapOwner, BootstrapPlatformDeps, BootstrapPlatformInput, BootstrapProgress, BootstrapResult, RootCredentials, } from './bootstrap/index.js';
|
|
69
|
+
export type { BootstrapCompany, BootstrapCompanyDeps, BootstrapExecutorDeps, BootstrapOwner, BootstrapPlatformDeps, BootstrapPlatformInput, BootstrapProgress, BootstrapResult, RootCredentials, } from './bootstrap/index.js';
|
|
62
70
|
export { bootstrapPlatform } from './bootstrap/index.js';
|
|
63
71
|
export type { OrgAppVersionRef, OrgAppVersionStatus, WtfalchPackageDrift, PlatformVersionStatus, ReadPlatformVersionStatusOptions, StatusCoolifyPort, StatusGithubPort, StatusGhExec, } from './plan/index.js';
|
|
64
72
|
export { readPlatformVersionStatus, wtfalchVersionsFromPnpmLockfile, providersStatusCoolifyPort, ghCliStatusGithubPort, } from './plan/index.js';
|
|
@@ -66,3 +74,5 @@ export type { RunnerDb } from './runner/db.js';
|
|
|
66
74
|
export { AlreadyRunningError, RunNotFoundError, StepFailedError, StepMismatchError, } from './runner/errors.js';
|
|
67
75
|
export { createJobRunner } from './runner/runner.js';
|
|
68
76
|
export type { CreateJobRunnerOptions, JobRunner, RunFailure, RunRecord, RunStatus, RunStepsOptions, RunnerStep, } from './runner/types.js';
|
|
77
|
+
export type { StoredApp } from './plan/health-gaps.js';
|
|
78
|
+
export { EXIT_JOBS, REPO_DOCKERFILE_CHECKED, hasRealCheck, healthGaps, } from './plan/health-gaps.js';
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { APP_ENTRY_KEY, DEFAULT_GATE_LIMIT, FILES_ENTRY_PERMISSIONS, AI_ENTRY_PERMISSIONS, DEFAULT_MAIL_APPS, ORG_SERVICE_SCHEMAS, appEntry, ownerMailboxes, validateBuyerIdentity, validateHosting, validateMail, validateOwners, validateSite, } from './org.js';
|
|
1
|
+
export { APP_ENTRY_KEY, DEFAULT_GATE_LIMIT, FILES_ENTRY_PERMISSIONS, AI_ENTRY_PERMISSIONS, DEFAULT_MAIL_APPS, ORG_SERVICE_SCHEMAS, SFU_SLOT_MAX, appEntry, ownerMailboxes, ownsServices, validateBuyerIdentity, validateHosting, validateMail, validateOwners, validateServices, validateSite, } from './org.js';
|
|
2
2
|
export { PLATFORM_SERVICE_KEYS, defaultPlatformHostname, validatePlatform, } from './platform.js';
|
|
3
3
|
// archon#273: the pinned platform service versions the planner deploys, so a
|
|
4
4
|
// host can show declared against running. Readonly: it is the planner's own
|
|
@@ -14,16 +14,20 @@ export { declaredOrgs, foundryAppliedState, foundryAppliedVersions, foundryAudit
|
|
|
14
14
|
export { AdoptRefusedError } from './plan/index.js';
|
|
15
15
|
export { createEdgeAccess, createEdgeDns, edgeDnsRecordOwners, createEdgeRateLimits, createEdgeTunnels, createEdgeWorkers, createEdgeZones, } from './plan/index.js';
|
|
16
16
|
export { readLiveResources, readLivePlatformResources, computePlanDetail, toPlan, planPlatform, toPlatformPlan, JOBS_INTERNAL_URL_SCOPE_KEY, JOBS_SCHEDULER_KEY, PLATFORM_AI_KEY, PLATFORM_AI_INTERNAL_URL_SCOPE_KEY, PLATFORM_AI_MANAGEMENT_KEY_SCOPE_KEY, PLATFORM_AI_ORGANISATION_SCOPE_KEY, PLATFORM_FILES_KEY, PLATFORM_FILES_INTERNAL_URL_SCOPE_KEY, PLATFORM_FILES_ZITADEL_PROJECT_SCOPE_KEY, PLATFORM_MAIL_INTERNAL_URL_SCOPE_KEY, PLATFORM_MAIL_ZITADEL_PROJECT_SCOPE_KEY, PLATFORM_EDGE_KEY, PLATFORM_EDGE_INTERNAL_URL_SCOPE_KEY, PLATFORM_EDGE_KEY_REF, PLATFORM_EDGE_CLOUDFLARE_TOKEN_REF, PLATFORM_SFU_APPLIED_SCOPE_KEY, FoundryPlanApply, DeclaredOrgNotFoundError, StalePlanError, ConfirmationRequiredError, NotAuthorizedError, NotATestOrgError, DeclaredPlatformNotFoundError, PlatformStalePlanError, PlatformConfirmationRequiredError, UpgradeConfirmationRequiredError, planNeedsConfirmation, } from './plan/index.js';
|
|
17
|
-
export { DEFAULT_ORG_PLATFORM_READINESS, ORG_PLATFORM_KEY_REFS, agoraHostValuesFromPlatform, heldPlatformKeyRefs, orgPlatformReadinessFrom, } from './plan/index.js';
|
|
17
|
+
export { DEFAULT_ORG_PLATFORM_READINESS, ORG_PLATFORM_KEY_REFS, agoraHostValuesForOwn, agoraHostValuesFromPlatform, heldOwnKeyRefs, heldPlatformKeyRefs, orgPlatformReadinessFor, orgPlatformReadinessFrom, } from './plan/index.js';
|
|
18
18
|
export { PlatformOrgBlockedError, PlatformOrgPorts } from './plan/index.js';
|
|
19
|
+
export { OWN_FILES_R2_SCOPE_KEY, OWN_FILES_R2_UNDELIVERED_SCOPE_KEY, OWN_SERVICES_APPLIED_SCOPE_KEY, OwnServicesBlockedError, orgTarget, } from './plan/index.js';
|
|
20
|
+
export { OWN_AUTH_APPLIED_GIT_REF_SCOPE_KEY, OWN_AUTH_APPLIED_HOSTNAME_SCOPE_KEY, OWN_AUTH_BLOCKED_KEY, OWN_FILES_BLOCKED_KEY, OWN_FILES_R2_BLOCKED_KEY, OWN_JOBS_BLOCKED_KEY, OWN_MAIL_BLOCKED_KEY, planOwnAuth, planOwnFiles, planOwnFilesR2, planOwnJobs, planOwnMail, provisionFilesHost, provisionJobsHost, provisionMailServer, } from './plan/index.js';
|
|
21
|
+
export { OWN_SFU_BLOCKED_KEY, OWN_SFU_FIREWALL_KEY, planOwnSfu, provisionSfuApp, sfuConfig, sfuPortsMappings, } from './plan/index.js';
|
|
19
22
|
export { FoundryStepExecutor, NotBuiltError, healthUrls, loginHostnameFor } from './plan/index.js';
|
|
23
|
+
export { ZITADEL_ORG_DOMAIN_SCOPE_KEY, ensureSignInOrg, registerSignInDomain, signInOrgDomain, } from './plan/index.js';
|
|
20
24
|
export { HttpJobsSchedulerClient } from './plan/index.js';
|
|
21
25
|
export { healthTargets, platformHealthTargets } from './plan/index.js';
|
|
22
26
|
export { ProvidersAiReader, ProvidersCloudflareReader, ProvidersCmsReader, ProvidersCoolifyReader, ProvidersZitadelReader, storeLoginHostResolver, storeScopeResolver, storeRecordedIds, } from './plan/index.js';
|
|
23
27
|
export { adaptZitadelListApps } from './plan/index.js';
|
|
24
28
|
export { checkMailIdentityAudience } from './plan/index.js';
|
|
25
29
|
export { checkMailAppsAllowlist } from './plan/index.js';
|
|
26
|
-
export { FoundryPlatformStepExecutor, ProvidersPlatformCloudflareReader, ProvidersPlatformCoolifyReader, createZitadelAdminMinter, createZitadelLoginPolicy, createZitadelProjectRoles, PLATFORM_AUTH_KEY, PLATFORM_MAIL_KEY, PLATFORM_MAIL_ADMIN_KEY_DESCRIPTION, } from './plan/index.js';
|
|
30
|
+
export { FoundryPlatformStepExecutor, ProvidersPlatformCloudflareReader, ProvidersPlatformCoolifyReader, createZitadelAdminMinter, createZitadelLoginPolicy, createZitadelProjectRoles, PLATFORM_AUTH_KEY, PLATFORM_MAIL_KEY, PLATFORM_MAIL_RELAY_KEY, PLATFORM_MAIL_RELAY_ROUTES_SCOPE_KEY, PLATFORM_MAIL_ADMIN_KEY_DESCRIPTION, } from './plan/index.js';
|
|
27
31
|
export { FILES_INTROSPECTION_SECRET_REF, FILES_MANAGEMENT_ISSUER, FILES_MANAGEMENT_KEY_ID, FILES_MANAGEMENT_KEY_REF, PLATFORM_AUTH_INSTANCE_BEARER_REF, } from './plan/index.js';
|
|
28
32
|
export { AI_MANAGEMENT_ISSUER, AI_MANAGEMENT_KEY_ID, AI_SIGNING_KEY_REF, AiManagementKeyExistsError, createAiManagementKeyMinter, PLATFORM_AI_MANAGEMENT_KEY_REF, PLATFORM_AI_MANAGEMENT_PERMISSIONS, } from './plan/index.js';
|
|
29
33
|
export { MissingCallsSfuConnectionError, applyCallsSfuPlatformStep, callsSfuAppliedFingerprint, } from './plan/index.js';
|
|
@@ -39,3 +43,4 @@ export { bootstrapPlatform } from './bootstrap/index.js';
|
|
|
39
43
|
export { readPlatformVersionStatus, wtfalchVersionsFromPnpmLockfile, providersStatusCoolifyPort, ghCliStatusGithubPort, } from './plan/index.js';
|
|
40
44
|
export { AlreadyRunningError, RunNotFoundError, StepFailedError, StepMismatchError, } from './runner/errors.js';
|
|
41
45
|
export { createJobRunner } from './runner/runner.js';
|
|
46
|
+
export { EXIT_JOBS, REPO_DOCKERFILE_CHECKED, hasRealCheck, healthGaps, } from './plan/health-gaps.js';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
-- @wtfalch/foundry: a company's own services. `services_own` true gives the
|
|
2
|
+
-- org its own sign-in, mail, files, jobs and calls SFU; false (the default)
|
|
3
|
+
-- keeps the platform's shared ones. `services_sfu_slot` is the slice of the
|
|
4
|
+
-- server's SFU ports an own org's media server takes; no two orgs share one.
|
|
5
|
+
-- Mirrors src/store/schema.ts's `declaredOrgs` table additions.
|
|
6
|
+
|
|
7
|
+
alter table foundry_declared_orgs
|
|
8
|
+
add column if not exists services_own boolean not null default false,
|
|
9
|
+
add column if not exists services_sfu_slot integer;
|
|
10
|
+
|
|
11
|
+
create unique index if not exists foundry_declared_orgs_sfu_slot_idx
|
|
12
|
+
on foundry_declared_orgs (services_sfu_slot)
|
|
13
|
+
where services_sfu_slot is not null;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
-- @wtfalch/foundry: `mail_relay` true puts one shared relay container on host
|
|
2
|
+
-- port 25, which routes each message by recipient domain to a company's own
|
|
3
|
+
-- mail server or to the platform's; false (the default) keeps the platform
|
|
4
|
+
-- Stalwart bound to port 25 directly. Mirrors src/store/schema.ts's
|
|
5
|
+
-- `foundryDeclaredPlatform` table addition.
|
|
6
|
+
|
|
7
|
+
alter table foundry_declared_platform
|
|
8
|
+
add column if not exists mail_relay boolean not null default false;
|