@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 { CloudflareClient } from '@wtfalch/providers/cloudflare';
|
|
2
|
-
import type
|
|
2
|
+
import { type DeclaredOrg } from '../org.js';
|
|
3
3
|
import type { DeclaredPlatform } from '../platform.js';
|
|
4
4
|
import type { DeclaredOrgStore, PlatformStore } from '../store.js';
|
|
5
5
|
import { type DeployHealthRuntime } from './deploy-health.js';
|
|
@@ -8,11 +8,16 @@ import type { AccessPort } from './edge-access.js';
|
|
|
8
8
|
import type { DnsPort } from './edge-dns.js';
|
|
9
9
|
import type { TunnelPort } from './edge-tunnels.js';
|
|
10
10
|
import type { CloudflarePort, CoolifyPort, FoundryStepExecutorPorts, ZitadelPort } from './executor.js';
|
|
11
|
+
export { FILES_MANAGEMENT_KEY_REF, JOBS_ADMIN_TOKEN_REF, PLATFORM_AI_MANAGEMENT_KEY_REF, } from './held-key-refs.js';
|
|
11
12
|
import type { LiveResource, PlatformCloudflareReader, PlatformCoolifyReader } from './live.js';
|
|
13
|
+
import { type FilesKeysMinted, type ProjectSource, type ServiceNetwork, type ServiceTarget, type StepScope } from './own-services.js';
|
|
12
14
|
import type { PlatformDriftedSteps, PlatformStepExecutionContext, PlatformStepExecutor } from './plan-apply.js';
|
|
13
15
|
import { type BrokerDeployRecord, PLATFORM_ZITADEL_ORG_SCOPE_KEY } from './platform-diff.js';
|
|
16
|
+
export { PLATFORM_ZITADEL_ORG_SCOPE_KEY };
|
|
14
17
|
import { type CallsSfuPlatformDeps } from './platform-sfu.js';
|
|
15
18
|
import { type PlatformUptimeDeps } from './uptime-worker.js';
|
|
19
|
+
/** The key `runPlatformSteps` records the apex zone's id under (`system:kind:key`). */
|
|
20
|
+
export declare function apexZoneKey(apex: string): string;
|
|
16
21
|
/**
|
|
17
22
|
* #168: everything {@link provisionPlatformAuth} needs beyond
|
|
18
23
|
* {@link PlatformStepExecutionContext} -- a bundle, not more constructor
|
|
@@ -27,6 +32,15 @@ import { type PlatformUptimeDeps } from './uptime-worker.js';
|
|
|
27
32
|
* being the platform key held under {@link PLATFORM_EDGE_KEY_REF}.
|
|
28
33
|
*/
|
|
29
34
|
export type PlatformDnsFactory = (edgeUrl: string) => DnsPort | Promise<DnsPort>;
|
|
35
|
+
/**
|
|
36
|
+
* The DNS port for a platform step: edge's recorded address, never a public
|
|
37
|
+
* hostname, because auth, mail and archon write their records before any of
|
|
38
|
+
* those exist. Edge's step runs first and records the address
|
|
39
|
+
* (`planPlatform`), so a missing one means that step has not finished.
|
|
40
|
+
*/
|
|
41
|
+
export declare function platformDns(deps: {
|
|
42
|
+
dns: PlatformDnsFactory;
|
|
43
|
+
}, recorded: Readonly<Record<string, string>>, step: string): Promise<DnsPort>;
|
|
30
44
|
/**
|
|
31
45
|
* F4.tunnels: builds the {@link TunnelPort} a platform step creates, configures
|
|
32
46
|
* and reads the connector token of its tunnel through, given the same address
|
|
@@ -37,6 +51,10 @@ export type PlatformDnsFactory = (edgeUrl: string) => DnsPort | Promise<DnsPort>
|
|
|
37
51
|
export type PlatformTunnelsFactory = (edgeUrl: string) => PlatformTunnelPort | Promise<PlatformTunnelPort>;
|
|
38
52
|
/** The tunnel calls a platform step makes; it never deletes one. */
|
|
39
53
|
export type PlatformTunnelPort = Pick<TunnelPort, 'findTunnelByName' | 'createTunnel' | 'putTunnelConfig' | 'getTunnelToken'>;
|
|
54
|
+
/** The tunnel port for a platform step; same address and same reason as {@link platformDns}. */
|
|
55
|
+
export declare function platformTunnels(deps: {
|
|
56
|
+
tunnels: PlatformTunnelsFactory;
|
|
57
|
+
}, recorded: Readonly<Record<string, string>>, step: string): Promise<PlatformTunnelPort>;
|
|
40
58
|
/**
|
|
41
59
|
* F4.access: builds the {@link AccessPort} a platform step finds, creates and
|
|
42
60
|
* deletes its Access app through, given the same address as
|
|
@@ -90,14 +108,21 @@ export interface PlatformAuthDeps {
|
|
|
90
108
|
tunnels: PlatformTunnelsFactory;
|
|
91
109
|
cloudflare: Pick<CloudflarePort, 'findZoneByApex'>;
|
|
92
110
|
/** Must target the platform's own new instance (its issuer), or `zitadelForInstance` must be set. */
|
|
93
|
-
zitadel: Pick<ZitadelPort, 'findOrCreateOrg' | 'mintAdminMachineUser' | 'mintLoginClientMachineUser' | 'listLoginClientPats' | 'reserveUserName' | 'closeSelfRegistration' | 'inviteOrgOwner'> & PlatformZitadelSmtp;
|
|
111
|
+
zitadel: Pick<ZitadelPort, 'findOrCreateOrg' | 'mintAdminMachineUser' | 'mintLoginClientMachineUser' | 'listLoginClientPats' | 'reserveUserName' | 'closeSelfRegistration' | 'inviteOrgOwner' | 'addOrgDomain' | 'setPrimaryOrgDomain' | 'setUserMetadata' | 'getUserMetadata'> & PlatformZitadelSmtp;
|
|
112
|
+
/**
|
|
113
|
+
* Mails the owner's first-sign-in link to their personal address (the same
|
|
114
|
+
* port as `FoundryStepExecutorPorts.ownerInviteMail`). The link carries the
|
|
115
|
+
* invite code, so `send` must not log it or put it in an error. Without it
|
|
116
|
+
* the owner invite cannot run and the step stops there with a fixed message.
|
|
117
|
+
*/
|
|
118
|
+
ownerInviteMail?: FoundryStepExecutorPorts['ownerInviteMail'];
|
|
94
119
|
/**
|
|
95
120
|
* Builds the ZITADEL port for the new instance from its issuer URL
|
|
96
121
|
* (`https://<auth.hostname>`). When set it is used instead of
|
|
97
122
|
* {@link zitadel}, so the mint always hits the instance this step just
|
|
98
123
|
* built. See `createZitadelAdminMinter` for the mint half.
|
|
99
124
|
*/
|
|
100
|
-
zitadelForInstance?: (issuerUrl: string) => PlatformAuthDeps['zitadel'];
|
|
125
|
+
zitadelForInstance?: (issuerUrl: string, bearerRef?: string) => PlatformAuthDeps['zitadel'];
|
|
101
126
|
deliverSecret: FoundryStepExecutorPorts['deliverSecret'];
|
|
102
127
|
/**
|
|
103
128
|
* #210: reads back the broker's Lettermint mail key, held under
|
|
@@ -125,9 +150,19 @@ export interface PlatformAuthRuntime {
|
|
|
125
150
|
readyIntervalMs?: number;
|
|
126
151
|
/** Tries at handing the admin key to the host (default 3). */
|
|
127
152
|
deliverAttempts?: number;
|
|
153
|
+
/** Timeout of each readiness poll request (default 10 seconds). */
|
|
154
|
+
readyRequestTimeoutMs?: number;
|
|
155
|
+
/** Told of every secret the sign-in step reads or mints, so the host can scrub it from logs and audit rows. */
|
|
156
|
+
onSecret?: (value: string) => void;
|
|
128
157
|
}
|
|
129
|
-
/**
|
|
130
|
-
|
|
158
|
+
/**
|
|
159
|
+
* An own org's scheduler only: the app's uuid, recorded and persisted before
|
|
160
|
+
* the admin token leaves and cleared once the token is delivered and written.
|
|
161
|
+
* Left set, the planner reports the scheduler blocked and never mints again.
|
|
162
|
+
*/
|
|
163
|
+
export declare const JOBS_ADMIN_TOKEN_UNDELIVERED_SCOPE_KEY = "jobs_admin_token_undelivered";
|
|
164
|
+
/** An own org's scheduler only: the git ref its app last deployed, so a changed declared ref plans the step again. */
|
|
165
|
+
export declare const JOBS_APPLIED_GIT_REF_SCOPE_KEY = "jobs_applied_git_ref";
|
|
131
166
|
/**
|
|
132
167
|
* The narrow slice of {@link CoolifyPort} {@link FoundryPlatformStepExecutor}
|
|
133
168
|
* needs for `coolify:jobs` (#173) -- the same project/environment/server/app/
|
|
@@ -135,7 +170,7 @@ export declare const JOBS_ADMIN_TOKEN_REF = "jobs:scheduler:adminToken";
|
|
|
135
170
|
* org's own app, reused here for the platform's one scheduler instead of once
|
|
136
171
|
* per org.
|
|
137
172
|
*/
|
|
138
|
-
type JobsCoolifyPort = Pick<CoolifyPort, 'findProjectByName' | 'createProject' | 'findOrCreateEnvironment' | 'listServers' | 'requireGithubApp' | 'findApplicationByName' | 'createGithubAppApplication' | 'listDatabases' | 'createPostgresDatabase' | 'getDatabase' | 'raw' | 'bulkUpsertEnvs' | 'listEnvs' | 'getApplication' | 'updateApplication' | 'deployAndWait'>;
|
|
173
|
+
export type JobsCoolifyPort = Pick<CoolifyPort, 'findProjectByName' | 'createProject' | 'findOrCreateEnvironment' | 'listServers' | 'requireGithubApp' | 'findApplicationByName' | 'createGithubAppApplication' | 'listDatabases' | 'createPostgresDatabase' | 'getDatabase' | 'raw' | 'bulkUpsertEnvs' | 'listEnvs' | 'getApplication' | 'updateApplication' | 'deployAndWait'>;
|
|
139
174
|
/**
|
|
140
175
|
* Options for {@link FoundryPlatformStepExecutor}'s constructor, replacing
|
|
141
176
|
* four positional arguments that shifted on every merge that added a
|
|
@@ -315,12 +350,14 @@ export declare class FoundryPlatformStepExecutor implements PlatformStepExecutor
|
|
|
315
350
|
auth?: string;
|
|
316
351
|
authExecutorMissing?: boolean;
|
|
317
352
|
authOtherInputsMissing?: string[];
|
|
353
|
+
authOwnerInvite?: string;
|
|
318
354
|
files?: string;
|
|
319
355
|
integrations?: string;
|
|
320
356
|
ai?: string;
|
|
321
357
|
edge?: string;
|
|
322
358
|
archon?: string;
|
|
323
359
|
mail?: string;
|
|
360
|
+
relay?: string;
|
|
324
361
|
accessProvider?: string;
|
|
325
362
|
backupExecutorMissing?: boolean;
|
|
326
363
|
hc?: string;
|
|
@@ -377,7 +414,51 @@ export declare class FoundryPlatformStepExecutor implements PlatformStepExecutor
|
|
|
377
414
|
private executeSfu;
|
|
378
415
|
private coolifyJobs;
|
|
379
416
|
}
|
|
417
|
+
export interface JobsHostDeps {
|
|
418
|
+
coolify: JobsCoolifyPort;
|
|
419
|
+
/** `FoundryStepExecutorPorts.deliverSecret`'s contract: never returned, recorded, logged or put in an error. */
|
|
420
|
+
deliverSecret: (ref: string, secret: string) => Promise<void>;
|
|
421
|
+
/** Reads a delivered secret back from the host. Without it, an org's undelivered marker cannot be resumed. */
|
|
422
|
+
readSecret?: (ref: string) => Promise<string | null>;
|
|
423
|
+
runtime: DeployHealthRuntime;
|
|
424
|
+
}
|
|
425
|
+
export interface JobsHostInput {
|
|
426
|
+
/** The scheduler's public domain; null leaves the app on Coolify's own, for a scheduler only containers reach. */
|
|
427
|
+
hostname: string | null;
|
|
428
|
+
gitRef: string;
|
|
429
|
+
project: ProjectSource;
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Provisions one jobs scheduler (database, app, admin token) for `target`:
|
|
433
|
+
* the platform's, or an own org's. Every name and the held-key ref come from
|
|
434
|
+
* the target.
|
|
435
|
+
*
|
|
436
|
+
* An org target also guards the mint (`CLAUDE.md`, "Platform steps" rules 1
|
|
437
|
+
* and 3): the token's id is recorded as undelivered and persisted before the
|
|
438
|
+
* token leaves, the delivered guard is recorded after the env write, and the
|
|
439
|
+
* applied git ref is recorded so a changed ref plans the step again. The
|
|
440
|
+
* platform's step keeps its provision-only behaviour.
|
|
441
|
+
*/
|
|
442
|
+
export declare function provisionJobsHost(scope: StepScope, deps: JobsHostDeps, target: ServiceTarget, input: JobsHostInput): Promise<{
|
|
443
|
+
resourceId?: string;
|
|
444
|
+
}>;
|
|
445
|
+
/**
|
|
446
|
+
* Fixed `custom_internal_name`s (`diff.ts`'s `coolifyCalls` sets the same
|
|
447
|
+
* kind of name on Synapse/MAS) so the Cloudflare tunnel below can address
|
|
448
|
+
* each app directly, by container name, on the shared Coolify Docker network
|
|
449
|
+
* -- bypassing Coolify's own Traefik, which dispatches purely by `Host`
|
|
450
|
+
* header and so cannot give two different apps the same public hostname.
|
|
451
|
+
*/
|
|
380
452
|
export declare const PLATFORM_AUTH_LOGIN_INTERNAL_NAME = "platform-auth-login";
|
|
453
|
+
/**
|
|
454
|
+
* The inline Dockerfile of the ZITADEL app. The published image is distroless (no shell, no `curl`) and has no
|
|
455
|
+
* HEALTHCHECK, and Coolify runs its own check as `sh -c`. So the app is a Dockerfile app: the image's own `ready`
|
|
456
|
+
* command is its exec-form HEALTHCHECK, which Coolify keeps while its own check is off. `ready` asks the port and
|
|
457
|
+
* TLS mode from the same `ZITADEL_*` settings the server reads. The start period covers the first run, which sets
|
|
458
|
+
* up the database and the first instance before it listens. The start command is the one
|
|
459
|
+
* {@link AUTH_ZITADEL_RUN_OPTIONS} names.
|
|
460
|
+
*/
|
|
461
|
+
export declare function zitadelDockerfile(): string;
|
|
381
462
|
export declare const AUTH_LOGIN_PORT = "3000";
|
|
382
463
|
/**
|
|
383
464
|
* Every ZITADEL OIDC/management/console/asset path -- `deploy/proxy/Caddyfile`'s
|
|
@@ -387,6 +468,8 @@ export declare const AUTH_LOGIN_PORT = "3000";
|
|
|
387
468
|
* everything else, including the root).
|
|
388
469
|
*/
|
|
389
470
|
export declare const AUTH_ZITADEL_PATH_PATTERN = "^/(oidc|oauth|saml|management|admin|system|auth|assets|debug/healthz|ui/console|\\.well-known|v2|v2beta|zitadel\\.[A-Za-z0-9.]+)(/|$)";
|
|
471
|
+
export declare const PLATFORM_AUTH_ZITADEL_SCOPE_KEY = "coolify_platform_auth_zitadel_app";
|
|
472
|
+
export declare const PLATFORM_AUTH_LOGIN_SCOPE_KEY = "coolify_platform_auth_login_app";
|
|
390
473
|
/**
|
|
391
474
|
* #283: the ref the new instance's IAM_OWNER machine token (a PAT) is
|
|
392
475
|
* delivered under, once. A host reads it back to build `zitadelForInstance`
|
|
@@ -403,6 +486,98 @@ export declare const PLATFORM_AUTH_INSTANCE_BEARER_REF = "zitadel:platform:insta
|
|
|
403
486
|
* (`wtfalch/auth/docs/deploy.md`).
|
|
404
487
|
*/
|
|
405
488
|
export declare const PLATFORM_AUTH_BROKER_MAIL_KEY_REF = "lettermint:platform:apiToken";
|
|
489
|
+
/** What {@link provisionAuthStack} reads besides its {@link ServiceTarget}: the platform's values, or one company's. */
|
|
490
|
+
export interface AuthStackInput {
|
|
491
|
+
hostname: string;
|
|
492
|
+
apex: string;
|
|
493
|
+
gitRef: string;
|
|
494
|
+
project: ProjectSource;
|
|
495
|
+
network: ServiceNetwork;
|
|
496
|
+
/** The ZITADEL SMTP sender address. */
|
|
497
|
+
smtpSender: string;
|
|
498
|
+
/** Where the login app sends a user with no sign-in request in flight, or null to leave it unset. */
|
|
499
|
+
defaultRedirectUri: string | null;
|
|
500
|
+
/** The company whose owner is invited into its own sign-in org; null for a company's own stack. */
|
|
501
|
+
ownerCompany: DeclaredOrg | null;
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* #168: makes `coolify:app` keyed {@link PLATFORM_AUTH_KEY} true -- a Coolify
|
|
505
|
+
* Postgres, the ZITADEL app itself, the zitadel-login app and (#204) the
|
|
506
|
+
* sign-in broker, reached through one Cloudflare tunnel and DNS record at
|
|
507
|
+
* `platform.auth.hostname`, then (only the first time) foundry's own admin
|
|
508
|
+
* machine user, handed to {@link PlatformAuthDeps.deliverSecret}. Env values
|
|
509
|
+
* and instance settings are `wtfalch/auth`'s own `docker-compose.yml`
|
|
510
|
+
* (checked 2026-09-29), with `ZITADEL_DOMAIN`/`companyincluded.com` replaced by this
|
|
511
|
+
* platform's own `apex`/`auth.hostname` wherever the compose named one --
|
|
512
|
+
* every other setting it leaves open (login v2 required, external port 443,
|
|
513
|
+
* TLS terminated at Cloudflare, `PASSWORDCHANGEREQUIRED` false) is kept
|
|
514
|
+
* exactly as that compose has it (#168's "Decide"). The estate's old, shared
|
|
515
|
+
* sign-in host carries nothing once this runs (#204, `WTFALCH-DEV-EXIT.md`
|
|
516
|
+
* row A4).
|
|
517
|
+
*
|
|
518
|
+
* Every part is safe to run again after a throw partway: each id is recorded
|
|
519
|
+
* (and persisted) as soon as its resource exists, each app is named and
|
|
520
|
+
* deployed on every run that needs it, and the admin key is delivered before
|
|
521
|
+
* the guard that skips the mint is recorded.
|
|
522
|
+
*
|
|
523
|
+
* Three apps, one public hostname, no separate `proxy` app: `wtfalch/auth`
|
|
524
|
+
* splits ZITADEL's API, its login UI and its broker with a built Caddy proxy
|
|
525
|
+
* container inside one docker-compose stack; foundry instead gives each app a
|
|
526
|
+
* fixed `custom_internal_name` and lets the Cloudflare tunnel itself
|
|
527
|
+
* path-route between them (`AUTH_ZITADEL_PATH_PATTERN`,
|
|
528
|
+
* `AUTH_BROKER_PATH_PATTERN`), skipping Coolify's own Traefik (which can only
|
|
529
|
+
* dispatch by `Host`, not by path) rather than provisioning a fourth Coolify
|
|
530
|
+
* resource to do the same job. No app is created with a Coolify `domains`
|
|
531
|
+
* binding for the same reason -- the tunnel reaches each one directly by its
|
|
532
|
+
* `custom_internal_name`, never through Traefik.
|
|
533
|
+
*
|
|
534
|
+
* Known gaps, left for review (#168's own "the PR stays open for William's
|
|
535
|
+
* review"):
|
|
536
|
+
* - ~~Neither the login app nor the broker ever receives
|
|
537
|
+
* `ZITADEL_SERVICE_USER_TOKEN`.~~ Closed by #216 (building on #209/#215):
|
|
538
|
+
* this function's own login-client block below mints a dedicated
|
|
539
|
+
* `IAM_LOGIN_CLIENT` machine user (never the admin one, which is
|
|
540
|
+
* `IAM_OWNER`) and delivers its token to both apps, once the broker's
|
|
541
|
+
* other required inputs are already met.
|
|
542
|
+
* - ~~The broker never receives the earlier mail provider's token either, so it will not
|
|
543
|
+
* start on `AUTH_APPS` alone.~~ Closed by #210, once `wtfalch/auth#138`
|
|
544
|
+
* moved the broker's account mail onto `@wtfalch/mailer`'s Lettermint
|
|
545
|
+
* transport: this function now reads the key back from
|
|
546
|
+
* {@link PlatformAuthDeps.heldKeys} under
|
|
547
|
+
* {@link PLATFORM_AUTH_BROKER_MAIL_KEY_REF} and pushes it as
|
|
548
|
+
* `LETTERMINT_API_TOKEN`, once a human (or Archon's own onboarding) has
|
|
549
|
+
* put one there. Still a gap until then, the same "blocked, not failing"
|
|
550
|
+
* way an unset `AUTH_APPS` already is.
|
|
551
|
+
* - `AUTH_APPS` starts unset: the broker has nothing to serve until a
|
|
552
|
+
* platform app registers with it ({@link provisionPlatformArchon} is the
|
|
553
|
+
* first), so it will not start (`parseApps` throws on an empty list) until
|
|
554
|
+
* then either. Safe to run again: the next apply that registers an app
|
|
555
|
+
* redeploys the broker with a non-empty list.
|
|
556
|
+
* - ~~Whoever supplies `deps.zitadel` (or `zitadelForInstance`) must hold a
|
|
557
|
+
* bearer token for the new instance, which foundry cannot read from it.~~
|
|
558
|
+
* Closed by #283: see "The instance bearer" below.
|
|
559
|
+
*
|
|
560
|
+
* The instance bearer (#283): the container creates no instance of its own
|
|
561
|
+
* (`ZITADEL_FIRSTINSTANCE_SKIP`). foundry generates an RSA keypair, puts the
|
|
562
|
+
* public half in `ZITADEL_SYSTEMAPIUSERS` (a system API user with
|
|
563
|
+
* SYSTEM_OWNER only), and once the container answers calls ZITADEL's system
|
|
564
|
+
* API `CreateInstance` with a machine owner (IAM_OWNER of the new instance)
|
|
565
|
+
* signed by the private half, which lives in memory for that run only. The
|
|
566
|
+
* owner's PAT comes back once and is handed to
|
|
567
|
+
* {@link PlatformAuthDeps.deliverSecret} under
|
|
568
|
+
* {@link PLATFORM_AUTH_INSTANCE_BEARER_REF}, before anything else uses it. A
|
|
569
|
+
* host builds `zitadelForInstance` as `(url) => createZitadelAdminMinter({
|
|
570
|
+
* url, token: <the secret delivered under that ref> })` plus its own org
|
|
571
|
+
* calls; `zitadelForInstance` is first called after the delivery. No human
|
|
572
|
+
* admin is created: a person who needs console access is added by the host
|
|
573
|
+
* through that bearer.
|
|
574
|
+
*
|
|
575
|
+
* #197: a `cloudflared` connector for this tunnel is run
|
|
576
|
+
* ({@link ensureCloudflaredConnector}), so `hostname` answers.
|
|
577
|
+
*/
|
|
578
|
+
export declare function provisionAuthStack(scope: StepScope, deps: PlatformAuthDeps, target: ServiceTarget, input: AuthStackInput): Promise<{
|
|
579
|
+
resourceId?: string;
|
|
580
|
+
}>;
|
|
406
581
|
/**
|
|
407
582
|
* The fixed description Stalwart's admin API key is minted under
|
|
408
583
|
* (`StalwartClient.createApiKey`'s own signature returns only the secret,
|
|
@@ -434,6 +609,7 @@ export interface PlatformMailAdminPort {
|
|
|
434
609
|
* written nothing else. The caller restarts the app and calls again.
|
|
435
610
|
*/
|
|
436
611
|
applySettings(input: {
|
|
612
|
+
/** The name the server announces on SMTP: its own hostname, never the relay's MX name. */
|
|
437
613
|
mailHost: string;
|
|
438
614
|
/** The platform's own apex domain, `platformMailSettingsObjects`'s `usernameDomain`. */
|
|
439
615
|
apex: string;
|
|
@@ -520,12 +696,144 @@ export interface PlatformMailRuntime {
|
|
|
520
696
|
/** Tries at handing the admin key to the host (default 3). */
|
|
521
697
|
deliverAttempts?: number;
|
|
522
698
|
}
|
|
699
|
+
/**
|
|
700
|
+
* What {@link provisionMailServer} reads besides the target's names: the
|
|
701
|
+
* platform's declared values for the platform's server, the company's for a
|
|
702
|
+
* company's.
|
|
703
|
+
*/
|
|
704
|
+
export interface MailServerInput {
|
|
705
|
+
/** Where the server's web, JMAP and management API answer: `mail.<domain>`. */
|
|
706
|
+
hostname: string;
|
|
707
|
+
/** The domain the server's admin account and default domain belong to. */
|
|
708
|
+
apex: string;
|
|
709
|
+
/** The MX name: the A record `mxRecord` publishes. The server announces `hostname`, not this. */
|
|
710
|
+
mailHost: string;
|
|
711
|
+
/** The Stalwart image tag. */
|
|
712
|
+
imageTag: string;
|
|
713
|
+
/** `host:container` list; empty publishes no host port. */
|
|
714
|
+
hostPorts: string;
|
|
715
|
+
/** The sign-in the server's directory trusts; its `mail` project's id is the audience. */
|
|
716
|
+
issuerUrl: string;
|
|
717
|
+
/** Publish `mailHost` as an unproxied A record at the server's address. `null`: someone else's record serves it. */
|
|
718
|
+
mxRecord: {
|
|
719
|
+
serverIpv4?: string | null;
|
|
720
|
+
} | null;
|
|
721
|
+
project: ProjectSource;
|
|
722
|
+
network: ServiceNetwork;
|
|
723
|
+
}
|
|
724
|
+
/**
|
|
725
|
+
* #169: makes `coolify:app` keyed {@link PLATFORM_MAIL_KEY} true -- Stalwart
|
|
726
|
+
* on Coolify with its own persistent storage, reached at
|
|
727
|
+
* `platform.mail.hostname` through a Cloudflare tunnel for web, JMAP and its
|
|
728
|
+
* management API (mirroring #168's ZITADEL/login apps: TLS terminated at
|
|
729
|
+
* Cloudflare, plain HTTP behind the tunnel) and at `mx.<apex>` over a direct,
|
|
730
|
+
* unproxied `A` record to the Coolify server's own address for SMTP -- raw
|
|
731
|
+
* SMTP cannot ride Cloudflare's HTTP-only tunnel the way a web app does,
|
|
732
|
+
* which is exactly why #169's own "Human steps" asks a person to open ports
|
|
733
|
+
* 25, 465 and 587 on that server's host firewall. Then, once the instance
|
|
734
|
+
* answers, the OIDC directory against the platform's own sign-in and
|
|
735
|
+
* foundry's admin API key, minted once and handed to
|
|
736
|
+
* {@link PlatformMailDeps.deliverSecret}.
|
|
737
|
+
*
|
|
738
|
+
* Every part is safe to run again after a throw partway, the same rules
|
|
739
|
+
* {@link provisionPlatformAuth} follows: each id is recorded (and persisted)
|
|
740
|
+
* as soon as its resource exists, the app is deployed on every run that
|
|
741
|
+
* needs it, and the admin key is delivered before the guard that skips the
|
|
742
|
+
* mint is recorded. Unlike ZITADEL's `ZITADEL_ADMIN_PASSWORD`, the recovery
|
|
743
|
+
* credential this step generates (`STALWART_RECOVERY_ADMIN`) is read back
|
|
744
|
+
* from Coolify's own env store on every run rather than held only in memory
|
|
745
|
+
* -- `CoolifyPort.listEnvs`'s own doc comment says these values come back
|
|
746
|
+
* live -- so a retry after a delivery failure re-authenticates with the same
|
|
747
|
+
* credential instead of losing it.
|
|
748
|
+
*
|
|
749
|
+
* **Why not `@wtfalch/email/postmaster`'s `mailObjects`/`applyObjects`**, the
|
|
750
|
+
* instance-bootstrap helper `wtfalch-manage` already uses for the hand-built
|
|
751
|
+
* `mail.companyincluded.com`: that plan also wires an `AcmeProvider` and `DnsServer`
|
|
752
|
+
* for Stalwart's own DNS-01 ACME challenge, and an `MtaRoute` that relays
|
|
753
|
+
* every outbound message through the earlier mail provider -- the estate moved transactional
|
|
754
|
+
* mail off it onto Lettermint before this issue (`@wtfalch/mailer` ADR
|
|
755
|
+
* 0006), and this step provisions no Cloudflare DNS token for Stalwart's own
|
|
756
|
+
* ACME either. Calling it as-is would mint an outbound relay this platform
|
|
757
|
+
* does not want and a credential requirement this issue does not cover, so
|
|
758
|
+
* {@link PlatformMailAdminPort.applySettings} is a narrower settings write
|
|
759
|
+
* instead, through `platformMailSettingsObjects`: the default hostname and
|
|
760
|
+
* the OIDC directory only.
|
|
761
|
+
*
|
|
762
|
+
* Known gaps, left for review (like #168's own "the PR stays open for
|
|
763
|
+
* William's review"):
|
|
764
|
+
* - **No ACME/TLS for the SMTP listener.** `mx.<apex>` answers with
|
|
765
|
+
* Stalwart's own self-signed certificate until a follow-up wires a real
|
|
766
|
+
* one (`mailObjects`'s `AcmeProvider`/`DnsServer` shape, minus the earlier relay
|
|
767
|
+
* relay it also carries).
|
|
768
|
+
* - **The Lettermint team token the org app step reads still comes from
|
|
769
|
+
* wherever the host already reads it, not held keys.** Needs
|
|
770
|
+
* `StepExecutionContext` to carry the platform, which `plan-apply.ts`'s
|
|
771
|
+
* own doc comment already scoped out of #167 as more than a small change
|
|
772
|
+
* -- a follow-up issue, not this one. (#205 removed the org step
|
|
773
|
+
* `coolify:mail`'s own fallback to the hand-built mail
|
|
774
|
+
* instance -- `diff.ts`'s `planMail` already blocks the step until the
|
|
775
|
+
* platform's own mail is applied, so the fallback only ever served a
|
|
776
|
+
* plan recorded before that, and now fails loudly instead.)
|
|
777
|
+
* - **A ZITADEL client's token may not carry the mail project's id in
|
|
778
|
+
* `aud` by default.** #346's own "Human steps": confirm this on the first
|
|
779
|
+
* live run, and if it does not, the client must request the mail
|
|
780
|
+
* project's audience scope.
|
|
781
|
+
* - **The admin API key is minted with `'inherit'` permissions**, not a
|
|
782
|
+
* narrower set scoped to only what `applySettings` needs.
|
|
783
|
+
* - **The undelivered-key guard records before `createApiKey` mints**, not
|
|
784
|
+
* after -- so a crash between the two records an id (`admin.createApiKey`
|
|
785
|
+
* has not even run) that no key was ever minted for. Harmless today
|
|
786
|
+
* (the guard only ever blocks a retry and names an id to revoke by), but
|
|
787
|
+
* worth tightening if `createApiKey` ever starts returning its own id.
|
|
788
|
+
*/
|
|
789
|
+
export declare function provisionMailServer(scope: StepScope, deps: PlatformMailDeps, target: ServiceTarget, input: MailServerInput): Promise<{
|
|
790
|
+
resourceId?: string;
|
|
791
|
+
}>;
|
|
792
|
+
/**
|
|
793
|
+
* #211: the broker cannot start without `ZITADEL_SERVICE_USER_TOKEN`/`_FILE`,
|
|
794
|
+
* `LETTERMINT_API_TOKEN` and `AUTH_APPS`. `provisionPlatformAuth` already
|
|
795
|
+
* skips deploying the broker while any is missing
|
|
796
|
+
* ({@link missingBrokerInputs}); this is the same check surfaced through
|
|
797
|
+
* `blockedSteps`, so a plan says why, by name, rather than the broker simply
|
|
798
|
+
* sitting undeployed with no explanation. `null` while there is no broker
|
|
799
|
+
* app id recorded yet (nothing to check) or every input is present.
|
|
800
|
+
*
|
|
801
|
+
* #211 round 3: wraps the live read the way {@link mailBlockedReason} wraps
|
|
802
|
+
* its own Coolify call -- a transient `listEnvs` failure must report a
|
|
803
|
+
* blocked reason naming the broker app id, never throw out of
|
|
804
|
+
* `blockedSteps` and take the rest of the plan down with it.
|
|
805
|
+
*
|
|
806
|
+
* #211 round 5 addendum: when `AUTH_APPS` is the reason, names which problem
|
|
807
|
+
* it actually is ({@link authAppsProblem}) instead of always saying "it has
|
|
808
|
+
* no ... AUTH_APPS yet" -- that wording is only true when nothing has been
|
|
809
|
+
* set; a present-but-invalid value is a different failure (a person needs to
|
|
810
|
+
* go fix an entry, not merely wait for one of #209/#210/Archon's own
|
|
811
|
+
* registration to supply one), and reporting it identically hid which one
|
|
812
|
+
* was true.
|
|
813
|
+
*
|
|
814
|
+
* #210: `LETTERMINT_API_TOKEN` joins `ZITADEL_SERVICE_USER_TOKEN` in being
|
|
815
|
+
* excluded from `otherInputsMissing` -- but only while `auth.heldKeys`
|
|
816
|
+
* actually resolves {@link PLATFORM_AUTH_BROKER_MAIL_KEY_REF} right now.
|
|
817
|
+
* `planAuth`'s own "loop fix" stops planning this step the moment every
|
|
818
|
+
* `otherInputsMissing` entry looks like something only another run can
|
|
819
|
+
* supply; excluding `LETTERMINT_API_TOKEN` unconditionally (the way
|
|
820
|
+
* `ZITADEL_SERVICE_USER_TOKEN` is, since foundry can always eventually mint
|
|
821
|
+
* that one) would keep the step planned, and `provisionPlatformAuth`
|
|
822
|
+
* running its full body, forever on a host that never wires `heldKeys` or
|
|
823
|
+
* whose operator has not put a Lettermint key there yet -- exactly the
|
|
824
|
+
* wasteful-replan bug #211 round 5 fixed for the broker deploy itself. Once
|
|
825
|
+
* the key *is* held, excluding it lets this run's `provisionPlatformAuth`
|
|
826
|
+
* push it (see that function's own `heldMailKey` read) and clears the gap
|
|
827
|
+
* for good.
|
|
828
|
+
*/
|
|
829
|
+
/** Why the platform owner cannot be invited yet, or `null`. Fixed text: names no address. */
|
|
830
|
+
export declare function ownerInviteBlockedReason(ownerCompany: DeclaredOrg | null, hasInviteMail: boolean): string | null;
|
|
523
831
|
/**
|
|
524
832
|
* #175: everything {@link provisionPlatformArchon} needs beyond
|
|
525
833
|
* {@link PlatformStepExecutionContext}, bundled like {@link PlatformAuthDeps}.
|
|
526
834
|
*/
|
|
527
835
|
export interface PlatformArchonDeps {
|
|
528
|
-
coolify: Pick<CoolifyPort, 'findProjectByName' | 'createProject' | 'findOrCreateEnvironment' | 'listServers' | 'findApplicationByName' | 'getApplication' | 'requireGithubApp' | 'createGithubAppApplication' | 'createDockerfileApplication' | 'listDatabases' | 'createPostgresDatabase' | 'getDatabase' | 'raw' | 'bulkUpsertEnvs' | 'listEnvs' | 'deleteEnv' | 'updateApplication' | 'deploy' | 'deployAndWait' | 'getDeployment'>;
|
|
836
|
+
coolify: Pick<CoolifyPort, 'findProjectByName' | 'listEnvironments' | 'createProject' | 'findOrCreateEnvironment' | 'listServers' | 'findApplicationByName' | 'getApplication' | 'requireGithubApp' | 'createGithubAppApplication' | 'createDockerfileApplication' | 'listDatabases' | 'createPostgresDatabase' | 'getDatabase' | 'raw' | 'bulkUpsertEnvs' | 'listEnvs' | 'deleteEnv' | 'updateApplication' | 'deploy' | 'deployAndWait' | 'getDeployment'>;
|
|
529
837
|
/** F4.dns: every DNS record call, through edge at its recorded address (see {@link PlatformDnsFactory}). */
|
|
530
838
|
dns: PlatformDnsFactory;
|
|
531
839
|
/** F4.tunnels: every tunnel call, through edge at its recorded address (see {@link PlatformTunnelsFactory}). */
|
|
@@ -604,7 +912,6 @@ export interface PlatformArchonRuntime {
|
|
|
604
912
|
* org (`foundry:<orgId>`), with the platform's step key in the org id's place.
|
|
605
913
|
*/
|
|
606
914
|
export declare const PLATFORM_ARCHON_ACCESS_APP_NAME = "foundry:platform:archon";
|
|
607
|
-
export { PLATFORM_ZITADEL_ORG_SCOPE_KEY };
|
|
608
915
|
/** Archon's OIDC client id: public, not a secret, so a plain scope id. */
|
|
609
916
|
export declare const PLATFORM_ARCHON_CLIENT_ID_SCOPE_KEY = "zitadel_platform_archon_client_id";
|
|
610
917
|
/**
|
|
@@ -764,8 +1071,6 @@ export interface PlatformAiRuntime {
|
|
|
764
1071
|
/** Pause between readiness polls (default 5 seconds). */
|
|
765
1072
|
readyIntervalMs?: number;
|
|
766
1073
|
}
|
|
767
|
-
/** The ref the AI management key is delivered under; the host's `AiOrganisationPort` adapter reads it back. */
|
|
768
|
-
export declare const PLATFORM_AI_MANAGEMENT_KEY_REF = "ai:platform:managementKey";
|
|
769
1074
|
/**
|
|
770
1075
|
* What Archon's management key may do in ai: exactly `ai`'s own
|
|
771
1076
|
* `ARCHON_AI_PERMISSIONS` (`apps/host/scripts/authz/bootstrap-archon.ts`) --
|
|
@@ -947,6 +1252,8 @@ export interface PlatformFilesRuntime {
|
|
|
947
1252
|
readyTimeoutMs?: number;
|
|
948
1253
|
/** Pause between readiness polls (default 5 seconds). */
|
|
949
1254
|
readyIntervalMs?: number;
|
|
1255
|
+
/** Called with every secret this step reads or mints, so the host's run can redact it. */
|
|
1256
|
+
onSecret?: (value: string) => void;
|
|
950
1257
|
}
|
|
951
1258
|
/**
|
|
952
1259
|
* #171: how the host signs a files management request with the key this step
|
|
@@ -960,10 +1267,74 @@ export interface PlatformFilesRuntime {
|
|
|
960
1267
|
*/
|
|
961
1268
|
export declare const FILES_MANAGEMENT_ISSUER = "foundry";
|
|
962
1269
|
export declare const FILES_MANAGEMENT_KEY_ID = "foundry-1";
|
|
963
|
-
/** The ref the management private key (PKCS#8 PEM) is delivered under. */
|
|
964
|
-
export declare const FILES_MANAGEMENT_KEY_REF = "files:platform:managementKey";
|
|
965
1270
|
/** The ref the files host's introspection client secret is delivered under. */
|
|
966
1271
|
export declare const FILES_INTROSPECTION_SECRET_REF = "zitadel:platform:files:introspection";
|
|
1272
|
+
export declare const PLATFORM_FILES_DB_NAME = "platform-files-db";
|
|
1273
|
+
/** The two Coolify apps files' `apps/host/README.md` ("Deploying on Coolify") names. */
|
|
1274
|
+
export declare const FILES_MIGRATE_APP_NAME = "files-migrate";
|
|
1275
|
+
export declare const FILES_RUNNER_APP_NAME = "files";
|
|
1276
|
+
/**
|
|
1277
|
+
* The runner's fixed `custom_internal_name`: the name another container on
|
|
1278
|
+
* the Coolify network reaches it by, and so the host part of
|
|
1279
|
+
* {@link PLATFORM_FILES_INTERNAL_URL_SCOPE_KEY}'s recorded address.
|
|
1280
|
+
*/
|
|
1281
|
+
export declare const FILES_RUNNER_INTERNAL_NAME = "platform-files";
|
|
1282
|
+
export interface FilesHostInput {
|
|
1283
|
+
/** The runner's `APP_URL` host, the management verifier's audience. */
|
|
1284
|
+
hostname: string;
|
|
1285
|
+
gitRef: string;
|
|
1286
|
+
project: ProjectSource;
|
|
1287
|
+
/** `AUTH_ISSUER`, and the sign-in instance the files ZITADEL project is made in. */
|
|
1288
|
+
issuer: string;
|
|
1289
|
+
/** The ZITADEL organisation the files project lives in: found or made by name, or already known by id. */
|
|
1290
|
+
zitadelOrg: {
|
|
1291
|
+
name: string;
|
|
1292
|
+
} | {
|
|
1293
|
+
id: string;
|
|
1294
|
+
};
|
|
1295
|
+
/** The files ZITADEL project (its id is the person-token audience) and the scope key its id is recorded under. */
|
|
1296
|
+
zitadelProject: {
|
|
1297
|
+
name: string;
|
|
1298
|
+
scopeKey: string;
|
|
1299
|
+
};
|
|
1300
|
+
/** {@link filesShareDomains} of the orgs this host serves: each is a runner domain and a public host. */
|
|
1301
|
+
shareDomains: readonly string[];
|
|
1302
|
+
/** An own org's `AUTHZ_BOOTSTRAP_OPERATOR`: the platform owner's id, from the platform's recorded state. */
|
|
1303
|
+
bootstrapOperator?: string;
|
|
1304
|
+
/** An own org's keys credential id: recorded as the guard once both apps carry the keyring. */
|
|
1305
|
+
keysGuardId?: string;
|
|
1306
|
+
}
|
|
1307
|
+
/** The credential id of the keyring an own files host runs on; recorded once both apps carry it. */
|
|
1308
|
+
export declare const OWN_FILES_KEYS_SCOPE_KEY = "files_keys_id";
|
|
1309
|
+
/** The credential id minted and not yet confirmed delivered. */
|
|
1310
|
+
export declare const OWN_FILES_KEYS_UNDELIVERED_SCOPE_KEY = "files_keys_undelivered";
|
|
1311
|
+
/**
|
|
1312
|
+
* An own files host's keyring, before its env writes. Both refs held: those values, no mint (a rerun
|
|
1313
|
+
* after a failed run resumes here). Nothing held: one mint, which is checked, registered as a
|
|
1314
|
+
* secret, recorded by id as undelivered, then delivered to both refs. A re-mint is harmless: it
|
|
1315
|
+
* yields an equivalent credential. Returns the guard id for {@link FilesHostInput.keysGuardId}.
|
|
1316
|
+
*/
|
|
1317
|
+
export declare function resolveOwnFilesKeys(scope: StepScope, deps: {
|
|
1318
|
+
deliverSecret: PlatformFilesDeps['deliverSecret'];
|
|
1319
|
+
readSecret?: (ref: string) => Promise<string | null>;
|
|
1320
|
+
runtime?: PlatformFilesDeps['runtime'];
|
|
1321
|
+
}, target: ServiceTarget, mint: () => Promise<FilesKeysMinted>, workerUrl: string, label: string): Promise<{
|
|
1322
|
+
keys: {
|
|
1323
|
+
workerUrl: string;
|
|
1324
|
+
credential: string;
|
|
1325
|
+
verifyKeys: string;
|
|
1326
|
+
};
|
|
1327
|
+
guardId: string;
|
|
1328
|
+
}>;
|
|
1329
|
+
/**
|
|
1330
|
+
* The body of {@link provisionPlatformFiles} for any files host: the
|
|
1331
|
+
* platform's, or an own org's. Every name, the internal name and the two
|
|
1332
|
+
* held-key refs come from `target`; the project, sign-in and ZITADEL home
|
|
1333
|
+
* come from `input`. See {@link provisionPlatformFiles} for what the step does.
|
|
1334
|
+
*/
|
|
1335
|
+
export declare function provisionFilesHost(scope: StepScope, deps: PlatformFilesDeps, target: ServiceTarget, input: FilesHostInput): Promise<{
|
|
1336
|
+
resourceId?: string;
|
|
1337
|
+
}>;
|
|
967
1338
|
export interface PlatformEdgeRuntime {
|
|
968
1339
|
fetch?: typeof fetch;
|
|
969
1340
|
sleep?: (ms: number) => Promise<void>;
|
|
@@ -1041,6 +1412,20 @@ export interface PlatformHcDeps {
|
|
|
1041
1412
|
/** Clock, sleep and fetch for the readiness wait; real ones by default. */
|
|
1042
1413
|
runtime?: PlatformHcRuntime;
|
|
1043
1414
|
}
|
|
1415
|
+
export declare function filesKeys(deps: PlatformFilesDeps | undefined): {
|
|
1416
|
+
workerUrl: string;
|
|
1417
|
+
credential: string;
|
|
1418
|
+
verifyKeys: string;
|
|
1419
|
+
} | null;
|
|
1420
|
+
/**
|
|
1421
|
+
* #380: the files host's own checks on what the host supplies, mirrored from
|
|
1422
|
+
* `apps/host/src/lib/authz/files-host.ts` (`R2_ENDPOINT`: a bare https origin)
|
|
1423
|
+
* and `src/lib/keys/config.ts` (`KEYS_VERIFY_KEYS`: 1 or 2 comma-separated
|
|
1424
|
+
* base64url values, each exactly 32 bytes), so a bad value blocks the step
|
|
1425
|
+
* naming the field instead of deploying a host that never turns healthy.
|
|
1426
|
+
* Never echoes the value.
|
|
1427
|
+
*/
|
|
1428
|
+
export declare function filesDepsInvalid(deps: PlatformFilesDeps | undefined): string | null;
|
|
1044
1429
|
/**
|
|
1045
1430
|
* #174: everything {@link provisionPlatformIntegrations} needs beyond
|
|
1046
1431
|
* {@link PlatformStepExecutionContext} -- a bundle, like
|
|
@@ -1232,6 +1617,16 @@ export declare class ProvidersPlatformCoolifyReader implements PlatformCoolifyRe
|
|
|
1232
1617
|
constructor(client: Pick<CoolifyPort, 'getApplication' | 'getDatabase' | 'listDatabaseBackups' | 'listEnvs' | 'raw'>, platformStore: Pick<PlatformStore, 'getServiceIds'>);
|
|
1233
1618
|
listResources(): Promise<LiveResource[]>;
|
|
1234
1619
|
}
|
|
1620
|
+
/**
|
|
1621
|
+
* `findZoneByApex` returns the longest suffix match, so for a subdomain it
|
|
1622
|
+
* answers with the parent zone. The platform's zone is its apex exactly;
|
|
1623
|
+
* any other zone is never adopted or reported as the platform's.
|
|
1624
|
+
*/
|
|
1625
|
+
export declare function exactZone<Z extends {
|
|
1626
|
+
name: string;
|
|
1627
|
+
}>(client: {
|
|
1628
|
+
findZoneByApex(domain: string): Promise<Z | undefined>;
|
|
1629
|
+
}, apex: string): Promise<Z | undefined>;
|
|
1235
1630
|
/** Scope id (no `:`): the share domains the runner's Coolify `domains` was last set to, comma-joined. */
|
|
1236
1631
|
export declare const PLATFORM_FILES_SHARE_DOMAINS_SCOPE_KEY = "platform_files_share_domains";
|
|
1237
1632
|
/**
|