@wtfalch/foundry 0.61.0 → 0.63.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/declare.js +4 -1
- package/dist/bootstrap/versions.d.ts +2 -0
- package/dist/index.d.ts +9 -1
- package/dist/index.js +3 -0
- package/dist/migrations/0065_native_build_runs.sql +32 -0
- package/dist/migrations/0066_org_kosmos.sql +10 -0
- package/dist/migrations/0067_requested_changes_kosmos.sql +6 -0
- package/dist/migrations/0068_platform_registry.sql +17 -0
- package/dist/modules/sign-in.js +1 -1
- package/dist/native-build.d.ts +50 -0
- package/dist/native-build.js +72 -0
- package/dist/org.d.ts +22 -3
- package/dist/org.js +25 -2
- package/dist/plan/agora.d.ts +1 -1
- package/dist/plan/agora.js +4 -0
- package/dist/plan/archon-blue-green.d.ts +5 -0
- package/dist/plan/archon-blue-green.js +2 -0
- package/dist/plan/blue-green-apply.d.ts +5 -0
- package/dist/plan/blue-green-apply.js +197 -22
- package/dist/plan/blue-green.d.ts +80 -9
- package/dist/plan/blue-green.js +115 -14
- package/dist/plan/boule.d.ts +1 -1
- package/dist/plan/boule.js +2 -0
- package/dist/plan/broker-entries.d.ts +9 -0
- package/dist/plan/broker-entries.js +29 -0
- package/dist/plan/deploy-health.js +9 -2
- package/dist/plan/desktop-callback.js +4 -1
- package/dist/plan/diff.d.ts +5 -0
- package/dist/plan/diff.js +184 -63
- package/dist/plan/edge-access.d.ts +2 -3
- package/dist/plan/edge-access.js +8 -0
- package/dist/plan/executor.d.ts +111 -19
- package/dist/plan/executor.js +578 -72
- package/dist/plan/index.d.ts +2 -0
- package/dist/plan/index.js +1 -0
- package/dist/plan/jobs-scheduler-http.d.ts +2 -0
- package/dist/plan/jobs-scheduler-http.js +11 -0
- package/dist/plan/kosmos.d.ts +56 -0
- package/dist/plan/kosmos.js +84 -0
- package/dist/plan/locked-hostnames.js +5 -2
- package/dist/plan/org-names.d.ts +3 -3
- package/dist/plan/org-names.js +3 -1
- package/dist/plan/org-postgres-migrate.js +1 -0
- package/dist/plan/own-limits.d.ts +12 -0
- package/dist/plan/own-limits.js +2 -0
- package/dist/plan/plan-apply.d.ts +51 -1
- package/dist/plan/plan-apply.js +298 -25
- package/dist/plan/platform-diff.d.ts +6 -0
- package/dist/plan/platform-diff.js +74 -0
- package/dist/plan/platform-org.d.ts +2 -0
- package/dist/plan/platform-providers.d.ts +46 -1
- package/dist/plan/platform-providers.js +162 -1
- package/dist/plan/platform-registry-keys.d.ts +58 -0
- package/dist/plan/platform-registry-keys.js +69 -0
- package/dist/plan/platform-registry.d.ts +67 -0
- package/dist/plan/platform-registry.js +298 -0
- package/dist/plan/post-logout-uris.d.ts +1 -1
- package/dist/plan/post-logout-uris.js +5 -2
- package/dist/plan/providers-readers.js +20 -1
- package/dist/plan/release-host.d.ts +31 -2
- package/dist/plan/release-host.js +114 -6
- package/dist/plan/stage-copy-host.d.ts +104 -0
- package/dist/plan/stage-copy-host.js +259 -0
- package/dist/plan/stage-copy.js +44 -0
- package/dist/plan/stage-env.d.ts +9 -0
- package/dist/plan/stage-env.js +38 -0
- package/dist/plan/stage-guards.d.ts +57 -1
- package/dist/plan/stage-guards.js +106 -0
- package/dist/plan/stage-isolation.js +56 -0
- package/dist/plan/stage-services.d.ts +93 -0
- package/dist/plan/stage-services.js +153 -0
- package/dist/plan/stage-signin.d.ts +152 -0
- package/dist/plan/stage-signin.js +133 -0
- package/dist/plan/stage-sink.d.ts +2 -0
- package/dist/plan/stage-sink.js +6 -1
- package/dist/platform.d.ts +7 -1
- package/dist/platform.js +1 -0
- package/dist/release/decide.d.ts +12 -0
- package/dist/release/decide.js +37 -2
- package/dist/release/migration-compatibility.d.ts +66 -0
- package/dist/release/migration-compatibility.js +340 -0
- package/dist/release/run.d.ts +71 -1
- package/dist/release/run.js +92 -8
- package/dist/release/state.d.ts +14 -1
- package/dist/release/state.js +43 -0
- package/dist/release/types.d.ts +20 -0
- package/dist/release/types.js +2 -0
- package/dist/requested-changes.d.ts +11 -2
- package/dist/requested-changes.js +13 -1
- package/dist/slug.d.ts +1 -1
- package/dist/slug.js +11 -2
- package/dist/store/postgres.js +21 -0
- package/dist/store/schema.d.ts +87 -0
- package/dist/store/schema.js +10 -0
- package/package.json +3 -2
- package/versions.digests.json +1 -0
- package/versions.json +1 -0
package/dist/plan/index.d.ts
CHANGED
|
@@ -49,6 +49,8 @@ export { StageRestoreError, StageRestoreRefusedError, createStageCopyPort, execR
|
|
|
49
49
|
export type { Runner, StageCopyPortDeps } from './stage-copy-port.js';
|
|
50
50
|
export type { CallsSfuConnection, CallsSfuConnectionPort, CallsSfuKeysPort, CallsSfuSshConnector, CallsSfuPlatformDeps, } from './platform-sfu.js';
|
|
51
51
|
export { MissingCallsSfuConnectionError, applyCallsSfuPlatformStep, callsSfuAppliedFingerprint, } from './platform-sfu.js';
|
|
52
|
+
export type { PlatformRegistryDeps, PlatformRegistryRuntime } from './platform-registry.js';
|
|
53
|
+
export { PLATFORM_REGISTRY_PULL_PASSWORD_REF, PLATFORM_REGISTRY_PUSH_PASSWORD_REF, PLATFORM_REGISTRY_PUSH_USER, platformRegistryPushCredential, } from './platform-registry-keys.js';
|
|
52
54
|
export type { MailCopyJob, MailCopyPort, MailCopyStepResult } from './mail-copy.js';
|
|
53
55
|
export { MAIL_COPY_COMPLETED_SCOPE_KEY, MAIL_COPY_DONE_SCOPE_KEY, MissingMailCopyAccountsError, MissingMailCopySourceTokenError, MissingMailCopyStalwartTokenError, parseMailCopyCompleted, runMailCopyStep, } from './mail-copy.js';
|
|
54
56
|
export type { ScopeResolver, RecordedIds, DnsRecordOwners } from './providers-readers.js';
|
package/dist/plan/index.js
CHANGED
|
@@ -30,6 +30,7 @@ export { STAGE_SINK_APPLIED_SCOPE_KEY, STAGE_SINK_IMAGE_REF, STAGE_SINK_SMTP_POR
|
|
|
30
30
|
export { STAGE_COPY_EXCLUDED_TABLES, StageCopyFailedError, StageCopyRefusedError, stageCopy, tableExcluded, undoStageCopy, } from './stage-copy.js';
|
|
31
31
|
export { StageRestoreError, StageRestoreRefusedError, createStageCopyPort, execRunner, } from './stage-copy-port.js';
|
|
32
32
|
export { MissingCallsSfuConnectionError, applyCallsSfuPlatformStep, callsSfuAppliedFingerprint, } from './platform-sfu.js';
|
|
33
|
+
export { PLATFORM_REGISTRY_PULL_PASSWORD_REF, PLATFORM_REGISTRY_PUSH_PASSWORD_REF, PLATFORM_REGISTRY_PUSH_USER, platformRegistryPushCredential, } from './platform-registry-keys.js';
|
|
33
34
|
export { MAIL_COPY_COMPLETED_SCOPE_KEY, MAIL_COPY_DONE_SCOPE_KEY, MissingMailCopyAccountsError, MissingMailCopySourceTokenError, MissingMailCopyStalwartTokenError, parseMailCopyCompleted, runMailCopyStep, } from './mail-copy.js';
|
|
34
35
|
export { ProvidersAiReader, ProvidersCloudflareReader, ProvidersCmsReader, ProvidersCoolifyReader, ProvidersZitadelReader, storeLoginHostResolver, storeScopeResolver, storeRecordedIds, } from './providers-readers.js';
|
|
35
36
|
export { PlatformBrokerPort } from './broker-entries.js';
|
|
@@ -50,4 +50,15 @@ export class HttpJobsSchedulerClient {
|
|
|
50
50
|
syncKey: typeof fields.syncKey === 'string' ? fields.syncKey : undefined,
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
|
+
/** `DELETE /admin/projects/<name>`. A refusal throws with the status only. */
|
|
54
|
+
async deleteProject(name) {
|
|
55
|
+
const response = await this.fetchImpl(`${this.baseUrl}/admin/projects/${encodeURIComponent(name)}`, {
|
|
56
|
+
method: 'DELETE',
|
|
57
|
+
headers: { authorization: `Bearer ${this.adminToken}` },
|
|
58
|
+
signal: AbortSignal.timeout(ADMIN_CALL_TIMEOUT_MS),
|
|
59
|
+
});
|
|
60
|
+
if (response.status === 200 || response.status === 204)
|
|
61
|
+
return;
|
|
62
|
+
throw Object.assign(new Error(`jobs scheduler: DELETE /admin/projects/<name> answered ${response.status}`), { status: response.status });
|
|
63
|
+
}
|
|
53
64
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { DeclaredOrg } from '../org.js';
|
|
2
|
+
/**
|
|
3
|
+
* foundry#949: what both the planner (`diff.ts`) and the executor
|
|
4
|
+
* (`executor.ts`) need to agree on for an org's kosmos -- the hostname, the
|
|
5
|
+
* recorded ids and the env vars those produce -- so the plan can compare the
|
|
6
|
+
* live app with exactly what the executor would write. The same split
|
|
7
|
+
* `boule.ts` and `agora.ts` already follow, trimmed to what kosmos needs for
|
|
8
|
+
* its first release: a ZITADEL client, a database and sign-in env. Kosmos has
|
|
9
|
+
* no mail, no owner seeding and no Archon credential -- it is not Boule.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* The fixed `kosmos` ZITADEL web client's `clientId`, recorded by
|
|
13
|
+
* `executor.ts`'s `zitadelApp` and read by `coolifyKosmos` for
|
|
14
|
+
* `AUTH_CLIENT_ID`. Public, not a secret, so a flat scope id like
|
|
15
|
+
* `boule_client_id`.
|
|
16
|
+
*/
|
|
17
|
+
export declare const KOSMOS_CLIENT_ID_SCOPE_KEY = "kosmos_client_id";
|
|
18
|
+
/**
|
|
19
|
+
* The kosmos database's Coolify uuid. It has no `system:kind:key` slot of its
|
|
20
|
+
* own (the step's one auto-recorded `resourceId` is the app), so it is a flat
|
|
21
|
+
* scope id, like the Boule and cms databases.
|
|
22
|
+
*/
|
|
23
|
+
export declare const KOSMOS_DATABASE_SCOPE_KEY = "coolify_kosmos_database";
|
|
24
|
+
/**
|
|
25
|
+
* Recorded once `coolifyKosmos` has actually called `deploy` on this org's
|
|
26
|
+
* kosmos app -- the same marker {@link BOULE_DEPLOYED_SCOPE_KEY} (`boule.ts`)
|
|
27
|
+
* and {@link AGORA_DEPLOYED_SCOPE_KEY} (`agora.ts`) are, and for the same
|
|
28
|
+
* reason: Coolify defaults `instant_deploy` to false, so a freshly created
|
|
29
|
+
* app sat built but never started.
|
|
30
|
+
*/
|
|
31
|
+
export declare const KOSMOS_DEPLOYED_SCOPE_KEY = "coolify_kosmos_deployed";
|
|
32
|
+
/**
|
|
33
|
+
* `kosmos.<domain>` when the org has a domain, `<slug>-kosmos.oikos.host`
|
|
34
|
+
* otherwise -- the same tier rule `bouleHostnameFor`/`agoraHostnameFor`
|
|
35
|
+
* follow; `../slug.ts`'s `KOSMOS_SUFFIX` reserves the collision.
|
|
36
|
+
*/
|
|
37
|
+
export declare function kosmosHostnameFor(org: DeclaredOrg): string;
|
|
38
|
+
/** The env names `desiredKosmosEnv` can write, so the live reader knows which to read back. */
|
|
39
|
+
export declare const KOSMOS_MANAGED_ENV_KEYS: readonly ["APP_URL", "AUTH_ISSUER", "AUTH_ORGANIZATION_ID", "AUTH_CLIENT_ID"];
|
|
40
|
+
/**
|
|
41
|
+
* The env vars an org's kosmos app should carry, and every input still missing
|
|
42
|
+
* to build them. Callers that write must refuse on a non-empty `missing`: an
|
|
43
|
+
* kosmos with a blank client id fails every sign-in. `issuer` is the host's
|
|
44
|
+
* issuer address (`AgoraHostValues.issuer`), the same one Agora and Boule sign
|
|
45
|
+
* in at.
|
|
46
|
+
*
|
|
47
|
+
* Not here, and written by the executor instead: the database URLs
|
|
48
|
+
* (`DATABASE_URL_OWNER`, `DATABASE_URL`, minted once) and `AUTH_COOKIE_SECRET`
|
|
49
|
+
* (generated once, never rotated) -- the same split `desiredBouleEnv` keeps.
|
|
50
|
+
*/
|
|
51
|
+
export declare function desiredKosmosEnv(org: DeclaredOrg, recorded: Readonly<Record<string, string>>, issuer: string | null): {
|
|
52
|
+
env: Record<string, string>;
|
|
53
|
+
missing: string[];
|
|
54
|
+
};
|
|
55
|
+
/** The desired env keys the live app lacks or holds a different value for. */
|
|
56
|
+
export declare function kosmosEnvDrift(desired: Readonly<Record<string, string>>, live: Readonly<Record<string, string>>): string[];
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { ZITADEL_ORG_SCOPE_KEY } from './agora.js';
|
|
2
|
+
/**
|
|
3
|
+
* foundry#949: what both the planner (`diff.ts`) and the executor
|
|
4
|
+
* (`executor.ts`) need to agree on for an org's kosmos -- the hostname, the
|
|
5
|
+
* recorded ids and the env vars those produce -- so the plan can compare the
|
|
6
|
+
* live app with exactly what the executor would write. The same split
|
|
7
|
+
* `boule.ts` and `agora.ts` already follow, trimmed to what kosmos needs for
|
|
8
|
+
* its first release: a ZITADEL client, a database and sign-in env. Kosmos has
|
|
9
|
+
* no mail, no owner seeding and no Archon credential -- it is not Boule.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* The fixed `kosmos` ZITADEL web client's `clientId`, recorded by
|
|
13
|
+
* `executor.ts`'s `zitadelApp` and read by `coolifyKosmos` for
|
|
14
|
+
* `AUTH_CLIENT_ID`. Public, not a secret, so a flat scope id like
|
|
15
|
+
* `boule_client_id`.
|
|
16
|
+
*/
|
|
17
|
+
export const KOSMOS_CLIENT_ID_SCOPE_KEY = 'kosmos_client_id';
|
|
18
|
+
/**
|
|
19
|
+
* The kosmos database's Coolify uuid. It has no `system:kind:key` slot of its
|
|
20
|
+
* own (the step's one auto-recorded `resourceId` is the app), so it is a flat
|
|
21
|
+
* scope id, like the Boule and cms databases.
|
|
22
|
+
*/
|
|
23
|
+
export const KOSMOS_DATABASE_SCOPE_KEY = 'coolify_kosmos_database';
|
|
24
|
+
/**
|
|
25
|
+
* Recorded once `coolifyKosmos` has actually called `deploy` on this org's
|
|
26
|
+
* kosmos app -- the same marker {@link BOULE_DEPLOYED_SCOPE_KEY} (`boule.ts`)
|
|
27
|
+
* and {@link AGORA_DEPLOYED_SCOPE_KEY} (`agora.ts`) are, and for the same
|
|
28
|
+
* reason: Coolify defaults `instant_deploy` to false, so a freshly created
|
|
29
|
+
* app sat built but never started.
|
|
30
|
+
*/
|
|
31
|
+
export const KOSMOS_DEPLOYED_SCOPE_KEY = 'coolify_kosmos_deployed';
|
|
32
|
+
/**
|
|
33
|
+
* `kosmos.<domain>` when the org has a domain, `<slug>-kosmos.oikos.host`
|
|
34
|
+
* otherwise -- the same tier rule `bouleHostnameFor`/`agoraHostnameFor`
|
|
35
|
+
* follow; `../slug.ts`'s `KOSMOS_SUFFIX` reserves the collision.
|
|
36
|
+
*/
|
|
37
|
+
export function kosmosHostnameFor(org) {
|
|
38
|
+
return org.domain ? `kosmos.${org.domain}` : `${org.slug}-kosmos.oikos.host`;
|
|
39
|
+
}
|
|
40
|
+
/** The env names `desiredKosmosEnv` can write, so the live reader knows which to read back. */
|
|
41
|
+
export const KOSMOS_MANAGED_ENV_KEYS = [
|
|
42
|
+
'APP_URL',
|
|
43
|
+
'AUTH_ISSUER',
|
|
44
|
+
'AUTH_ORGANIZATION_ID',
|
|
45
|
+
'AUTH_CLIENT_ID',
|
|
46
|
+
];
|
|
47
|
+
/**
|
|
48
|
+
* The env vars an org's kosmos app should carry, and every input still missing
|
|
49
|
+
* to build them. Callers that write must refuse on a non-empty `missing`: an
|
|
50
|
+
* kosmos with a blank client id fails every sign-in. `issuer` is the host's
|
|
51
|
+
* issuer address (`AgoraHostValues.issuer`), the same one Agora and Boule sign
|
|
52
|
+
* in at.
|
|
53
|
+
*
|
|
54
|
+
* Not here, and written by the executor instead: the database URLs
|
|
55
|
+
* (`DATABASE_URL_OWNER`, `DATABASE_URL`, minted once) and `AUTH_COOKIE_SECRET`
|
|
56
|
+
* (generated once, never rotated) -- the same split `desiredBouleEnv` keeps.
|
|
57
|
+
*/
|
|
58
|
+
export function desiredKosmosEnv(org, recorded, issuer) {
|
|
59
|
+
const zitadelOrgId = recorded[ZITADEL_ORG_SCOPE_KEY];
|
|
60
|
+
const clientId = recorded[KOSMOS_CLIENT_ID_SCOPE_KEY];
|
|
61
|
+
const missing = [];
|
|
62
|
+
if (!issuer)
|
|
63
|
+
missing.push('issuer');
|
|
64
|
+
if (!zitadelOrgId)
|
|
65
|
+
missing.push('zitadelOrgId');
|
|
66
|
+
if (!clientId)
|
|
67
|
+
missing.push('clientId');
|
|
68
|
+
const env = {
|
|
69
|
+
APP_URL: `https://${kosmosHostnameFor(org)}`,
|
|
70
|
+
};
|
|
71
|
+
if (issuer)
|
|
72
|
+
env.AUTH_ISSUER = issuer;
|
|
73
|
+
if (zitadelOrgId)
|
|
74
|
+
env.AUTH_ORGANIZATION_ID = zitadelOrgId;
|
|
75
|
+
if (clientId)
|
|
76
|
+
env.AUTH_CLIENT_ID = clientId;
|
|
77
|
+
return { env, missing };
|
|
78
|
+
}
|
|
79
|
+
/** The desired env keys the live app lacks or holds a different value for. */
|
|
80
|
+
export function kosmosEnvDrift(desired, live) {
|
|
81
|
+
return Object.entries(desired)
|
|
82
|
+
.filter(([key, value]) => live[key] !== value)
|
|
83
|
+
.map(([key]) => key);
|
|
84
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { ownsServices } from '../org.js';
|
|
1
|
+
import { kosmosOn, ownsServices } from '../org.js';
|
|
2
2
|
import { OWN_SERVICE_LABELS } from '../service-labels.js';
|
|
3
3
|
import { agoraHostnameFor } from './agora.js';
|
|
4
4
|
import { bouleHostnameFor } from './boule.js';
|
|
5
5
|
import { cmsStudioHostnameFor, siteHostnameFor } from './hostnames.js';
|
|
6
|
+
import { kosmosHostnameFor } from './kosmos.js';
|
|
6
7
|
import { loginHostnameFor } from './login-host.js';
|
|
7
8
|
import { ownHostnames } from './own-hostnames.js';
|
|
8
9
|
export { OWN_SERVICE_LABELS };
|
|
@@ -31,6 +32,8 @@ export function lockedHostnames(org, platformHostnames) {
|
|
|
31
32
|
}
|
|
32
33
|
locked.add(agoraHostnameFor(org));
|
|
33
34
|
locked.add(bouleHostnameFor(org));
|
|
35
|
+
if (kosmosOn(org))
|
|
36
|
+
locked.add(kosmosHostnameFor(org));
|
|
34
37
|
if (org.site.enabled)
|
|
35
38
|
locked.add(cmsStudioHostnameFor(org));
|
|
36
39
|
if (org.calls.enabled && org.calls.usesDomain)
|
|
@@ -58,6 +61,6 @@ export function hostingNames(org, platformHostnames) {
|
|
|
58
61
|
locked: lockedHostnames(org, platformHostnames),
|
|
59
62
|
slug: org.slug,
|
|
60
63
|
siteOwnsApex: org.site.enabled && org.site.usesDomain && org.domain !== null,
|
|
61
|
-
builtinBlueGreen: org.boule?.blueGreen !== undefined || org.agora?.blueGreen !== undefined,
|
|
64
|
+
builtinBlueGreen: org.boule?.blueGreen !== undefined || org.agora?.blueGreen !== undefined || kosmosOn(org),
|
|
62
65
|
};
|
|
63
66
|
}
|
package/dist/plan/org-names.d.ts
CHANGED
|
@@ -6,13 +6,13 @@ export type OrgNameSource = 'own' | 'hosting';
|
|
|
6
6
|
* hosting entries: Agora, Boule, the tunnel and its connector, and chat,
|
|
7
7
|
* calls and the cms site while they are on.
|
|
8
8
|
*/
|
|
9
|
-
export declare function companyServiceNames(org: Pick<DeclaredOrg, 'slug' | 'chat' | 'calls' | 'site'>): string[];
|
|
9
|
+
export declare function companyServiceNames(org: Pick<DeclaredOrg, 'slug' | 'chat' | 'calls' | 'site' | 'kosmos'>): string[];
|
|
10
10
|
/**
|
|
11
11
|
* Every container name, network alias, database, tunnel and bucket name that
|
|
12
12
|
* foundry derives for an org: its own-service names when it owns services,
|
|
13
13
|
* and its hosting-entry and database names.
|
|
14
14
|
*/
|
|
15
|
-
export declare function orgDerivedNames(org: Pick<DeclaredOrg, 'slug' | 'domain' | 'services' | 'hosting' | 'chat' | 'calls' | 'site'>): {
|
|
15
|
+
export declare function orgDerivedNames(org: Pick<DeclaredOrg, 'slug' | 'domain' | 'services' | 'hosting' | 'chat' | 'calls' | 'site' | 'kosmos'>): {
|
|
16
16
|
name: string;
|
|
17
17
|
source: OrgNameSource;
|
|
18
18
|
}[];
|
|
@@ -26,5 +26,5 @@ export interface NameClash {
|
|
|
26
26
|
source: OrgNameSource;
|
|
27
27
|
}
|
|
28
28
|
/** Every name `org` derives that another org or the platform also derives. */
|
|
29
|
-
export declare function findNameClashes(org: Pick<DeclaredOrg, 'slug' | 'domain' | 'services' | 'hosting' | 'chat' | 'calls' | 'site'>, others: readonly Pick<DeclaredOrg, 'slug' | 'domain' | 'services' | 'hosting' | 'chat' | 'calls' | 'site'>[]): NameClash[];
|
|
29
|
+
export declare function findNameClashes(org: Pick<DeclaredOrg, 'slug' | 'domain' | 'services' | 'hosting' | 'chat' | 'calls' | 'site' | 'kosmos'>, others: readonly Pick<DeclaredOrg, 'slug' | 'domain' | 'services' | 'hosting' | 'chat' | 'calls' | 'site' | 'kosmos'>[]): NameClash[];
|
|
30
30
|
export declare function nameClashMessage(slug: string, clash: NameClash): string;
|
package/dist/plan/org-names.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ownsServices } from '../org.js';
|
|
1
|
+
import { kosmosOn, ownsServices } from '../org.js';
|
|
2
2
|
import { entryHasDatabase } from '../worker-app-config.js';
|
|
3
3
|
import { chatInternalNames, chatRekeyedInternalNames } from './chat-matrix.js';
|
|
4
4
|
import { workerAppHostname, workspaceHost, workspaceInboxHost } from './hostnames.js';
|
|
@@ -20,6 +20,8 @@ export function companyServiceNames(org) {
|
|
|
20
20
|
`${s}-tunnel`,
|
|
21
21
|
`${s}-cloudflared`,
|
|
22
22
|
];
|
|
23
|
+
if (kosmosOn(org))
|
|
24
|
+
names.push(`${s}-kosmos`, `${s}-kosmos-db`);
|
|
23
25
|
if (org.chat?.enabled) {
|
|
24
26
|
names.push(...Object.values(chatInternalNames(s)), ...Object.values(chatRekeyedInternalNames(s)));
|
|
25
27
|
}
|
|
@@ -107,6 +107,12 @@ export declare const LIMITS: {
|
|
|
107
107
|
autoDeploy: false | "declared";
|
|
108
108
|
capabilities: CapabilityTier;
|
|
109
109
|
};
|
|
110
|
+
readonly kosmos: {
|
|
111
|
+
memory: string;
|
|
112
|
+
dbMemory: string;
|
|
113
|
+
autoDeploy: false | "declared";
|
|
114
|
+
capabilities: CapabilityTier;
|
|
115
|
+
};
|
|
110
116
|
readonly companyOther: {
|
|
111
117
|
memory: string;
|
|
112
118
|
dbMemory: string;
|
|
@@ -167,6 +173,12 @@ export declare const LIMITS: {
|
|
|
167
173
|
autoDeploy: false | "declared";
|
|
168
174
|
capabilities: CapabilityTier;
|
|
169
175
|
};
|
|
176
|
+
readonly platformRegistry: {
|
|
177
|
+
memory: string;
|
|
178
|
+
dbMemory: string;
|
|
179
|
+
autoDeploy: false | "declared";
|
|
180
|
+
capabilities: CapabilityTier;
|
|
181
|
+
};
|
|
170
182
|
readonly platformMail: {
|
|
171
183
|
memory: string;
|
|
172
184
|
dbMemory: string;
|
package/dist/plan/own-limits.js
CHANGED
|
@@ -29,6 +29,7 @@ export const LIMITS = {
|
|
|
29
29
|
calls: profile('1g', '512m'),
|
|
30
30
|
agora: profile('1g', '512m'),
|
|
31
31
|
boule: profile('512m'),
|
|
32
|
+
kosmos: profile('512m'),
|
|
32
33
|
companyOther: profile('1g', '512m'),
|
|
33
34
|
platformArchon: profile('1g'),
|
|
34
35
|
platformAi: profile('1g'),
|
|
@@ -39,6 +40,7 @@ export const LIMITS = {
|
|
|
39
40
|
platformFiles: profile('1g'),
|
|
40
41
|
platformEdge: profile('512m'),
|
|
41
42
|
platformHc: profile('512m'),
|
|
43
|
+
platformRegistry: profile('256m'),
|
|
42
44
|
platformMail: profile('1g'),
|
|
43
45
|
platformAuth: profile('1g'),
|
|
44
46
|
platformJobs: profile('1g'),
|
|
@@ -4,7 +4,7 @@ import { type AuditSealingOptions } from '../audit/tick.js';
|
|
|
4
4
|
import type { DeclaredOrg, HostingEntry, OrgId } from '../org.js';
|
|
5
5
|
import type { Plan, PlanStep } from '../plan.js';
|
|
6
6
|
import type { DeclaredPlatform } from '../platform.js';
|
|
7
|
-
import type
|
|
7
|
+
import { type ReleaseStore } from '../release/state.js';
|
|
8
8
|
import type { JobRunner } from '../runner/types.js';
|
|
9
9
|
import type { AppliedState } from '../state.js';
|
|
10
10
|
import { type AppliedVersionsStore, type DeclaredOrgStore, type PlatformStore } from '../store.js';
|
|
@@ -224,11 +224,21 @@ export interface StepExecutor {
|
|
|
224
224
|
}>>;
|
|
225
225
|
/** Whether the company's stage mail sink app (by Coolify id) is stopped or gone, read live. Optional: an executor without it reads the sink as up. */
|
|
226
226
|
stageSinkDown?(appId: string): Promise<boolean>;
|
|
227
|
+
/** Which stage-guard ports the host has; they decide the guard names in the applied structure hash. Optional: neither. */
|
|
228
|
+
stageGuardPorts?(): {
|
|
229
|
+
probes: boolean;
|
|
230
|
+
access: boolean;
|
|
231
|
+
};
|
|
227
232
|
/**
|
|
228
233
|
* The release runner's outside calls for one blue/green entry (`plan/release-host.ts`); `null` while its blue
|
|
229
234
|
* copy is not recorded. Optional: an executor without it runs no release.
|
|
230
235
|
*/
|
|
231
236
|
releaseHost?(org: DeclaredOrg, subject: BlueGreenSubject, context: ReleaseContext, renew: () => Promise<void>): ReleaseHost | null;
|
|
237
|
+
/**
|
|
238
|
+
* One release tick for one subject: the recorded stage gate's list follows the people holding `releases:preview`,
|
|
239
|
+
* owners staying. Never throws. Optional: an executor without it leaves every gate as it is.
|
|
240
|
+
*/
|
|
241
|
+
syncStageGateMembers?(org: DeclaredOrg, subject: BlueGreenSubject, context: ReleaseContext): Promise<void>;
|
|
232
242
|
/** Whether the executor's held keys can store a Worker app database's CA and certificate. Absent: true. */
|
|
233
243
|
workerAppDbKeysPuttable?(): boolean;
|
|
234
244
|
/** Whether the host supplies the backup bucket a Worker app database dumps into. Absent: true. */
|
|
@@ -305,6 +315,8 @@ export interface PlatformDriftedSteps {
|
|
|
305
315
|
authApps?: boolean;
|
|
306
316
|
/** The host sets it. */
|
|
307
317
|
edge?: boolean;
|
|
318
|
+
/** A usable, reachable Coolify server has no registry login for the current host and passwords. */
|
|
319
|
+
registryLogins?: boolean;
|
|
308
320
|
/** #380 */
|
|
309
321
|
files?: boolean;
|
|
310
322
|
backupStorage?: boolean;
|
|
@@ -340,6 +352,8 @@ export interface PlatformStepExecutor {
|
|
|
340
352
|
backupExecutorMissing?: boolean;
|
|
341
353
|
/** #540: see `PlanPlatformOptions.blocked.hc` (`platform-diff.ts`). */
|
|
342
354
|
hc?: string;
|
|
355
|
+
/** See `PlanPlatformOptions.blocked.registry` (`platform-diff.ts`). */
|
|
356
|
+
registry?: string;
|
|
343
357
|
/** #540: see `PlanPlatformOptions.blocked.heartbeatsExecutorMissing` (`platform-diff.ts`). */
|
|
344
358
|
heartbeatsExecutorMissing?: boolean;
|
|
345
359
|
/** foundry#542: see `PlanPlatformOptions.blocked.uptime` (`platform-diff.ts`). */
|
|
@@ -373,8 +387,12 @@ export interface PlatformStepExecutor {
|
|
|
373
387
|
servers?: BlueGreenServer[];
|
|
374
388
|
blocked?: string;
|
|
375
389
|
}>;
|
|
390
|
+
/** The stage guards platform-archon can make now, by name; they go into its applied structure hash. */
|
|
391
|
+
archonStageGuardNames?(platform: DeclaredPlatform, recorded: Readonly<Record<string, string>>, owner: PlatformOwnerInput): string[];
|
|
376
392
|
/** The release runner's outside calls for platform-archon (`plan/release-host.ts`); `null` while its app is not recorded. */
|
|
377
393
|
releaseHost?(platform: DeclaredPlatform, subject: BlueGreenSubject, context: ReleaseContext, renew: () => Promise<void>): ReleaseHost | null;
|
|
394
|
+
/** As {@link FoundryStepExecutor.syncStageGateMembers}, for platform-archon's own stage gate (orgId `platform`). */
|
|
395
|
+
syncStageGateMembers?(platform: DeclaredPlatform, subject: BlueGreenSubject, context: ReleaseContext): Promise<void>;
|
|
378
396
|
execute(action: StepAction, context: PlatformStepExecutionContext): Promise<{
|
|
379
397
|
resourceId?: string;
|
|
380
398
|
} | undefined>;
|
|
@@ -633,6 +651,14 @@ export interface FoundryPlanApplyOptions {
|
|
|
633
651
|
now?: () => Date;
|
|
634
652
|
/** #441: how long a mutation lease lasts before another caller may take it over. Defaults to 30 minutes. */
|
|
635
653
|
leaseTtlMs?: number;
|
|
654
|
+
/**
|
|
655
|
+
* A stage copy runs without the org's lease and takes it for a moment each time it records something. When an
|
|
656
|
+
* apply or a tick holds the lease it waits `waitMs` and tries again, up to `tries` times. Defaults to 30 s and 10.
|
|
657
|
+
*/
|
|
658
|
+
copyLeaseRetry?: {
|
|
659
|
+
tries: number;
|
|
660
|
+
waitMs: number;
|
|
661
|
+
};
|
|
636
662
|
/**
|
|
637
663
|
* #441 (part b): a durable, DB-row-backed record of `apply`, `destroy`,
|
|
638
664
|
* `adopt` and `release` for one org, held under the same mutation lease
|
|
@@ -730,6 +756,7 @@ export declare class FoundryPlanApply implements PlanApply {
|
|
|
730
756
|
private readonly onlySteps;
|
|
731
757
|
private readonly now;
|
|
732
758
|
private readonly leaseTtlMs;
|
|
759
|
+
private readonly copyLeaseRetry;
|
|
733
760
|
private readonly appliedVersions;
|
|
734
761
|
private readonly backupGate;
|
|
735
762
|
private readonly platformDeps;
|
|
@@ -900,6 +927,25 @@ export declare class FoundryPlanApply implements PlanApply {
|
|
|
900
927
|
target: string;
|
|
901
928
|
error?: string;
|
|
902
929
|
}>>;
|
|
930
|
+
/**
|
|
931
|
+
* Phase 2 for one org: takes the copy lock of each stage copy request under the org's lease, then runs the
|
|
932
|
+
* copies after the lease is released. While a copy runs, every other tick and apply for the org goes on, a
|
|
933
|
+
* hard-fail rollback included; the copy records through short leases ({@link copyContext}).
|
|
934
|
+
*/
|
|
935
|
+
private runOrgStageCopies;
|
|
936
|
+
/** Phase 2 for platform-archon; see {@link runOrgStageCopies}. */
|
|
937
|
+
private runPlatformStageCopies;
|
|
938
|
+
/**
|
|
939
|
+
* Runs one claimed copy with no lease held, then records its outcome and consumes the request. A lost lock
|
|
940
|
+
* throws (`StaleLeaseError`) and leaves the request open.
|
|
941
|
+
*/
|
|
942
|
+
private runClaimedCopy;
|
|
943
|
+
/**
|
|
944
|
+
* The recorded ids a copy reads and writes, without the org's lease. It reads a snapshot taken when the lock was
|
|
945
|
+
* claimed; each `persist` takes the lease for a moment, re-reads the stored ids and writes only the keys the
|
|
946
|
+
* copy changed, so a tick that ran in between loses nothing. The lease may be busy: it waits and tries again.
|
|
947
|
+
*/
|
|
948
|
+
private copyContext;
|
|
903
949
|
/**
|
|
904
950
|
* The recurring audit sealing run (foundry#929): seals every ledger registered by an `audit:ledger` step with the
|
|
905
951
|
* platform signer and has the timestamp authority stamp the new checkpoints. Foundry has no scheduler, so the host
|
|
@@ -907,6 +953,10 @@ export declare class FoundryPlanApply implements PlanApply {
|
|
|
907
953
|
* log line say what happened and why when nothing did. It writes nothing to the org's records.
|
|
908
954
|
*/
|
|
909
955
|
runAuditSealTick(): Promise<SealResult[]>;
|
|
956
|
+
/**
|
|
957
|
+
* One org's release run. `defer`: stage copy requests stay open and `deferred` says whether there were any.
|
|
958
|
+
* `claim`: the copies this run decided on, each with its lock taken, for {@link runClaimedCopy}.
|
|
959
|
+
*/
|
|
910
960
|
private runOrgReleaseTick;
|
|
911
961
|
/** An own org with calls on: the host's answer about its slot's ports. A failure or a slow answer is "not open". */
|
|
912
962
|
private readSfuPortsOpen;
|