@wtfalch/foundry 0.60.0 → 0.62.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apps/cloudflare.d.ts +173 -0
- package/dist/apps/cloudflare.js +17 -0
- package/dist/apps/deploy.d.ts +116 -0
- package/dist/apps/deploy.js +791 -0
- package/dist/apps/github.d.ts +47 -0
- package/dist/apps/github.js +201 -0
- package/dist/apps/index.d.ts +9 -0
- package/dist/apps/index.js +9 -0
- package/dist/apps/migrations.d.ts +110 -0
- package/dist/apps/migrations.js +458 -0
- package/dist/audit/connector.d.ts +43 -0
- package/dist/audit/connector.js +22 -0
- package/dist/audit/evidence.d.ts +49 -0
- package/dist/audit/evidence.js +56 -0
- package/dist/audit/index.d.ts +9 -0
- package/dist/audit/index.js +9 -0
- package/dist/audit/migrations.d.ts +5 -0
- package/dist/audit/migrations.js +5 -0
- package/dist/audit/registry.d.ts +49 -0
- package/dist/audit/registry.js +44 -0
- package/dist/audit/seal.d.ts +49 -0
- package/dist/audit/seal.js +131 -0
- package/dist/audit/sealer-role.d.ts +33 -0
- package/dist/audit/sealer-role.js +56 -0
- package/dist/audit/signer.d.ts +40 -0
- package/dist/audit/signer.js +78 -0
- package/dist/audit/tick.d.ts +36 -0
- package/dist/audit/tick.js +87 -0
- package/dist/catalogue/copy-en.d.ts +1 -0
- package/dist/catalogue/copy-en.js +129 -0
- package/dist/catalogue/index.d.ts +9 -0
- package/dist/catalogue/index.js +21 -0
- package/dist/catalogue/integrations.d.ts +4 -0
- package/dist/catalogue/integrations.js +104 -0
- package/dist/catalogue/systems.d.ts +7 -0
- package/dist/catalogue/systems.js +31 -0
- package/dist/catalogue/types.d.ts +110 -0
- package/dist/catalogue/types.js +1 -0
- package/dist/entry-rules.d.ts +53 -0
- package/dist/entry-rules.js +71 -0
- package/dist/hosted-label.d.ts +13 -0
- package/dist/hosted-label.js +81 -0
- package/dist/index.d.ts +14 -5
- package/dist/index.js +7 -3
- package/dist/migrations/0062_hosting_github.sql +6 -0
- package/dist/migrations/0063_boule_apps.sql +7 -0
- package/dist/migrations/0064_requested_changes_boule_apps.sql +6 -0
- package/dist/migrations/0065_native_build_runs.sql +32 -0
- package/dist/migrations/0066_org_kosmos.sql +10 -0
- package/dist/modules/ai.d.ts +3 -0
- package/dist/modules/ai.js +64 -0
- package/dist/modules/assistants.d.ts +3 -0
- package/dist/modules/assistants.js +72 -0
- package/dist/modules/audit-ledger.d.ts +29 -0
- package/dist/modules/audit-ledger.js +49 -0
- package/dist/modules/audit.d.ts +7 -0
- package/dist/modules/audit.js +44 -0
- package/dist/modules/database.d.ts +23 -0
- package/dist/modules/database.js +189 -0
- package/dist/modules/email.d.ts +3 -0
- package/dist/modules/email.js +18 -0
- package/dist/modules/entry-parts.d.ts +23 -0
- package/dist/modules/entry-parts.js +63 -0
- package/dist/modules/files.d.ts +3 -0
- package/dist/modules/files.js +64 -0
- package/dist/modules/index.d.ts +74 -0
- package/dist/modules/index.js +774 -0
- package/dist/modules/jobs.d.ts +3 -0
- package/dist/modules/jobs.js +23 -0
- package/dist/modules/organisations.d.ts +3 -0
- package/dist/modules/organisations.js +20 -0
- package/dist/modules/outputs.d.ts +9 -0
- package/dist/modules/outputs.js +38 -0
- package/dist/modules/sign-in.d.ts +3 -0
- package/dist/modules/sign-in.js +197 -0
- package/dist/modules/signed-records.d.ts +3 -0
- package/dist/modules/signed-records.js +32 -0
- package/dist/modules/types.d.ts +194 -0
- package/dist/modules/types.js +1 -0
- package/dist/modules/workspaces.d.ts +3 -0
- package/dist/modules/workspaces.js +75 -0
- package/dist/native-build.d.ts +70 -0
- package/dist/native-build.js +92 -0
- package/dist/org.d.ts +184 -24
- package/dist/org.js +444 -49
- package/dist/plan/agora.d.ts +1 -1
- package/dist/plan/agora.js +4 -0
- package/dist/plan/apps-db-route.d.ts +90 -0
- package/dist/plan/apps-db-route.js +168 -0
- package/dist/plan/apps-zones.d.ts +56 -0
- package/dist/plan/apps-zones.js +118 -0
- package/dist/plan/archon-blue-green.js +11 -1
- package/dist/plan/blue-green-apply.d.ts +37 -1
- package/dist/plan/blue-green-apply.js +55 -15
- package/dist/plan/blue-green.d.ts +60 -8
- package/dist/plan/blue-green.js +115 -10
- package/dist/plan/boule.d.ts +23 -1
- package/dist/plan/boule.js +35 -2
- package/dist/plan/broker-entries.d.ts +28 -3
- package/dist/plan/broker-entries.js +74 -3
- package/dist/plan/broker-registrar.d.ts +168 -0
- package/dist/plan/broker-registrar.js +440 -0
- package/dist/plan/broker-workspaces-database.d.ts +48 -0
- package/dist/plan/broker-workspaces-database.js +87 -0
- package/dist/plan/deploy-health.js +9 -2
- package/dist/plan/desktop-callback.js +15 -7
- package/dist/plan/diff.d.ts +8 -2
- package/dist/plan/diff.js +552 -104
- package/dist/plan/edge-access.d.ts +6 -0
- package/dist/plan/edge-access.js +1 -0
- package/dist/plan/edge-dns.d.ts +6 -0
- package/dist/plan/edge-dns.js +9 -7
- package/dist/plan/entry-ai-key.d.ts +1 -1
- package/dist/plan/entry-ai-key.js +2 -2
- package/dist/plan/entry-audit.d.ts +58 -0
- package/dist/plan/entry-audit.js +122 -0
- package/dist/plan/entry-customer-grants.d.ts +75 -0
- package/dist/plan/entry-customer-grants.js +154 -0
- package/dist/plan/entry-edge-key.d.ts +26 -1
- package/dist/plan/entry-edge-key.js +42 -3
- package/dist/plan/entry-jobs.d.ts +64 -0
- package/dist/plan/entry-jobs.js +92 -0
- package/dist/plan/entry-repo-key-apply.d.ts +55 -0
- package/dist/plan/entry-repo-key-apply.js +185 -0
- package/dist/plan/entry-repo-key.d.ts +92 -0
- package/dist/plan/entry-repo-key.js +235 -0
- package/dist/plan/entry-workspaces.d.ts +55 -0
- package/dist/plan/entry-workspaces.js +90 -0
- package/dist/plan/executor.d.ts +231 -19
- package/dist/plan/executor.js +1648 -129
- package/dist/plan/gates.d.ts +1 -1
- package/dist/plan/gates.js +62 -9
- package/dist/plan/github-deploy-keys.d.ts +78 -0
- package/dist/plan/github-deploy-keys.js +305 -0
- package/dist/plan/github-source.d.ts +53 -0
- package/dist/plan/github-source.js +140 -0
- package/dist/plan/held-key-refs.d.ts +12 -0
- package/dist/plan/held-key-refs.js +12 -0
- package/dist/plan/hostnames.d.ts +9 -0
- package/dist/plan/hostnames.js +28 -1
- package/dist/plan/index.d.ts +9 -0
- package/dist/plan/index.js +6 -0
- package/dist/plan/jobs-scheduler-http.js +3 -0
- package/dist/plan/keys-registrar-http.d.ts +32 -0
- package/dist/plan/keys-registrar-http.js +105 -0
- package/dist/plan/keys-registrar.d.ts +67 -0
- package/dist/plan/keys-registrar.js +103 -0
- package/dist/plan/kosmos.d.ts +56 -0
- package/dist/plan/kosmos.js +84 -0
- package/dist/plan/live.d.ts +1 -1
- package/dist/plan/locked-hostnames.d.ts +2 -2
- package/dist/plan/locked-hostnames.js +7 -4
- package/dist/plan/login-host.js +11 -5
- package/dist/plan/machine-paths.d.ts +36 -0
- package/dist/plan/machine-paths.js +137 -0
- package/dist/plan/mcp.d.ts +1 -1
- package/dist/plan/mcp.js +49 -17
- package/dist/plan/org-apps-admin.d.ts +108 -0
- package/dist/plan/org-apps-admin.js +434 -0
- package/dist/plan/org-names.d.ts +3 -3
- package/dist/plan/org-names.js +15 -2
- package/dist/plan/org-postgres-migrate.js +1 -0
- package/dist/plan/own-diff.d.ts +23 -0
- package/dist/plan/own-diff.js +53 -0
- package/dist/plan/own-hostnames.d.ts +2 -0
- package/dist/plan/own-hostnames.js +1 -0
- package/dist/plan/own-jobs-route.d.ts +47 -0
- package/dist/plan/own-jobs-route.js +91 -0
- package/dist/plan/own-limits.d.ts +6 -0
- package/dist/plan/own-limits.js +1 -0
- package/dist/plan/own-services.d.ts +4 -0
- package/dist/plan/own-services.js +4 -0
- package/dist/plan/plan-apply.d.ts +39 -0
- package/dist/plan/plan-apply.js +163 -9
- package/dist/plan/platform-diff.d.ts +2 -2
- package/dist/plan/platform-diff.js +7 -2
- package/dist/plan/platform-org.d.ts +30 -0
- package/dist/plan/platform-org.js +12 -1
- package/dist/plan/platform-providers.d.ts +2 -0
- package/dist/plan/platform-providers.js +13 -2
- package/dist/plan/post-logout-uris.d.ts +1 -1
- package/dist/plan/post-logout-uris.js +12 -3
- package/dist/plan/private-pages.d.ts +26 -0
- package/dist/plan/private-pages.js +39 -0
- package/dist/plan/providers-readers.js +20 -1
- package/dist/plan/proxy-labels.d.ts +12 -1
- package/dist/plan/proxy-labels.js +32 -2
- package/dist/plan/release-host.d.ts +6 -2
- package/dist/plan/release-host.js +36 -8
- package/dist/plan/stage-env.d.ts +19 -1
- package/dist/plan/stage-env.js +25 -1
- package/dist/plan/stage-guards.d.ts +20 -0
- package/dist/plan/stage-guards.js +24 -0
- package/dist/plan/stage-isolation.d.ts +24 -0
- package/dist/plan/stage-isolation.js +29 -1
- package/dist/plan/worker-app-database.d.ts +46 -42
- package/dist/plan/worker-app-database.js +150 -153
- package/dist/plan/worker-app.d.ts +5 -65
- package/dist/plan/worker-app.js +28 -105
- package/dist/plan/worker.d.ts +2 -2
- package/dist/plan/worker.js +2 -1
- package/dist/plan/workspace-client.d.ts +25 -0
- package/dist/plan/workspace-client.js +48 -0
- package/dist/plan/workspace-sign-in.d.ts +55 -0
- package/dist/plan/workspace-sign-in.js +273 -0
- package/dist/plan/workspace-worker.d.ts +4 -0
- package/dist/plan/workspace-worker.js +13 -0
- package/dist/plan/workspace.d.ts +101 -0
- package/dist/plan/workspace.js +305 -0
- package/dist/release/decide.d.ts +30 -1
- package/dist/release/decide.js +73 -11
- package/dist/release/health.d.ts +8 -3
- package/dist/release/health.js +58 -5
- package/dist/release/migration-compatibility.d.ts +66 -0
- package/dist/release/migration-compatibility.js +340 -0
- package/dist/release/run.d.ts +2 -5
- package/dist/release/run.js +4 -0
- package/dist/release/state.d.ts +3 -1
- package/dist/release/state.js +6 -0
- package/dist/release/types.d.ts +20 -0
- package/dist/requested-changes.d.ts +11 -2
- package/dist/requested-changes.js +13 -1
- package/dist/service-labels.d.ts +4 -0
- package/dist/service-labels.js +11 -0
- package/dist/slug.d.ts +2 -0
- package/dist/slug.js +20 -7
- package/dist/store/postgres.js +25 -2
- package/dist/store/schema.d.ts +73 -1
- package/dist/store/schema.js +10 -0
- package/dist/worker-app-config.d.ts +1 -11
- package/dist/worker-app-config.js +4 -17
- package/dist/worker-app-migrations.d.ts +105 -0
- package/dist/worker-app-migrations.js +166 -0
- package/dist/worker-app-upload.d.ts +103 -0
- package/dist/worker-app-upload.js +128 -0
- package/package.json +19 -2
package/dist/plan/own-diff.js
CHANGED
|
@@ -8,6 +8,7 @@ import { OWN_AUTH_APPLIED_HOSTNAME_SCOPE_KEY, ownAuthApplied } from './login-hos
|
|
|
8
8
|
import { OWN_MAIL_SMTP_INTERNAL_SCOPE_KEY, OWN_MAIL_SMTP_PORT_BASE, OWN_MAIL_SMTP_PORT_SCOPE_KEY, isOwnMailSmtpPort, } from './mail-relay.js';
|
|
9
9
|
import { mailStoreIsLegacy, mailStoreSwitchDescription } from './mail-store.js';
|
|
10
10
|
import { ownHostnames } from './own-hostnames.js';
|
|
11
|
+
import { JOBS_PUBLIC_URL_SCOPE_KEY, OWN_JOBS_ROUTE_TUNNEL_SCOPE_KEY } from './own-jobs-route.js';
|
|
11
12
|
import { OWN_FILES_R2_SCOPE_KEY, OWN_SERVICES_APPLIED_SCOPE_KEY, orgTarget, } from './own-services.js';
|
|
12
13
|
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';
|
|
13
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_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, orgDeclaresMcp, platformMailMxHost, } from './platform-diff.js';
|
|
@@ -67,6 +68,58 @@ export function planOwnJobs(options) {
|
|
|
67
68
|
},
|
|
68
69
|
};
|
|
69
70
|
}
|
|
71
|
+
/** `unmanaged` key of the one line `planOwnJobsRoute` reports. */
|
|
72
|
+
export const OWN_JOBS_ROUTE_BLOCKED_KEY = 'own-jobs-route';
|
|
73
|
+
/**
|
|
74
|
+
* The public route of an own org's jobs scheduler: `jobs.<domain>`, serving only what an app calls. The
|
|
75
|
+
* owner company always publishes its scheduler, since every company on shared services registers there; any
|
|
76
|
+
* other own org publishes its own only while an entry declares `uses.jobs`. Planned again when the hostname
|
|
77
|
+
* moved. Every blocked line is fixed text: it is part of the stale-plan comparison.
|
|
78
|
+
*/
|
|
79
|
+
export function planOwnJobsRoute(options) {
|
|
80
|
+
const { org, recordedIds, readiness, ownServices } = options;
|
|
81
|
+
if (!ownsServices(org))
|
|
82
|
+
return { step: null, blocked: null };
|
|
83
|
+
const owner = readiness.platformOwnerOrgId === org.id;
|
|
84
|
+
if (!owner && !org.hosting.entries.some((e) => e.uses?.jobs)) {
|
|
85
|
+
return { step: null, blocked: null };
|
|
86
|
+
}
|
|
87
|
+
const hostname = ownHostnames(org).jobs;
|
|
88
|
+
const schedulerApplied = Boolean(recordedIds[`coolify:own-jobs:${org.id}`]) &&
|
|
89
|
+
Boolean(recordedIds[JOBS_INTERNAL_URL_SCOPE_KEY]);
|
|
90
|
+
if (!schedulerApplied && !options.schedulerPlanned)
|
|
91
|
+
return { step: null, blocked: null };
|
|
92
|
+
if (recordedIds[JOBS_PUBLIC_URL_SCOPE_KEY] === `https://${hostname}` &&
|
|
93
|
+
recordedIds[OWN_JOBS_ROUTE_TUNNEL_SCOPE_KEY]) {
|
|
94
|
+
return { step: null, blocked: null };
|
|
95
|
+
}
|
|
96
|
+
const waiting = (reason) => ({
|
|
97
|
+
step: null,
|
|
98
|
+
blocked: `${org.slug}'s own jobs scheduler route is blocked, not planned: ${reason}`,
|
|
99
|
+
});
|
|
100
|
+
if (!ownServices)
|
|
101
|
+
return waiting('the host supplies no ownServices port');
|
|
102
|
+
if (!options.zoneActive)
|
|
103
|
+
return waiting(`the zone for ${ownHostnames(org).zone} is not active yet`);
|
|
104
|
+
const ownEdge = readiness.ownEdgePorts;
|
|
105
|
+
if (readiness.dnsBlocked && !(ownEdge?.dns && ownEdge.tunnels)) {
|
|
106
|
+
return waiting(readiness.dnsBlocked);
|
|
107
|
+
}
|
|
108
|
+
if (!ownEdge?.tunnels && !readiness.edgeReady) {
|
|
109
|
+
return waiting('the host supplies no tunnel port and edge is not ready');
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
blocked: null,
|
|
113
|
+
step: {
|
|
114
|
+
action: { system: 'coolify', kind: 'own-jobs-route', key: org.id, desired: { hostname } },
|
|
115
|
+
step: {
|
|
116
|
+
description: `publish ${org.slug}'s own jobs scheduler at ${hostname}: its tunnel and connector and a proxied CNAME`,
|
|
117
|
+
destroys: false,
|
|
118
|
+
migrates: false,
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
70
123
|
const BROKER_ENTRY_APPLIED_PREFIX = brokerEntryAppliedScopeKey('');
|
|
71
124
|
/** `unmanaged` key of the one line `planOwnAuth` reports. */
|
|
72
125
|
export const OWN_AUTH_BLOCKED_KEY = 'own-auth';
|
|
@@ -13,6 +13,8 @@ export interface OwnHostnames {
|
|
|
13
13
|
files: string;
|
|
14
14
|
/** `sfu.<domain>`, or `<slug>-sfu.oikos.host`. */
|
|
15
15
|
sfu: string;
|
|
16
|
+
/** `jobs.<domain>`, or `<slug>-jobs.oikos.host`. */
|
|
17
|
+
jobs: string;
|
|
16
18
|
/** `https://<auth>`. */
|
|
17
19
|
issuerUrl: string;
|
|
18
20
|
/** The mail domain: `<domain>`, or `<slug>.oikos.host` (names are `name@<slug>.oikos.host`). */
|
|
@@ -18,6 +18,7 @@ export function ownHostnames(org) {
|
|
|
18
18
|
mail: mailOnDomain ? `mail.${domain}` : `${slug}-mail.${NO_DOMAIN_ZONE}`,
|
|
19
19
|
files: name('files'),
|
|
20
20
|
sfu: name('sfu'),
|
|
21
|
+
jobs: name('jobs'),
|
|
21
22
|
issuerUrl: `https://${auth}`,
|
|
22
23
|
mailDomain: mailOnDomain && domain ? domain : `${slug}.${NO_DOMAIN_ZONE}`,
|
|
23
24
|
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { CoolifyPort } from './executor.js';
|
|
2
|
+
import type { ServiceNetwork, ServiceTarget, StepScope } from './own-services.js';
|
|
3
|
+
/**
|
|
4
|
+
* A company's jobs scheduler on a public hostname: a tunnel that dials the scheduler by its internal address,
|
|
5
|
+
* a connector app, and a proxied CNAME for `jobs.<domain>`. An entry runs on its own company's network and
|
|
6
|
+
* cannot dial the scheduler's container name, so `uses.jobs` gives it `https://<hostname>` once this is
|
|
7
|
+
* recorded. The owner company (hq) runs the scheduler every shared-services company registers on; a company
|
|
8
|
+
* that runs its own services publishes its own the same way. No Coolify domain and no Access app: the
|
|
9
|
+
* scheduler authenticates every caller itself (a project's sync key, the admin token), and a gate in front
|
|
10
|
+
* would stop the apps.
|
|
11
|
+
*
|
|
12
|
+
* One `coolify:own-jobs-route` step. It records {@link JOBS_PUBLIC_URL_SCOPE_KEY} last, so a run that fails
|
|
13
|
+
* halfway is planned again, finds the tunnel and connector by name, and no entry is given a dead URL.
|
|
14
|
+
*/
|
|
15
|
+
export declare const JOBS_PUBLIC_URL_SCOPE_KEY = "jobs_public_url";
|
|
16
|
+
export declare const OWN_JOBS_ROUTE_TUNNEL_SCOPE_KEY = "cloudflare_own_jobs_tunnel";
|
|
17
|
+
export declare const OWN_JOBS_ROUTE_CONNECTOR_APP_SCOPE_KEY = "coolify_own_jobs_connector_app";
|
|
18
|
+
/**
|
|
19
|
+
* The only paths the public route serves: what a project's app calls (`/sync`, `/timers`, `/timers/<name>`,
|
|
20
|
+
* `/deliveries`). `/admin/*` is not on the internet; foundry reaches it on the Coolify network.
|
|
21
|
+
*/
|
|
22
|
+
export declare const JOBS_ENTRY_PATH_PATTERN = "^/(sync$|timers(/|$)|deliveries$)";
|
|
23
|
+
export declare const JOBS_ROUTE_CALL_TIMEOUT_MS = 60000;
|
|
24
|
+
/** The tunnel's name and the connector app's, one pair per company. */
|
|
25
|
+
export declare const jobsRouteNames: (target: Pick<ServiceTarget, "id">) => {
|
|
26
|
+
tunnel: string;
|
|
27
|
+
connector: string;
|
|
28
|
+
};
|
|
29
|
+
export interface JobsRoutePorts {
|
|
30
|
+
coolify: Pick<CoolifyPort, 'findOrCreateEnvironment' | 'listServers' | 'findApplicationByName' | 'getApplication' | 'createDockerfileApplication' | 'updateApplication' | 'bulkUpsertEnvs' | 'listEnvs' | 'deploy'>;
|
|
31
|
+
network: ServiceNetwork;
|
|
32
|
+
/** One outside call's limit in milliseconds; the connector step gets five times it. */
|
|
33
|
+
callTimeoutMs?: number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Makes the route in this order: tunnel, its ingress, the connector app, the CNAME, then the recorded URL.
|
|
37
|
+
* Each is found by name or content first, so a second run makes nothing twice. A failure leaves what was made
|
|
38
|
+
* (found again on the next run) and no recorded URL. Errors carry the call's label only.
|
|
39
|
+
*/
|
|
40
|
+
export declare function provisionJobsRoute(scope: StepScope, ports: JobsRoutePorts, target: ServiceTarget, input: {
|
|
41
|
+
hostname: string;
|
|
42
|
+
apex: string;
|
|
43
|
+
internalUrl: string;
|
|
44
|
+
projectUuid: string;
|
|
45
|
+
}): Promise<{
|
|
46
|
+
resourceId: string;
|
|
47
|
+
}>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { withTimeout } from './call-timeout.js';
|
|
2
|
+
import { ensureCloudflaredConnector } from './cloudflared-connector.js';
|
|
3
|
+
import { recordOwnDnsHostname } from './own-dns.js';
|
|
4
|
+
/**
|
|
5
|
+
* A company's jobs scheduler on a public hostname: a tunnel that dials the scheduler by its internal address,
|
|
6
|
+
* a connector app, and a proxied CNAME for `jobs.<domain>`. An entry runs on its own company's network and
|
|
7
|
+
* cannot dial the scheduler's container name, so `uses.jobs` gives it `https://<hostname>` once this is
|
|
8
|
+
* recorded. The owner company (hq) runs the scheduler every shared-services company registers on; a company
|
|
9
|
+
* that runs its own services publishes its own the same way. No Coolify domain and no Access app: the
|
|
10
|
+
* scheduler authenticates every caller itself (a project's sync key, the admin token), and a gate in front
|
|
11
|
+
* would stop the apps.
|
|
12
|
+
*
|
|
13
|
+
* One `coolify:own-jobs-route` step. It records {@link JOBS_PUBLIC_URL_SCOPE_KEY} last, so a run that fails
|
|
14
|
+
* halfway is planned again, finds the tunnel and connector by name, and no entry is given a dead URL.
|
|
15
|
+
*/
|
|
16
|
+
export const JOBS_PUBLIC_URL_SCOPE_KEY = 'jobs_public_url';
|
|
17
|
+
export const OWN_JOBS_ROUTE_TUNNEL_SCOPE_KEY = 'cloudflare_own_jobs_tunnel';
|
|
18
|
+
export const OWN_JOBS_ROUTE_CONNECTOR_APP_SCOPE_KEY = 'coolify_own_jobs_connector_app';
|
|
19
|
+
/**
|
|
20
|
+
* The only paths the public route serves: what a project's app calls (`/sync`, `/timers`, `/timers/<name>`,
|
|
21
|
+
* `/deliveries`). `/admin/*` is not on the internet; foundry reaches it on the Coolify network.
|
|
22
|
+
*/
|
|
23
|
+
export const JOBS_ENTRY_PATH_PATTERN = '^/(sync$|timers(/|$)|deliveries$)';
|
|
24
|
+
export const JOBS_ROUTE_CALL_TIMEOUT_MS = 60_000;
|
|
25
|
+
/** The tunnel's name and the connector app's, one pair per company. */
|
|
26
|
+
export const jobsRouteNames = (target) => ({
|
|
27
|
+
tunnel: `${target.id}-jobs-tunnel`,
|
|
28
|
+
connector: `${target.id}-jobs-cloudflared`,
|
|
29
|
+
});
|
|
30
|
+
/**
|
|
31
|
+
* Makes the route in this order: tunnel, its ingress, the connector app, the CNAME, then the recorded URL.
|
|
32
|
+
* Each is found by name or content first, so a second run makes nothing twice. A failure leaves what was made
|
|
33
|
+
* (found again on the next run) and no recorded URL. Errors carry the call's label only.
|
|
34
|
+
*/
|
|
35
|
+
export async function provisionJobsRoute(scope, ports, target, input) {
|
|
36
|
+
const ms = ports.callTimeoutMs ?? JOBS_ROUTE_CALL_TIMEOUT_MS;
|
|
37
|
+
const at = `own step coolify:own-jobs-route for ${target.id}`;
|
|
38
|
+
const names = jobsRouteNames(target);
|
|
39
|
+
const record = async (key, id) => {
|
|
40
|
+
scope.recordScopeId(key, id);
|
|
41
|
+
await scope.persist();
|
|
42
|
+
};
|
|
43
|
+
const zoneId = await withTimeout(`${at}: zone lookup`, ms, () => ports.network.zoneId(input.apex));
|
|
44
|
+
if (!zoneId)
|
|
45
|
+
throw new Error(`${at}: no Cloudflare zone for ${input.apex}`);
|
|
46
|
+
const tunnels = await ports.network.tunnels();
|
|
47
|
+
const dns = await ports.network.dns();
|
|
48
|
+
const { environment } = await withTimeout(`${at}: environment`, ms, () => ports.coolify.findOrCreateEnvironment(input.projectUuid, 'production'));
|
|
49
|
+
const tunnel = (await withTimeout(`${at}: tunnel lookup`, ms, () => tunnels.findTunnelByName(names.tunnel))) ??
|
|
50
|
+
(await withTimeout(`${at}: tunnel create`, ms, () => tunnels.createTunnel(names.tunnel)));
|
|
51
|
+
await record(OWN_JOBS_ROUTE_TUNNEL_SCOPE_KEY, tunnel.id);
|
|
52
|
+
await withTimeout(`${at}: tunnel ingress`, ms, () => tunnels.putTunnelConfig(tunnel.id, {
|
|
53
|
+
ingress: [
|
|
54
|
+
{ hostname: input.hostname, path: JOBS_ENTRY_PATH_PATTERN, service: input.internalUrl },
|
|
55
|
+
{ service: 'http_status:404' },
|
|
56
|
+
],
|
|
57
|
+
}));
|
|
58
|
+
await withTimeout(`${at}: connector app`, ms * 5, () => ensureCloudflaredConnector({
|
|
59
|
+
coolify: ports.coolify,
|
|
60
|
+
tunnels,
|
|
61
|
+
tunnelId: tunnel.id,
|
|
62
|
+
appName: names.connector,
|
|
63
|
+
projectUuid: input.projectUuid,
|
|
64
|
+
environment,
|
|
65
|
+
resolveServer: async () => {
|
|
66
|
+
const servers = await ports.coolify.listServers();
|
|
67
|
+
const server = servers.find((s) => s.is_usable && s.is_reachable) ?? servers[0];
|
|
68
|
+
if (!server)
|
|
69
|
+
throw new Error('no usable Coolify server is registered');
|
|
70
|
+
return server;
|
|
71
|
+
},
|
|
72
|
+
recordedConnectorAppId: scope.recordedIds()[OWN_JOBS_ROUTE_CONNECTOR_APP_SCOPE_KEY] ?? null,
|
|
73
|
+
recordAppId: (appId) => record(OWN_JOBS_ROUTE_CONNECTOR_APP_SCOPE_KEY, appId),
|
|
74
|
+
}));
|
|
75
|
+
const wanted = {
|
|
76
|
+
type: 'CNAME',
|
|
77
|
+
name: input.hostname,
|
|
78
|
+
content: `${tunnel.id}.cfargotunnel.com`,
|
|
79
|
+
proxied: true,
|
|
80
|
+
};
|
|
81
|
+
const [live] = await withTimeout(`${at}: dns lookup`, ms, () => dns.findDnsRecord(input.apex, input.hostname, 'CNAME'));
|
|
82
|
+
if (!live) {
|
|
83
|
+
await withTimeout(`${at}: dns create`, ms, () => dns.createDnsRecord(input.apex, wanted));
|
|
84
|
+
}
|
|
85
|
+
else if (live.content !== wanted.content) {
|
|
86
|
+
await withTimeout(`${at}: dns update`, ms, () => dns.updateDnsRecord(input.apex, live.id, wanted));
|
|
87
|
+
}
|
|
88
|
+
await recordOwnDnsHostname(scope, input.hostname);
|
|
89
|
+
await record(JOBS_PUBLIC_URL_SCOPE_KEY, `https://${input.hostname}`);
|
|
90
|
+
return { resourceId: tunnel.id };
|
|
91
|
+
}
|
|
@@ -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;
|
package/dist/plan/own-limits.js
CHANGED
|
@@ -27,6 +27,8 @@ export interface ServiceTarget {
|
|
|
27
27
|
authZitadel: string;
|
|
28
28
|
authLogin: string;
|
|
29
29
|
authBroker: string;
|
|
30
|
+
/** The Coolify Postgres database of the broker's customer workspaces. */
|
|
31
|
+
workspacesDb: string;
|
|
30
32
|
authTunnel: string;
|
|
31
33
|
authCloudflared: string;
|
|
32
34
|
mailApp: string;
|
|
@@ -61,6 +63,8 @@ export interface ServiceTarget {
|
|
|
61
63
|
instanceBearer: string;
|
|
62
64
|
authAdmin: string;
|
|
63
65
|
brokerMailKey: string;
|
|
66
|
+
/** 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. */
|
|
67
|
+
brokerWorkspacesDatabase: string;
|
|
64
68
|
mailAdminKey: string;
|
|
65
69
|
filesManagement: string;
|
|
66
70
|
filesIntrospection: string;
|
|
@@ -16,6 +16,7 @@ export function platformTarget() {
|
|
|
16
16
|
authZitadel: 'platform-auth-zitadel',
|
|
17
17
|
authLogin: 'platform-auth-login',
|
|
18
18
|
authBroker: 'platform-auth-broker',
|
|
19
|
+
workspacesDb: 'platform-workspaces-db',
|
|
19
20
|
authTunnel: 'platform-auth-tunnel',
|
|
20
21
|
authCloudflared: 'platform-auth-cloudflared',
|
|
21
22
|
mailApp: 'platform-mail-stalwart',
|
|
@@ -47,6 +48,7 @@ export function platformTarget() {
|
|
|
47
48
|
instanceBearer: 'zitadel:platform:instanceBearer',
|
|
48
49
|
authAdmin: 'zitadel:platform:admin',
|
|
49
50
|
brokerMailKey: 'lettermint:platform:apiToken',
|
|
51
|
+
brokerWorkspacesDatabase: 'auth:platform:workspacesDatabaseUrl',
|
|
50
52
|
mailAdminKey: 'stalwart:platform:admin',
|
|
51
53
|
filesManagement: 'files:platform:managementKey',
|
|
52
54
|
filesIntrospection: 'zitadel:platform:files:introspection',
|
|
@@ -71,6 +73,7 @@ export function orgTarget(org) {
|
|
|
71
73
|
authZitadel: `${s}-auth-zitadel`,
|
|
72
74
|
authLogin: `${s}-auth-login`,
|
|
73
75
|
authBroker: `${s}-auth-broker`,
|
|
76
|
+
workspacesDb: `${s}-workspaces-db`,
|
|
74
77
|
authTunnel: `${s}-auth-tunnel`,
|
|
75
78
|
authCloudflared: `${s}-auth-cloudflared`,
|
|
76
79
|
mailApp: `${s}-mail-stalwart`,
|
|
@@ -102,6 +105,7 @@ export function orgTarget(org) {
|
|
|
102
105
|
instanceBearer: `zitadel:org:${s}:instanceBearer`,
|
|
103
106
|
authAdmin: `zitadel:org:${s}:admin`,
|
|
104
107
|
brokerMailKey: `lettermint:org:${s}:apiToken`,
|
|
108
|
+
brokerWorkspacesDatabase: `auth:org:${s}:workspacesDatabaseUrl`,
|
|
105
109
|
mailAdminKey: `stalwart:org:${s}:admin`,
|
|
106
110
|
filesManagement: `files:org:${s}:managementKey`,
|
|
107
111
|
filesIntrospection: `zitadel:org:${s}:files:introspection`,
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { DestroyProgress, PlanApply } from '../apply.js';
|
|
2
|
+
import type { SealResult } from '../audit/seal.js';
|
|
3
|
+
import { type AuditSealingOptions } from '../audit/tick.js';
|
|
2
4
|
import type { DeclaredOrg, HostingEntry, OrgId } from '../org.js';
|
|
3
5
|
import type { Plan, PlanStep } from '../plan.js';
|
|
4
6
|
import type { DeclaredPlatform } from '../platform.js';
|
|
@@ -12,6 +14,7 @@ import { type MoveGatePorts } from './backup-gate.js';
|
|
|
12
14
|
import { type BlueGreenServer, type BlueGreenSubject } from './blue-green.js';
|
|
13
15
|
import { type UnconfirmedUpgrade } from './confirm-upgrade.js';
|
|
14
16
|
import { type StepAction } from './diff.js';
|
|
17
|
+
import type { GithubDeployKeysPort } from './github-deploy-keys.js';
|
|
15
18
|
import { type LiveReaders, type LiveResource, type PlatformLiveReaders } from './live.js';
|
|
16
19
|
import { type LoginHostDiscoveryPort } from './login-host.js';
|
|
17
20
|
import { type OrgPostgresMigrationConfirmation } from './org-postgres-migrate.js';
|
|
@@ -249,6 +252,14 @@ export interface StepExecutor {
|
|
|
249
252
|
* without it leaves the option as the answer.
|
|
250
253
|
*/
|
|
251
254
|
ownBrokerPort?(org: DeclaredOrg, recordedIds: Readonly<Record<string, string>>): Promise<boolean>;
|
|
255
|
+
/**
|
|
256
|
+
* Whether the host can make the broker's workspaces database: it can hold the URL and the broker and the held keys
|
|
257
|
+
* were just read and have none. Read only for an org whose entry declares workspaces and only while
|
|
258
|
+
* {@link brokerHasWorkspacesDatabase} is false. Optional: without it the plan only reports the entry blocked.
|
|
259
|
+
*/
|
|
260
|
+
canProvisionWorkspacesDatabase?(org: DeclaredOrg, recordedIds: Readonly<Record<string, string>>): Promise<boolean>;
|
|
261
|
+
/** Whether the broker this org's entries are written to has `WORKSPACES_DATABASE_URL`. Read only for an org whose entry declares workspaces. */
|
|
262
|
+
brokerHasWorkspacesDatabase?(org: DeclaredOrg, recordedIds: Readonly<Record<string, string>>): Promise<boolean>;
|
|
252
263
|
/**
|
|
253
264
|
* Every secret this executor minted or read so far (peer password, bridge
|
|
254
265
|
* secret, invite code and link). The host's run adds them to its redactor
|
|
@@ -462,6 +473,8 @@ export interface FoundryApplyOptions {
|
|
|
462
473
|
typed: string;
|
|
463
474
|
/** The removed entry's gate keys, read by the caller from the company before the change. */
|
|
464
475
|
gates?: readonly string[];
|
|
476
|
+
/** The paths the entry's private pages had before the change, for an entry that still exists: their `pp-`/`wp-` gate deletes pass with its confirmation. */
|
|
477
|
+
privatePages?: readonly string[];
|
|
465
478
|
};
|
|
466
479
|
}
|
|
467
480
|
export interface FoundryPlanApplyOptions {
|
|
@@ -550,6 +563,8 @@ export interface FoundryPlanApplyOptions {
|
|
|
550
563
|
workerApps?: boolean;
|
|
551
564
|
/** ADR 0002: the keys custody Worker address, when the executor has a `KeysCustodyPort`. A `keysCustody` secret is blocked without it. */
|
|
552
565
|
workerAppKeysUrl?: string;
|
|
566
|
+
/** F7: the keys service's registrar address, when the executor has a `KeysRegistrarPort`. */
|
|
567
|
+
keysRegistrarUrl?: string;
|
|
553
568
|
/** C2: true when the host's executor was built with a `WorkerAppDatabasePort`. */
|
|
554
569
|
workerAppDatabase?: boolean;
|
|
555
570
|
/** ADR 0002: the platform's Cloudflare account id. A container image outside its registry is blocked. */
|
|
@@ -604,6 +619,16 @@ export interface FoundryPlanApplyOptions {
|
|
|
604
619
|
* the release runner. Without it no release runs, in an apply or in {@link FoundryPlanApply.runReleaseTick}.
|
|
605
620
|
*/
|
|
606
621
|
releaseStore?: ReleaseStore;
|
|
622
|
+
/**
|
|
623
|
+
* F12: the GitHub deploy-key client (`createGithubDeployKeys`), to ask which deploy keys an entry's repo has.
|
|
624
|
+
* Without it an entry with `uses.repo` is reported blocked and nothing else changes.
|
|
625
|
+
*/
|
|
626
|
+
githubDeployKeys?: GithubDeployKeysPort;
|
|
627
|
+
/**
|
|
628
|
+
* `uses.audit` (foundry#929): what {@link FoundryPlanApply.runAuditSealTick} seals with. Without it the tick
|
|
629
|
+
* seals nothing and logs that it was not given.
|
|
630
|
+
*/
|
|
631
|
+
auditSealing?: AuditSealingOptions;
|
|
607
632
|
/** Overridable for tests; defaults to `() => new Date()`. */
|
|
608
633
|
now?: () => Date;
|
|
609
634
|
/** #441: how long a mutation lease lasts before another caller may take it over. Defaults to 30 minutes. */
|
|
@@ -693,6 +718,7 @@ export declare class FoundryPlanApply implements PlanApply {
|
|
|
693
718
|
private readonly edgeWorkers;
|
|
694
719
|
private readonly workerApps;
|
|
695
720
|
private readonly workerAppKeysUrl;
|
|
721
|
+
private readonly keysRegistrarUrl;
|
|
696
722
|
private readonly workerAppDatabase;
|
|
697
723
|
private readonly workerAppRegistryAccountId;
|
|
698
724
|
private readonly ownerInviteMail;
|
|
@@ -709,6 +735,8 @@ export declare class FoundryPlanApply implements PlanApply {
|
|
|
709
735
|
private readonly platformDeps;
|
|
710
736
|
private readonly runner;
|
|
711
737
|
private readonly releaseStore;
|
|
738
|
+
private readonly githubDeployKeys;
|
|
739
|
+
private readonly auditSealing;
|
|
712
740
|
constructor(options: FoundryPlanApplyOptions);
|
|
713
741
|
plan(id: OrgId): Promise<Plan>;
|
|
714
742
|
/**
|
|
@@ -872,6 +900,13 @@ export declare class FoundryPlanApply implements PlanApply {
|
|
|
872
900
|
target: string;
|
|
873
901
|
error?: string;
|
|
874
902
|
}>>;
|
|
903
|
+
/**
|
|
904
|
+
* The recurring audit sealing run (foundry#929): seals every ledger registered by an `audit:ledger` step with the
|
|
905
|
+
* platform signer and has the timestamp authority stamp the new checkpoints. Foundry has no scheduler, so the host
|
|
906
|
+
* calls this every `AUDIT_SEAL_EVERY_MINUTES` (15). One ledger failing never stops the others; each result and a
|
|
907
|
+
* log line say what happened and why when nothing did. It writes nothing to the org's records.
|
|
908
|
+
*/
|
|
909
|
+
runAuditSealTick(): Promise<SealResult[]>;
|
|
875
910
|
private runOrgReleaseTick;
|
|
876
911
|
/** An own org with calls on: the host's answer about its slot's ports. A failure or a slow answer is "not open". */
|
|
877
912
|
private readSfuPortsOpen;
|
|
@@ -891,6 +926,10 @@ export declare class FoundryPlanApply implements PlanApply {
|
|
|
891
926
|
* org step that needs one is blocked.
|
|
892
927
|
*/
|
|
893
928
|
private orgPlatformReadiness;
|
|
929
|
+
/** F12: asks GitHub about the repo of each entry with a deploy key to make, check or remove. Never throws; a failure is a plain line. */
|
|
930
|
+
private repoKeysReadiness;
|
|
931
|
+
/** Which refs of the platform GitHub App hold no value now. A resolver that throws counts as not held; only refs are kept. */
|
|
932
|
+
private githubAppKeysReadiness;
|
|
894
933
|
/**
|
|
895
934
|
* The org's `heldSecret` envs: the refs that hold a value now, which of those differ from the live
|
|
896
935
|
* production env, which could not be read, and which fit no ref. A resolver that throws counts as
|