@wtfalch/foundry 0.17.1 → 0.19.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.d.ts +11 -2
- package/dist/bootstrap/declare.js +20 -11
- package/dist/index.d.ts +4 -2
- package/dist/index.js +2 -1
- package/dist/migrations/0045_names_to_edge.sql +107 -0
- package/dist/org.d.ts +17 -17
- package/dist/org.js +48 -58
- package/dist/plan/agora.d.ts +25 -0
- package/dist/plan/agora.js +25 -0
- package/dist/plan/boule.d.ts +8 -0
- package/dist/plan/boule.js +8 -0
- package/dist/plan/diff.d.ts +3 -1
- package/dist/plan/diff.js +115 -52
- package/dist/plan/entry-edge-key.d.ts +79 -0
- package/dist/plan/{entry-names-key.js → entry-edge-key.js} +90 -66
- package/dist/plan/executor.d.ts +87 -42
- package/dist/plan/executor.js +277 -120
- package/dist/plan/index.d.ts +2 -2
- package/dist/plan/index.js +1 -1
- package/dist/plan/live.d.ts +3 -3
- package/dist/plan/login-host.d.ts +15 -11
- package/dist/plan/login-host.js +16 -16
- package/dist/plan/plan-apply.d.ts +6 -5
- package/dist/plan/plan-apply.js +40 -38
- package/dist/plan/platform-diff.d.ts +22 -22
- package/dist/plan/platform-diff.js +45 -45
- package/dist/plan/platform-org-ports.d.ts +1 -0
- package/dist/plan/platform-org-ports.js +3 -0
- package/dist/plan/platform-org.d.ts +8 -8
- package/dist/plan/platform-org.js +7 -7
- package/dist/plan/platform-providers.d.ts +17 -16
- package/dist/plan/platform-providers.js +136 -118
- package/dist/plan/providers-readers.d.ts +34 -11
- package/dist/plan/providers-readers.js +72 -15
- package/dist/plan/step-system.js +1 -1
- package/dist/plan/{edge-worker.d.ts → worker.d.ts} +53 -33
- package/dist/plan/{edge-worker.js → worker.js} +92 -54
- package/dist/platform.d.ts +3 -3
- package/dist/platform.js +1 -1
- package/dist/store/postgres.js +7 -7
- package/dist/store/schema.d.ts +6 -6
- package/dist/store/schema.js +4 -4
- package/package.json +1 -1
- package/dist/plan/entry-names-key.d.ts +0 -73
|
@@ -698,32 +698,32 @@ export const PLATFORM_FILES_MANAGEMENT_UNDELIVERED_SCOPE_KEY = 'files_management
|
|
|
698
698
|
export const PLATFORM_FILES_APPLIED_HOSTNAME_SCOPE_KEY = 'files_applied_hostname';
|
|
699
699
|
export const PLATFORM_FILES_APPLIED_GIT_REF_SCOPE_KEY = 'files_applied_git_ref';
|
|
700
700
|
/**
|
|
701
|
-
* #478: the resource key `
|
|
701
|
+
* #478: the resource key `planEdge` plans under and
|
|
702
702
|
* `FoundryPlatformStepExecutor` dispatches on -- the {@link PLATFORM_FILES_KEY}
|
|
703
|
-
* shape for the
|
|
704
|
-
* records the app's uuid under `coolify:app:platform:
|
|
705
|
-
* succeeds, which is what stops `
|
|
703
|
+
* shape for the edge host (https://github.com/wtfalch/edge). `runPlatformSteps`
|
|
704
|
+
* records the app's uuid under `coolify:app:platform:edge` once the step
|
|
705
|
+
* succeeds, which is what stops `planEdge` planning it again.
|
|
706
706
|
*/
|
|
707
|
-
export const
|
|
707
|
+
export const PLATFORM_EDGE_KEY = 'platform:edge';
|
|
708
708
|
/**
|
|
709
|
-
* #478: the held-key ref the
|
|
709
|
+
* #478: the held-key ref the edge platform secret's full value is
|
|
710
710
|
* delivered under, once, by {@link FoundryStepExecutorPorts.deliverSecret} --
|
|
711
711
|
* never the host's app env, which gets only its SHA-256 hash
|
|
712
|
-
* (`
|
|
713
|
-
* secret back from this ref to call
|
|
712
|
+
* (`EDGE_PLATFORM_KEY_HASH`). Archon's own `EdgePort` reads the full
|
|
713
|
+
* secret back from this ref to call edge' `mintKey`/`revokeKey`/
|
|
714
714
|
* `setReservedLabels`.
|
|
715
715
|
*/
|
|
716
|
-
export const
|
|
716
|
+
export const PLATFORM_EDGE_KEY_REF = 'edge:platform:key';
|
|
717
717
|
/**
|
|
718
|
-
* #478: the held-key ref for
|
|
718
|
+
* #478: the held-key ref for edge' own Cloudflare API token (tunnel edit +
|
|
719
719
|
* DNS edit on one account) -- the issue's own "never Archon's token": this
|
|
720
|
-
* is a narrower,
|
|
721
|
-
* separately from whatever broader token `
|
|
720
|
+
* is a narrower, edge-only credential a human provisions and holds
|
|
721
|
+
* separately from whatever broader token `PlatformEdgeDeps.cloudflare`
|
|
722
722
|
* uses to stand the host itself up on Coolify.
|
|
723
723
|
*/
|
|
724
|
-
export const
|
|
724
|
+
export const PLATFORM_EDGE_CLOUDFLARE_TOKEN_REF = 'cloudflare:edge:apiToken';
|
|
725
725
|
/**
|
|
726
|
-
* #478: scope keys (colon-free) for the
|
|
726
|
+
* #478: scope keys (colon-free) for the edge platform key, the
|
|
727
727
|
* {@link PLATFORM_FILES_MANAGEMENT_UNDELIVERED_SCOPE_KEY} pattern once:
|
|
728
728
|
* the undelivered id is recorded and persisted the moment the secret is
|
|
729
729
|
* minted, before delivery; the guard is recorded only once it was delivered
|
|
@@ -731,16 +731,16 @@ export const PLATFORM_NAMES_CLOUDFLARE_TOKEN_REF = 'cloudflare:names:apiToken';
|
|
|
731
731
|
* means the secret is lost (never kept past that point): the planner
|
|
732
732
|
* reports it and never mints again. Neither key holds the secret itself.
|
|
733
733
|
*/
|
|
734
|
-
export const
|
|
735
|
-
export const
|
|
734
|
+
export const PLATFORM_EDGE_PLATFORM_KEY_UNDELIVERED_SCOPE_KEY = 'edge_platform_key_undelivered';
|
|
735
|
+
export const PLATFORM_EDGE_PLATFORM_KEY_SET_SCOPE_KEY = 'edge_platform_key_set';
|
|
736
736
|
/**
|
|
737
737
|
* #478: the hostname and `gitRef` the last successful names run applied.
|
|
738
|
-
* `
|
|
738
|
+
* `planEdge` plans the step again when either differs from the declared
|
|
739
739
|
* value; a re-run updates `git_branch` and the runner env and redeploys,
|
|
740
740
|
* and mints no new platform key (the guards above hold).
|
|
741
741
|
*/
|
|
742
|
-
export const
|
|
743
|
-
export const
|
|
742
|
+
export const PLATFORM_EDGE_APPLIED_HOSTNAME_SCOPE_KEY = 'edge_applied_hostname';
|
|
743
|
+
export const PLATFORM_EDGE_APPLIED_GIT_REF_SCOPE_KEY = 'edge_applied_git_ref';
|
|
744
744
|
/**
|
|
745
745
|
* #167: the platform's own diff engine, mirroring `diff.ts`'s
|
|
746
746
|
* `computePlanDetail` for the one shared {@link DeclaredPlatform} record
|
|
@@ -755,7 +755,7 @@ export const PLATFORM_NAMES_APPLIED_GIT_REF_SCOPE_KEY = 'names_applied_git_ref';
|
|
|
755
755
|
* Scope for #167: the frame plus one step, {@link planApexZone}. #173 adds
|
|
756
756
|
* {@link planJobs}, #170 adds {@link planSfu}, #168 adds {@link planAuth},
|
|
757
757
|
* #169 adds {@link planMail}, #174 adds {@link planIntegrations}, #171 adds
|
|
758
|
-
* {@link planFiles}, #172 adds {@link planAi}, #478 adds {@link
|
|
758
|
+
* {@link planFiles}, #172 adds {@link planAi}, #478 adds {@link planEdge}
|
|
759
759
|
* and #175 adds
|
|
760
760
|
* {@link planArchon}, the same way #19 shipped org plan/apply against
|
|
761
761
|
* three singletons before #27, #71, #80 and the rest added the remaining
|
|
@@ -805,10 +805,10 @@ export function planPlatform(platform, live, options = {}) {
|
|
|
805
805
|
const ai = planAi(platform, recordedIds, blocked.ai);
|
|
806
806
|
if (ai.step)
|
|
807
807
|
plannedSteps.push(ai.step);
|
|
808
|
-
// #478: independent of auth and files -- the
|
|
809
|
-
const
|
|
810
|
-
if (
|
|
811
|
-
plannedSteps.push(
|
|
808
|
+
// #478: independent of auth and files -- the edge host needs neither.
|
|
809
|
+
const edge = planEdge(platform, recordedIds, blocked.edge);
|
|
810
|
+
if (edge.step)
|
|
811
|
+
plannedSteps.push(edge.step);
|
|
812
812
|
// #175: last, on purpose. Archon's sign-in client lives in the sign-in
|
|
813
813
|
// `planAuth` builds, and its env reads what every other service's step
|
|
814
814
|
// records or delivers, so in one apply it runs after all of them.
|
|
@@ -842,9 +842,9 @@ export function planPlatform(platform, live, options = {}) {
|
|
|
842
842
|
integrations.blocked
|
|
843
843
|
? null
|
|
844
844
|
: versionChangeLine('integrations', recordedIds[PLATFORM_INTEGRATIONS_APPLIED_GITREF_SCOPE_KEY], platform.integrations.gitRef, 'git ref, redeploy'),
|
|
845
|
-
|
|
845
|
+
edge.blocked
|
|
846
846
|
? null
|
|
847
|
-
: versionChangeLine('
|
|
847
|
+
: versionChangeLine('edge', recordedIds[PLATFORM_EDGE_APPLIED_GIT_REF_SCOPE_KEY], platform.edge.gitRef, 'git ref, redeploy'),
|
|
848
848
|
auth.blocked
|
|
849
849
|
? null
|
|
850
850
|
: versionNotAppliedLine('zitadel', Boolean(recordedIds[resourceKey('coolify', 'app', PLATFORM_AUTH_KEY)]), recordedIds[PLATFORM_AUTH_ZITADEL_APPLIED_IMAGE_TAG_SCOPE_KEY], VERSIONS.zitadel),
|
|
@@ -876,8 +876,8 @@ export function planPlatform(platform, live, options = {}) {
|
|
|
876
876
|
unmanaged.push({ description: files.blocked, key: 'files' });
|
|
877
877
|
if (ai.blocked)
|
|
878
878
|
unmanaged.push({ description: ai.blocked, key: 'ai' });
|
|
879
|
-
if (
|
|
880
|
-
unmanaged.push({ description:
|
|
879
|
+
if (edge.blocked)
|
|
880
|
+
unmanaged.push({ description: edge.blocked, key: 'edge' });
|
|
881
881
|
if (archon.blocked)
|
|
882
882
|
unmanaged.push({ description: archon.blocked, key: 'archon' });
|
|
883
883
|
// #398: the old integrations key stays pinned until Archon holds the new
|
|
@@ -1747,13 +1747,13 @@ function planFiles(platform, recordedIds, executorBlocked, authReady, drifted) {
|
|
|
1747
1747
|
};
|
|
1748
1748
|
}
|
|
1749
1749
|
/**
|
|
1750
|
-
* #478: the
|
|
1750
|
+
* #478: the edge host (https://github.com/wtfalch/edge) -- its database
|
|
1751
1751
|
* (owner and runtime roles), its one Coolify app (no separate migrate app:
|
|
1752
1752
|
* the Dockerfile migrates inside the runner's own entrypoint, see
|
|
1753
|
-
* `
|
|
1753
|
+
* `provisionPlatformEdge`'s own doc comment), foundry's own platform key
|
|
1754
1754
|
* (minted once, its hash alone written to the runner) and a Cloudflare
|
|
1755
|
-
* tunnel + DNS record at `platform.
|
|
1756
|
-
* keyed {@link
|
|
1755
|
+
* tunnel + DNS record at `platform.edge.hostname`. One `coolify:app` step
|
|
1756
|
+
* keyed {@link PLATFORM_EDGE_KEY}, the {@link planFiles} bundling shape,
|
|
1757
1757
|
* public like {@link planMail}/{@link planAuth} rather than internal-only
|
|
1758
1758
|
* like {@link planFiles}/{@link planAi}.
|
|
1759
1759
|
*
|
|
@@ -1764,23 +1764,23 @@ function planFiles(platform, recordedIds, executorBlocked, authReady, drifted) {
|
|
|
1764
1764
|
*
|
|
1765
1765
|
* Blocked, not planned: a platform key minted but never delivered (lost,
|
|
1766
1766
|
* `ServiceError`-free the way {@link planFiles}'s own management-key check
|
|
1767
|
-
* is), or `executorBlocked` -- the host built no
|
|
1767
|
+
* is), or `executorBlocked` -- the host built no edge dependencies, or
|
|
1768
1768
|
* (CLAUDE.md's "Human steps") no Cloudflare API token is held yet under
|
|
1769
|
-
* {@link
|
|
1769
|
+
* {@link PLATFORM_EDGE_CLOUDFLARE_TOKEN_REF}.
|
|
1770
1770
|
*/
|
|
1771
|
-
function
|
|
1772
|
-
if (!platform.
|
|
1771
|
+
function planEdge(platform, recordedIds, executorBlocked) {
|
|
1772
|
+
if (!platform.edge.enabled)
|
|
1773
1773
|
return { step: null, blocked: null };
|
|
1774
|
-
const undelivered = recordedIds[
|
|
1775
|
-
if (undelivered && !recordedIds[
|
|
1774
|
+
const undelivered = recordedIds[PLATFORM_EDGE_PLATFORM_KEY_UNDELIVERED_SCOPE_KEY];
|
|
1775
|
+
if (undelivered && !recordedIds[PLATFORM_EDGE_PLATFORM_KEY_SET_SCOPE_KEY]) {
|
|
1776
1776
|
return {
|
|
1777
1777
|
step: null,
|
|
1778
|
-
blocked: `the
|
|
1778
|
+
blocked: `the edge host is blocked, not planned: its platform key (${undelivered}) was minted but never delivered and cannot be shown again; remove the recorded key ${PLATFORM_EDGE_PLATFORM_KEY_UNDELIVERED_SCOPE_KEY} to mint a new one`,
|
|
1779
1779
|
};
|
|
1780
1780
|
}
|
|
1781
|
-
const applied = Boolean(recordedIds[resourceKey('coolify', 'app',
|
|
1782
|
-
recordedIds[
|
|
1783
|
-
recordedIds[
|
|
1781
|
+
const applied = Boolean(recordedIds[resourceKey('coolify', 'app', PLATFORM_EDGE_KEY)]) &&
|
|
1782
|
+
recordedIds[PLATFORM_EDGE_APPLIED_HOSTNAME_SCOPE_KEY] === platform.edge.hostname &&
|
|
1783
|
+
recordedIds[PLATFORM_EDGE_APPLIED_GIT_REF_SCOPE_KEY] === platform.edge.gitRef;
|
|
1784
1784
|
if (applied)
|
|
1785
1785
|
return { step: null, blocked: null };
|
|
1786
1786
|
if (executorBlocked)
|
|
@@ -1790,11 +1790,11 @@ function planNames(platform, recordedIds, executorBlocked) {
|
|
|
1790
1790
|
action: {
|
|
1791
1791
|
system: 'coolify',
|
|
1792
1792
|
kind: 'app',
|
|
1793
|
-
key:
|
|
1794
|
-
desired: { hostname: platform.
|
|
1793
|
+
key: PLATFORM_EDGE_KEY,
|
|
1794
|
+
desired: { hostname: platform.edge.hostname, gitRef: platform.edge.gitRef },
|
|
1795
1795
|
},
|
|
1796
1796
|
step: {
|
|
1797
|
-
description: `provision the
|
|
1797
|
+
description: `provision the edge host at ${platform.edge.hostname}, with its own database and foundry's platform key`,
|
|
1798
1798
|
destroys: false,
|
|
1799
1799
|
migrates: false,
|
|
1800
1800
|
},
|
|
@@ -47,6 +47,9 @@ export class PlatformOrgPorts {
|
|
|
47
47
|
const token = await this.held('jobs', JOBS_ADMIN_TOKEN_REF);
|
|
48
48
|
return new HttpJobsSchedulerClient(`https://${platform.jobs.hostname}`, token, this.options.fetch);
|
|
49
49
|
}
|
|
50
|
+
async authHostname() {
|
|
51
|
+
return (await this.options.store.get())?.auth.hostname ?? null;
|
|
52
|
+
}
|
|
50
53
|
async recorded(service, scopeKey) {
|
|
51
54
|
const value = (await this.options.store.getServiceIds())[scopeKey];
|
|
52
55
|
if (!value) {
|
|
@@ -88,16 +88,16 @@ export interface OrgPlatformReadiness {
|
|
|
88
88
|
/** The jobs scheduler (#173) is enabled, applied, and its admin token is held. */
|
|
89
89
|
jobsReady: boolean;
|
|
90
90
|
/**
|
|
91
|
-
* #317 (F7) and #478: the
|
|
92
|
-
* `coolify:app` deploy step has applied (`
|
|
93
|
-
* the host's executor was built with
|
|
91
|
+
* #317 (F7) and #478: the edge service is declared `enabled`, its own
|
|
92
|
+
* `coolify:app` deploy step has applied (`provisionPlatformEdge`), and
|
|
93
|
+
* the host's executor was built with an `EdgePort` -- the same
|
|
94
94
|
* `filesReady`/`aiReady` "declared and applied" shape now that #478 built
|
|
95
95
|
* the deploy step #317 shipped ahead of (this field's own doc comment
|
|
96
96
|
* used to say there was none; N1 is no longer deployed by hand).
|
|
97
97
|
*/
|
|
98
|
-
|
|
99
|
-
/** `https://<platform.
|
|
100
|
-
|
|
98
|
+
edgeReady: boolean;
|
|
99
|
+
/** `https://<platform.edge.hostname>`, which an entry with `uses.edge` gets as `EDGE_URL`. `null` until `edgeReady`. */
|
|
100
|
+
edgeUrl?: string | null;
|
|
101
101
|
/**
|
|
102
102
|
* The ref of a platform key an applied service needs that is not held yet,
|
|
103
103
|
* `null` when it is held or when the service is not applied. A ref only,
|
|
@@ -166,8 +166,8 @@ export declare function orgPlatformReadinessFrom(platform: DeclaredPlatform, pla
|
|
|
166
166
|
heldRefs?: ReadonlySet<string>,
|
|
167
167
|
/** True when the host wired no held-keys reader ({@link OrgPlatformReadiness.noKeyReader}). */
|
|
168
168
|
noKeyReader?: boolean,
|
|
169
|
-
/** #317: true when the host's executor was built with
|
|
170
|
-
|
|
169
|
+
/** #317: true when the host's executor was built with an `EdgePort` ({@link OrgPlatformReadiness.edgeReady}). */
|
|
170
|
+
edgePortPresent?: boolean): OrgPlatformReadiness;
|
|
171
171
|
/**
|
|
172
172
|
* #177: {@link AgoraHostValues} derived from the same platform state, rather
|
|
173
173
|
* than requiring a bespoke host-supplied `AgoraHostPort` for values the
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { JOBS_SCHEDULER_KEY, PLATFORM_AI_INTERNAL_URL_SCOPE_KEY, PLATFORM_AI_KEY, PLATFORM_AUTH_KEY, PLATFORM_FILES_INTERNAL_URL_SCOPE_KEY, PLATFORM_FILES_KEY, PLATFORM_MAIL_INTERNAL_URL_SCOPE_KEY, PLATFORM_MAIL_KEY, PLATFORM_MAIL_ZITADEL_PROJECT_SCOPE_KEY,
|
|
1
|
+
import { JOBS_SCHEDULER_KEY, PLATFORM_AI_INTERNAL_URL_SCOPE_KEY, PLATFORM_AI_KEY, PLATFORM_AUTH_KEY, PLATFORM_EDGE_KEY, PLATFORM_FILES_INTERNAL_URL_SCOPE_KEY, PLATFORM_FILES_KEY, PLATFORM_MAIL_INTERNAL_URL_SCOPE_KEY, PLATFORM_MAIL_KEY, PLATFORM_MAIL_ZITADEL_PROJECT_SCOPE_KEY, platformMailMxHost, } from './platform-diff.js';
|
|
2
2
|
import { FILES_MANAGEMENT_KEY_REF, JOBS_ADMIN_TOKEN_REF, PLATFORM_AI_MANAGEMENT_KEY_REF, } from './platform-providers.js';
|
|
3
3
|
/**
|
|
4
4
|
* #443: the no-domain company mail apex -- `<slug>.oikos.host`, the same
|
|
@@ -32,7 +32,7 @@ export const DEFAULT_ORG_PLATFORM_READINESS = {
|
|
|
32
32
|
callsReady: false,
|
|
33
33
|
authReady: false,
|
|
34
34
|
jobsReady: false,
|
|
35
|
-
|
|
35
|
+
edgeReady: false,
|
|
36
36
|
missingKeys: { files: null, ai: null, jobs: null },
|
|
37
37
|
};
|
|
38
38
|
/** The held-key refs an org step needs from the platform, one per service. */
|
|
@@ -80,13 +80,13 @@ export function orgPlatformReadinessFrom(platform, platformRecordedIds,
|
|
|
80
80
|
heldRefs,
|
|
81
81
|
/** True when the host wired no held-keys reader ({@link OrgPlatformReadiness.noKeyReader}). */
|
|
82
82
|
noKeyReader = false,
|
|
83
|
-
/** #317: true when the host's executor was built with
|
|
84
|
-
|
|
83
|
+
/** #317: true when the host's executor was built with an `EdgePort` ({@link OrgPlatformReadiness.edgeReady}). */
|
|
84
|
+
edgePortPresent = false) {
|
|
85
85
|
const mailApplied = platform.mail.enabled && coolifyAppApplied(platformRecordedIds, PLATFORM_MAIL_KEY);
|
|
86
86
|
const filesApplied = platform.files.enabled && coolifyAppApplied(platformRecordedIds, PLATFORM_FILES_KEY);
|
|
87
87
|
const aiApplied = platform.ai.enabled && coolifyAppApplied(platformRecordedIds, PLATFORM_AI_KEY);
|
|
88
88
|
const jobsApplied = platform.jobs.enabled && Boolean(platformRecordedIds[`coolify:jobs:${JOBS_SCHEDULER_KEY}`]);
|
|
89
|
-
const
|
|
89
|
+
const edgeApplied = platform.edge.enabled && coolifyAppApplied(platformRecordedIds, PLATFORM_EDGE_KEY);
|
|
90
90
|
const missing = (applied, ref) => applied && heldRefs !== undefined && !heldRefs.has(ref) ? ref : null;
|
|
91
91
|
const missingKeys = {
|
|
92
92
|
files: missing(filesApplied, ORG_PLATFORM_KEY_REFS.files),
|
|
@@ -113,8 +113,8 @@ namesPortPresent = false) {
|
|
|
113
113
|
callsReady: platform.sfu.enabled,
|
|
114
114
|
authReady: platform.auth.enabled && coolifyAppApplied(platformRecordedIds, PLATFORM_AUTH_KEY),
|
|
115
115
|
jobsReady: jobsApplied && missingKeys.jobs === null,
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
edgeReady: edgeApplied && edgePortPresent,
|
|
117
|
+
edgeUrl: edgeApplied && edgePortPresent ? `https://${platform.edge.hostname}` : null,
|
|
118
118
|
missingKeys,
|
|
119
119
|
...(noKeyReader ? { noKeyReader } : {}),
|
|
120
120
|
};
|
|
@@ -132,11 +132,11 @@ export interface FoundryPlatformStepExecutorOptions {
|
|
|
132
132
|
*/
|
|
133
133
|
ai?: PlatformAiDeps;
|
|
134
134
|
/**
|
|
135
|
-
* #478: dependencies for `coolify:app` keyed {@link
|
|
135
|
+
* #478: dependencies for `coolify:app` keyed {@link PLATFORM_EDGE_KEY} --
|
|
136
136
|
* optional for the same reason as `auth`; a host that declares
|
|
137
|
-
* `platform.
|
|
137
|
+
* `platform.edge.enabled` without it gets the step reported as blocked.
|
|
138
138
|
*/
|
|
139
|
-
|
|
139
|
+
edge?: PlatformEdgeDeps;
|
|
140
140
|
/**
|
|
141
141
|
* #175: dependencies for `coolify:app` keyed {@link PLATFORM_ARCHON_KEY}
|
|
142
142
|
* -- optional for the same reason as {@link auth}.
|
|
@@ -203,8 +203,8 @@ export interface FoundryPlatformStepExecutorOptions {
|
|
|
203
203
|
* #172 adds `coolify:app` keyed {@link PLATFORM_AI_KEY}, the platform's AI
|
|
204
204
|
* host ({@link provisionPlatformAi}), once a host passes {@link PlatformAiDeps}.
|
|
205
205
|
*
|
|
206
|
-
* #478 adds `coolify:app` keyed {@link
|
|
207
|
-
* ({@link
|
|
206
|
+
* #478 adds `coolify:app` keyed {@link PLATFORM_EDGE_KEY}, the edge host
|
|
207
|
+
* ({@link provisionPlatformEdge}), once a host passes {@link PlatformEdgeDeps}.
|
|
208
208
|
*
|
|
209
209
|
* #175 adds `coolify:app` keyed {@link PLATFORM_ARCHON_KEY}, Archon itself
|
|
210
210
|
* ({@link provisionPlatformArchon}), once a host passes
|
|
@@ -225,7 +225,7 @@ export declare class FoundryPlatformStepExecutor implements PlatformStepExecutor
|
|
|
225
225
|
private readonly integrations?;
|
|
226
226
|
private readonly files?;
|
|
227
227
|
private readonly ai?;
|
|
228
|
-
private readonly
|
|
228
|
+
private readonly edge?;
|
|
229
229
|
private readonly archon?;
|
|
230
230
|
private readonly backup?;
|
|
231
231
|
constructor(cloudflare: Pick<CloudflarePort, 'findZoneByApex' | 'createZone'>, declaredOrgStore: Pick<DeclaredOrgStore, 'findOrgIdForZone' | 'list' | 'get'>, options?: FoundryPlatformStepExecutorOptions);
|
|
@@ -236,7 +236,7 @@ export declare class FoundryPlatformStepExecutor implements PlatformStepExecutor
|
|
|
236
236
|
files?: string;
|
|
237
237
|
integrations?: string;
|
|
238
238
|
ai?: string;
|
|
239
|
-
|
|
239
|
+
edge?: string;
|
|
240
240
|
archon?: string;
|
|
241
241
|
mail?: string;
|
|
242
242
|
backupExecutorMissing?: boolean;
|
|
@@ -428,7 +428,7 @@ export interface PlatformArchonDeps {
|
|
|
428
428
|
zitadelForInstance?: (issuerUrl: string) => PlatformArchonDeps['zitadel'];
|
|
429
429
|
/**
|
|
430
430
|
* Who the Cloudflare Access app in front of Archon lets through. Host
|
|
431
|
-
* config, not `DeclaredPlatform`: the platform record
|
|
431
|
+
* config, not `DeclaredPlatform`: the platform record edge services, not
|
|
432
432
|
* people. Empty blocks the step (`blockedSteps`) rather than building an
|
|
433
433
|
* Access app nobody can pass.
|
|
434
434
|
*/
|
|
@@ -640,8 +640,9 @@ export declare const PLATFORM_AI_MANAGEMENT_KEY_REF = "ai:platform:managementKey
|
|
|
640
640
|
* What Archon's management key may do in ai: exactly `ai`'s own
|
|
641
641
|
* `ARCHON_AI_PERMISSIONS` (`apps/host/scripts/authz/bootstrap-archon.ts`) --
|
|
642
642
|
* configuration only (offerings, provisioning, offers, budgets, usage, cost)
|
|
643
|
-
* plus deleting (archiving) an organisation
|
|
644
|
-
*
|
|
643
|
+
* plus deleting (archiving) an organisation and setting another
|
|
644
|
+
* organisation's webhook address and budget, never connections, runs or
|
|
645
|
+
* webhook deliveries. ai's `/v1/platform/keys` route refuses any permission outside
|
|
645
646
|
* this same list, so a request for more than this is a 403, not a wider key.
|
|
646
647
|
*/
|
|
647
648
|
export declare const PLATFORM_AI_MANAGEMENT_PERMISSIONS: readonly string[];
|
|
@@ -822,7 +823,7 @@ export declare const FILES_MANAGEMENT_KEY_ID = "foundry-1";
|
|
|
822
823
|
export declare const FILES_MANAGEMENT_KEY_REF = "files:platform:managementKey";
|
|
823
824
|
/** The ref the files host's introspection client secret is delivered under. */
|
|
824
825
|
export declare const FILES_INTROSPECTION_SECRET_REF = "zitadel:platform:files:introspection";
|
|
825
|
-
export interface
|
|
826
|
+
export interface PlatformEdgeRuntime {
|
|
826
827
|
fetch?: typeof fetch;
|
|
827
828
|
sleep?: (ms: number) => Promise<void>;
|
|
828
829
|
now?: () => number;
|
|
@@ -831,17 +832,17 @@ export interface PlatformNamesRuntime {
|
|
|
831
832
|
readyTimeoutMs?: number;
|
|
832
833
|
readyIntervalMs?: number;
|
|
833
834
|
}
|
|
834
|
-
export interface
|
|
835
|
+
export interface PlatformEdgeDeps {
|
|
835
836
|
coolify: Pick<CoolifyPort, 'findProjectByName' | 'createProject' | 'findOrCreateEnvironment' | 'listServers' | 'findApplicationByName' | 'getApplication' | 'requireGithubApp' | 'createGithubAppApplication' | 'createDockerfileApplication' | 'listDatabases' | 'createPostgresDatabase' | 'getDatabase' | 'bulkUpsertEnvs' | 'listEnvs' | 'updateApplication' | 'deploy' | 'deployAndWait' | 'raw'>;
|
|
836
837
|
cloudflare: Pick<CloudflarePort, 'findZoneByApex' | 'findTunnelByName' | 'createTunnel' | 'putTunnelConfig' | 'getTunnelToken' | 'findDnsRecord' | 'createDnsRecord' | 'updateDnsRecord'>;
|
|
837
838
|
deliverSecret: FoundryStepExecutorPorts['deliverSecret'];
|
|
838
839
|
/**
|
|
839
|
-
* Reads the
|
|
840
|
-
* {@link
|
|
840
|
+
* Reads the edge service's own Cloudflare API token back, held under
|
|
841
|
+
* {@link PLATFORM_EDGE_CLOUDFLARE_TOKEN_REF} -- a human step (the issue's
|
|
841
842
|
* own "Human steps"), never minted here: foundry has no Cloudflare
|
|
842
843
|
* account of its own to carve that token out of. Absent, or with nothing
|
|
843
844
|
* held under that ref yet, is reported blocked by `blockedSteps`
|
|
844
|
-
* ({@link
|
|
845
|
+
* ({@link edgeBlockedReason}), never planned to throw.
|
|
845
846
|
*/
|
|
846
847
|
heldKeys?: {
|
|
847
848
|
resolve(ref: string): Promise<string | null>;
|
|
@@ -853,7 +854,7 @@ export interface PlatformNamesDeps {
|
|
|
853
854
|
*/
|
|
854
855
|
cloudflareAccountId?: string;
|
|
855
856
|
/** Clock, sleep and fetch for the readiness wait and delivery retries; real ones by default. */
|
|
856
|
-
runtime?:
|
|
857
|
+
runtime?: PlatformEdgeRuntime;
|
|
857
858
|
}
|
|
858
859
|
/**
|
|
859
860
|
* #174: everything {@link provisionPlatformIntegrations} needs beyond
|