@wtfalch/foundry 0.4.0 → 0.6.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 +111 -0
- package/dist/bootstrap/bootstrap.js +323 -0
- package/dist/bootstrap/declare.d.ts +36 -0
- package/dist/bootstrap/declare.js +99 -0
- package/dist/bootstrap/drive.d.ts +40 -0
- package/dist/bootstrap/drive.js +51 -0
- package/dist/bootstrap/index.d.ts +3 -0
- package/dist/bootstrap/index.js +1 -0
- package/dist/calls-sfu-deploy-support.d.ts +17 -6
- package/dist/calls-sfu-deploy-support.js +19 -7
- package/dist/calls-sfu-keys.d.ts +21 -0
- package/dist/calls-sfu-keys.js +27 -0
- package/dist/calls-sfu-ssh-adapter.d.ts +55 -0
- package/dist/calls-sfu-ssh-adapter.js +78 -0
- package/dist/calls-sfu-ssh-client.d.ts +49 -0
- package/dist/calls-sfu-ssh-client.js +82 -0
- package/dist/calls-sfu.d.ts +36 -12
- package/dist/calls-sfu.js +41 -9
- package/dist/index.d.ts +38 -7
- package/dist/index.js +20 -4
- package/dist/migrations/0016_platform.sql +61 -0
- package/dist/migrations/0017_requested_changes.sql +42 -0
- package/dist/migrations/0018_platform_apply.sql +19 -0
- package/dist/migrations/0019_mail_owner_mailbox.sql +15 -0
- package/dist/migrations/0020_mail_apps.sql +7 -0
- package/dist/migrations/0021_mail_role_mailboxes.sql +6 -0
- package/dist/org.d.ts +81 -0
- package/dist/org.js +96 -0
- package/dist/plan/agora.d.ts +77 -0
- package/dist/plan/agora.js +127 -0
- package/dist/plan/cloudflared-connector.d.ts +70 -0
- package/dist/plan/cloudflared-connector.js +106 -0
- package/dist/plan/diff.d.ts +20 -3
- package/dist/plan/diff.js +713 -115
- package/dist/plan/executor.d.ts +680 -172
- package/dist/plan/executor.js +755 -189
- package/dist/plan/index.d.ts +28 -5
- package/dist/plan/index.js +15 -2
- package/dist/plan/jobs-scheduler-http.d.ts +29 -0
- package/dist/plan/jobs-scheduler-http.js +50 -0
- package/dist/plan/live.d.ts +51 -2
- package/dist/plan/live.js +8 -0
- package/dist/plan/mail-copy.d.ts +125 -0
- package/dist/plan/mail-copy.js +166 -0
- package/dist/plan/mail-identity-allowlist.d.ts +50 -0
- package/dist/plan/mail-identity-allowlist.js +11 -0
- package/dist/plan/mail-identity.d.ts +68 -0
- package/dist/plan/mail-identity.js +17 -0
- package/dist/plan/plan-apply.d.ts +197 -3
- package/dist/plan/plan-apply.js +278 -9
- package/dist/plan/platform-diff.d.ts +487 -0
- package/dist/plan/platform-diff.js +1220 -0
- package/dist/plan/platform-org-ports.d.ts +50 -0
- package/dist/plan/platform-org-ports.js +70 -0
- package/dist/plan/platform-org.d.ts +121 -0
- package/dist/plan/platform-org.js +115 -0
- package/dist/plan/platform-providers.d.ts +615 -0
- package/dist/plan/platform-providers.js +4410 -0
- package/dist/plan/platform-sfu.d.ts +128 -0
- package/dist/plan/platform-sfu.js +142 -0
- package/dist/plan/providers-readers.js +22 -1
- package/dist/plan/zitadel-machine-user.d.ts +83 -0
- package/dist/plan/zitadel-machine-user.js +238 -0
- package/dist/plan.d.ts +6 -0
- package/dist/platform.d.ts +55 -0
- package/dist/platform.js +46 -0
- package/dist/requested-changes.d.ts +118 -0
- package/dist/requested-changes.js +125 -0
- package/dist/slug.js +15 -2
- package/dist/store/audit.js +4 -2
- package/dist/store/postgres.d.ts +29 -1
- package/dist/store/postgres.js +203 -4
- package/dist/store/schema.d.ts +755 -0
- package/dist/store/schema.js +70 -0
- package/dist/store.d.ts +53 -0
- package/package.json +1 -1
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #33: whether Stalwart's OIDC directory audience rule actually binds the
|
|
3
|
+
* app that is carrying a token, not just whether the configured value looks
|
|
4
|
+
* right.
|
|
5
|
+
*
|
|
6
|
+
* The retired `checkMailIdentity` (app-template's `bootstrap/mail-
|
|
7
|
+
* identity.ts`) confirmed the directory's `requireAudience` matched what
|
|
8
|
+
* bootstrap configured, and that read as "the audience rule is enforced."
|
|
9
|
+
* It is not, for every app: Stalwart's OIDC directory lookup only enforces
|
|
10
|
+
* `requireAudience` on the JWT path (`authenticate_jwt`). A bearer token
|
|
11
|
+
* that is opaque instead goes through `authenticate_opaque`, which calls
|
|
12
|
+
* userinfo and builds the account -- checking neither audience nor scope at
|
|
13
|
+
* all (measured against `stalwartlabs/stalwart` `d9dee0a`,
|
|
14
|
+
* `crates/directory/src/backend/oidc/lookup.rs`). An app whose ZITADEL
|
|
15
|
+
* `accessTokenType` is opaque mints tokens that skip the rule entirely, no
|
|
16
|
+
* matter what the value check says.
|
|
17
|
+
*
|
|
18
|
+
* So {@link checkMailIdentityAudience} checks the audience *value* only --
|
|
19
|
+
* its name says so -- and separately names every opaque-minting app, so a
|
|
20
|
+
* clean value match stops reading as "and therefore enforced" for apps it
|
|
21
|
+
* never binds. Which apps are *allowed* to hold a JWT the rule does accept
|
|
22
|
+
* is a declared allow-list, out of scope here (#34).
|
|
23
|
+
*
|
|
24
|
+
* An exported pure function, decided in #33: the host supplies the live
|
|
25
|
+
* `requireAudience` value, the audience this org expects, and the apps in
|
|
26
|
+
* the audience project. This module reads no live state and calls nothing
|
|
27
|
+
* -- `apps` takes its own narrow shape rather than importing
|
|
28
|
+
* `@wtfalch/providers/zitadel`'s `ZitadelApp`, the same way `executor.ts`'s
|
|
29
|
+
* `ZitadelPort.listApps` already declares its own shape for a real
|
|
30
|
+
* `ZitadelClient` to satisfy structurally.
|
|
31
|
+
*/
|
|
32
|
+
export interface MailIdentityApp {
|
|
33
|
+
/** ZITADEL's own app id. */
|
|
34
|
+
id: string;
|
|
35
|
+
/** The OIDC client id ZITADEL issued -- what a fault names, never a secret. */
|
|
36
|
+
clientId: string;
|
|
37
|
+
name: string;
|
|
38
|
+
/**
|
|
39
|
+
* How ZITADEL mints this app's access token. `undefined` means unknown
|
|
40
|
+
* (an `api` app, which has no `oidcConfig` at all, or a caller that
|
|
41
|
+
* cannot yet tell) and is never treated as either value -- an app this
|
|
42
|
+
* function cannot classify is reported on its own, not folded into
|
|
43
|
+
* "safe".
|
|
44
|
+
*/
|
|
45
|
+
accessTokenType?: 'jwt' | 'opaque';
|
|
46
|
+
}
|
|
47
|
+
export interface MailIdentityAudienceCheck {
|
|
48
|
+
/** `null` when the directory's `requireAudience` matches `expectedAudience`; otherwise the reason it does not. */
|
|
49
|
+
audienceMismatch: string | null;
|
|
50
|
+
/**
|
|
51
|
+
* One line per app in the audience project that mints an opaque access
|
|
52
|
+
* token, naming the app and its client id -- Stalwart's audience rule
|
|
53
|
+
* never binds it, whatever `audienceMismatch` says. Empty means every app
|
|
54
|
+
* this function was given mints a JWT (or none were given).
|
|
55
|
+
*/
|
|
56
|
+
opaqueApps: string[];
|
|
57
|
+
/**
|
|
58
|
+
* One line per app this function could not classify at all (an
|
|
59
|
+
* `accessTokenType` of neither `'jwt'` nor `'opaque'`) -- worth a human
|
|
60
|
+
* look, since it is reported as neither enforced nor refused.
|
|
61
|
+
*/
|
|
62
|
+
unknownAccessTokenApps: string[];
|
|
63
|
+
}
|
|
64
|
+
export declare function checkMailIdentityAudience(input: {
|
|
65
|
+
requireAudience: string;
|
|
66
|
+
expectedAudience: string;
|
|
67
|
+
apps: readonly MailIdentityApp[];
|
|
68
|
+
}): MailIdentityAudienceCheck;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function checkMailIdentityAudience(input) {
|
|
2
|
+
const { requireAudience, expectedAudience, apps } = input;
|
|
3
|
+
const audienceMismatch = requireAudience === expectedAudience
|
|
4
|
+
? null
|
|
5
|
+
: `requireAudience is '${requireAudience}', expected '${expectedAudience}'`;
|
|
6
|
+
const opaqueApps = [];
|
|
7
|
+
const unknownAccessTokenApps = [];
|
|
8
|
+
for (const app of apps) {
|
|
9
|
+
if (app.accessTokenType === 'opaque') {
|
|
10
|
+
opaqueApps.push(`${app.name} (${app.clientId}) mints an opaque access token -- Stalwart's requireAudience does not bind it`);
|
|
11
|
+
}
|
|
12
|
+
else if (app.accessTokenType !== 'jwt') {
|
|
13
|
+
unknownAccessTokenApps.push(`${app.name} (${app.clientId}) has an unknown access token type`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return { audienceMismatch, opaqueApps, unknownAccessTokenApps };
|
|
17
|
+
}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import type { PlanApply } from '../apply.js';
|
|
2
2
|
import type { DeclaredOrg, OrgId } from '../org.js';
|
|
3
|
-
import type { Plan } from '../plan.js';
|
|
3
|
+
import type { Plan, PlanStep } from '../plan.js';
|
|
4
|
+
import type { DeclaredPlatform } from '../platform.js';
|
|
4
5
|
import type { AppliedState } from '../state.js';
|
|
5
|
-
import type { DeclaredOrgStore } from '../store.js';
|
|
6
|
+
import type { DeclaredOrgStore, PlatformStore } from '../store.js';
|
|
7
|
+
import { type AgoraHostPort } from './agora.js';
|
|
6
8
|
import { type StepAction } from './diff.js';
|
|
7
|
-
import { type LiveReaders, type LiveResource } from './live.js';
|
|
9
|
+
import { type LiveReaders, type LiveResource, type PlatformLiveReaders } from './live.js';
|
|
10
|
+
import { type HeldKeyResolver } from './platform-org.js';
|
|
11
|
+
import type { CallsSfuConnectionPort } from './platform-sfu.js';
|
|
8
12
|
/** Who did it, for `@wtfalch/audit`'s "who, when, the plan, the result" (#9, #19). */
|
|
9
13
|
export interface AuditActor {
|
|
10
14
|
readonly id: string;
|
|
@@ -27,6 +31,8 @@ export interface AuditLog {
|
|
|
27
31
|
actor: AuditActor;
|
|
28
32
|
orgId: OrgId;
|
|
29
33
|
plan: Plan;
|
|
34
|
+
/** `'platform'` for the shared platform's plan (#167), whose `orgId` is the owner org's; absent for an org's own. */
|
|
35
|
+
target?: 'platform';
|
|
30
36
|
at: Date;
|
|
31
37
|
}): Promise<void>;
|
|
32
38
|
recordApply(entry: {
|
|
@@ -35,6 +41,8 @@ export interface AuditLog {
|
|
|
35
41
|
plan: Plan;
|
|
36
42
|
outcome: 'applied' | 'refused' | 'failed';
|
|
37
43
|
reason?: string;
|
|
44
|
+
/** `'platform'` for the shared platform's apply (#167), whose `orgId` is the owner org's; absent for an org's own. */
|
|
45
|
+
target?: 'platform';
|
|
38
46
|
at: Date;
|
|
39
47
|
}): Promise<void>;
|
|
40
48
|
/** `FoundryPlanApply.destroy`'s own record -- no `Plan` involved, so this is its own entry shape rather than a reuse of `recordApply`'s. */
|
|
@@ -61,6 +69,13 @@ export interface AuthzGate {
|
|
|
61
69
|
* own permission rather than reusing `canApply`.
|
|
62
70
|
*/
|
|
63
71
|
canDestroy(actor: AuditActor, orgId: OrgId): Promise<boolean>;
|
|
72
|
+
/**
|
|
73
|
+
* Gates `applyPlatform` (#167): changing the shared platform every org
|
|
74
|
+
* runs on is a stronger action than applying one org, so it is its own
|
|
75
|
+
* permission. Optional so an existing host that only gates orgs keeps
|
|
76
|
+
* compiling; a gate without it refuses every platform apply.
|
|
77
|
+
*/
|
|
78
|
+
canApplyPlatform?(actor: AuditActor): Promise<boolean>;
|
|
64
79
|
}
|
|
65
80
|
/**
|
|
66
81
|
* What {@link StepExecutor.execute} needs beyond the {@link StepAction}
|
|
@@ -108,6 +123,13 @@ export interface StepExecutionContext {
|
|
|
108
123
|
* scope key can never collide with the `system:kind:key` form.
|
|
109
124
|
*/
|
|
110
125
|
recordScopeId(key: string, id: string): void;
|
|
126
|
+
/**
|
|
127
|
+
* Writes every id recorded so far to the store now, not only at the end of
|
|
128
|
+
* the run. For an id that must survive a crash mid-step: the ai key's id
|
|
129
|
+
* between its mint and its delivery. The end-of-run write is a superset of
|
|
130
|
+
* this one, since both write the same map.
|
|
131
|
+
*/
|
|
132
|
+
persistScopeIds(): Promise<void>;
|
|
111
133
|
}
|
|
112
134
|
/**
|
|
113
135
|
* Makes one {@link StepAction} true against a real system. No implementation
|
|
@@ -121,6 +143,63 @@ export interface StepExecutor {
|
|
|
121
143
|
resourceId?: string;
|
|
122
144
|
} | undefined>;
|
|
123
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* #167: {@link StepExecutionContext}'s platform-scoped counterpart -- what
|
|
148
|
+
* {@link PlatformStepExecutor.execute} needs beyond the {@link StepAction}
|
|
149
|
+
* itself, for the shared platform (`platform.ts`'s `DeclaredPlatform`) rather
|
|
150
|
+
* than one `DeclaredOrg`. A separate type rather than a reuse of
|
|
151
|
+
* `StepExecutionContext`: that type's `org` field is read directly by every
|
|
152
|
+
* real executor method in `executor.ts`, so widening it to also carry a
|
|
153
|
+
* platform would touch that whole file, which is out of scope here -- #167
|
|
154
|
+
* ships the plan/apply frame only, proven against fakes, the same way #19
|
|
155
|
+
* shipped org plan/apply before #20 wired `FoundryStepExecutor` against it.
|
|
156
|
+
*/
|
|
157
|
+
export interface PlatformStepExecutionContext {
|
|
158
|
+
readonly platform: DeclaredPlatform;
|
|
159
|
+
/** {@link StepExecutionContext.recordedIds}, for the platform's own run. */
|
|
160
|
+
recordedIds(): Readonly<Record<string, string>>;
|
|
161
|
+
/** {@link StepExecutionContext.recordScopeId}, for the platform's own run. */
|
|
162
|
+
recordScopeId(key: string, id: string): void;
|
|
163
|
+
/**
|
|
164
|
+
* Writes every id recorded so far to the platform's applied state now,
|
|
165
|
+
* rather than when the run ends or throws, so a crash between two calls
|
|
166
|
+
* cannot lose an id a provider already created. Optional: a caller that
|
|
167
|
+
* builds its own context (tests) may omit it.
|
|
168
|
+
*/
|
|
169
|
+
persist?(): Promise<void>;
|
|
170
|
+
}
|
|
171
|
+
/** {@link StepExecutor}, for the platform (#167) rather than an org. */
|
|
172
|
+
export interface PlatformStepExecutor {
|
|
173
|
+
/**
|
|
174
|
+
* Steps this executor cannot run, as fixed text (part of the stale-plan
|
|
175
|
+
* comparison, so it must not vary per call). The planner reports each as an
|
|
176
|
+
* unmanaged item and plans no step for it, the way a blocked Agora is
|
|
177
|
+
* reported, instead of planning a step that throws on every apply.
|
|
178
|
+
*/
|
|
179
|
+
blockedSteps?(platform: DeclaredPlatform, recordedIds: Readonly<Record<string, string>>): Promise<{
|
|
180
|
+
auth?: string;
|
|
181
|
+
/** #211 round 6: see `PlanPlatformOptions.blocked.authExecutorMissing`. */
|
|
182
|
+
authExecutorMissing?: boolean;
|
|
183
|
+
/** #216 round 5: see `PlanPlatformOptions.blocked.authOtherInputsMissing`. */
|
|
184
|
+
authOtherInputsMissing?: string[];
|
|
185
|
+
files?: string;
|
|
186
|
+
integrations?: string;
|
|
187
|
+
ai?: string;
|
|
188
|
+
archon?: string;
|
|
189
|
+
mail?: string;
|
|
190
|
+
}>;
|
|
191
|
+
/**
|
|
192
|
+
* Steps whose live state differs from the declared one in a way the recorded
|
|
193
|
+
* ids cannot show, read from the live system on each plan.
|
|
194
|
+
*/
|
|
195
|
+
driftedSteps?(platform: DeclaredPlatform, recordedIds: Readonly<Record<string, string>>): Promise<{
|
|
196
|
+
archon?: boolean;
|
|
197
|
+
auth?: boolean;
|
|
198
|
+
}>;
|
|
199
|
+
execute(action: StepAction, context: PlatformStepExecutionContext): Promise<{
|
|
200
|
+
resourceId?: string;
|
|
201
|
+
} | undefined>;
|
|
202
|
+
}
|
|
124
203
|
/**
|
|
125
204
|
* One resource `FoundryPlanApply.destroy` has decided to tear down: an entry
|
|
126
205
|
* from `AppliedState.serviceIds` (`resourceKey`'s `system:kind:key` form, or
|
|
@@ -168,6 +247,22 @@ export declare class NotAuthorizedError extends Error {
|
|
|
168
247
|
export declare class NotATestOrgError extends Error {
|
|
169
248
|
constructor(orgId: OrgId);
|
|
170
249
|
}
|
|
250
|
+
/** #167: {@link DeclaredOrgNotFoundError}, for the platform's own singleton record (#166). */
|
|
251
|
+
export declare class DeclaredPlatformNotFoundError extends Error {
|
|
252
|
+
constructor();
|
|
253
|
+
}
|
|
254
|
+
/** #167: {@link StalePlanError}, for `planPlatform`/`applyPlatform`. */
|
|
255
|
+
export declare class PlatformStalePlanError extends Error {
|
|
256
|
+
constructor();
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* #167: {@link ConfirmationRequiredError}, for `applyPlatform` -- typed as
|
|
260
|
+
* the literal word `platform` rather than a declared name, since
|
|
261
|
+
* {@link DeclaredPlatform} has no `displayName` of its own to type (#166).
|
|
262
|
+
*/
|
|
263
|
+
export declare class PlatformConfirmationRequiredError extends Error {
|
|
264
|
+
constructor();
|
|
265
|
+
}
|
|
171
266
|
export interface FoundryPlanApplyOptions {
|
|
172
267
|
store: DeclaredOrgStore;
|
|
173
268
|
readers: LiveReaders;
|
|
@@ -177,8 +272,51 @@ export interface FoundryPlanApplyOptions {
|
|
|
177
272
|
authz: AuthzGate;
|
|
178
273
|
/** The operator running this plan/apply — every audit row's "who" (#9, #19). */
|
|
179
274
|
actor: AuditActor;
|
|
275
|
+
/** Host-supplied Agora values (#164). Optional: without it, or if it throws, the Agora is reported blocked and every other step still plans. */
|
|
276
|
+
agora?: AgoraHostPort;
|
|
277
|
+
/**
|
|
278
|
+
* #173: true when the host's executor was built with a `jobsScheduler`
|
|
279
|
+
* port. Optional, and false without it: each hosted org's registration with
|
|
280
|
+
* the jobs scheduler is then reported blocked instead of planned, so the
|
|
281
|
+
* step never runs (and fails) where no scheduler is deployed.
|
|
282
|
+
*/
|
|
283
|
+
jobsScheduler?: boolean;
|
|
284
|
+
/**
|
|
285
|
+
* #177: true when the host's executor was built with its own `filesOrganisation`
|
|
286
|
+
* / `aiOrganisation` port. Without one, the org step needs the platform's
|
|
287
|
+
* key, so it is blocked while `platform.heldKeys` is absent or does not hold it.
|
|
288
|
+
*/
|
|
289
|
+
filesOrganisation?: boolean;
|
|
290
|
+
aiOrganisation?: boolean;
|
|
180
291
|
/** Overridable for tests; defaults to `() => new Date()`. */
|
|
181
292
|
now?: () => Date;
|
|
293
|
+
/**
|
|
294
|
+
* #167: dependencies for planning/applying the shared platform (#166)
|
|
295
|
+
* alongside an org. Optional so an org-only caller — every #19-era caller,
|
|
296
|
+
* including every existing org test in this file — is unaffected;
|
|
297
|
+
* `planPlatform`/`applyPlatform` throw a plain `Error` if called without
|
|
298
|
+
* this set.
|
|
299
|
+
*/
|
|
300
|
+
platform?: {
|
|
301
|
+
store: PlatformStore;
|
|
302
|
+
readers: PlatformLiveReaders;
|
|
303
|
+
executor: PlatformStepExecutor;
|
|
304
|
+
/**
|
|
305
|
+
* #170: read-only look at the held SFU connection. When the platform
|
|
306
|
+
* declares the SFU but this is absent, or resolves `null`, the plan
|
|
307
|
+
* reports the SFU as blocked (in `unmanaged`) instead of planning a step
|
|
308
|
+
* that would throw and halt every step after it.
|
|
309
|
+
*/
|
|
310
|
+
sfuConnection?: CallsSfuConnectionPort;
|
|
311
|
+
/**
|
|
312
|
+
* #177: Archon's held keys. An org plan resolves the platform's files,
|
|
313
|
+
* AI and jobs keys through it and blocks the org step (naming the ref,
|
|
314
|
+
* never a value) while one is not held. Absent: every service without a
|
|
315
|
+
* host-supplied port is blocked (`filesOrganisation`, `aiOrganisation`,
|
|
316
|
+
* `jobsScheduler` on the options), never planned to fail at apply.
|
|
317
|
+
*/
|
|
318
|
+
heldKeys?: HeldKeyResolver;
|
|
319
|
+
};
|
|
182
320
|
}
|
|
183
321
|
/**
|
|
184
322
|
* The #19 implementation of {@link PlanApply}: computes a plan against live
|
|
@@ -194,10 +332,30 @@ export declare class FoundryPlanApply implements PlanApply {
|
|
|
194
332
|
private readonly audit;
|
|
195
333
|
private readonly authz;
|
|
196
334
|
private readonly actor;
|
|
335
|
+
private readonly agora;
|
|
336
|
+
private readonly jobsScheduler;
|
|
337
|
+
private readonly hostFiles;
|
|
338
|
+
private readonly hostAi;
|
|
197
339
|
private readonly now;
|
|
340
|
+
private readonly platformDeps;
|
|
198
341
|
constructor(options: FoundryPlanApplyOptions);
|
|
199
342
|
plan(id: OrgId): Promise<Plan>;
|
|
200
343
|
apply(plan: Plan, confirmedOrgName: string): Promise<AppliedState>;
|
|
344
|
+
/**
|
|
345
|
+
* #167: {@link plan}, for the shared platform (#166) rather than an org --
|
|
346
|
+
* there is no id to pass, since `DeclaredPlatform` is one singleton record.
|
|
347
|
+
*/
|
|
348
|
+
planPlatform(): Promise<Plan>;
|
|
349
|
+
/**
|
|
350
|
+
* #167: {@link apply}, for the platform. `plan.orgId` is the platform's own
|
|
351
|
+
* `ownerOrgId` (`toPlatformPlan`'s own doc comment), so the same
|
|
352
|
+
* `AuditLog`/`AuthzGate` seams `apply` uses need no platform-specific
|
|
353
|
+
* shape of their own. `confirmedName` must be the literal word `platform`
|
|
354
|
+
* rather than a declared name -- `DeclaredPlatform` has none.
|
|
355
|
+
*/
|
|
356
|
+
applyPlatform(plan: Plan, confirmedName: string): Promise<{
|
|
357
|
+
serviceIds: Record<string, string>;
|
|
358
|
+
}>;
|
|
201
359
|
/**
|
|
202
360
|
* Deletes every resource foundry recorded for `id`, in reverse creation
|
|
203
361
|
* order, then the declared org and its applied state themselves (goal
|
|
@@ -211,7 +369,43 @@ export declare class FoundryPlanApply implements PlanApply {
|
|
|
211
369
|
*/
|
|
212
370
|
destroy(id: OrgId, confirmedOrgName: string): Promise<void>;
|
|
213
371
|
private refuse;
|
|
372
|
+
private refusePlatform;
|
|
214
373
|
private refuseDestroy;
|
|
215
374
|
private runSteps;
|
|
216
375
|
private computeFreshPlan;
|
|
376
|
+
/**
|
|
377
|
+
* #177: an org's plan reads the shared platform's own addresses and
|
|
378
|
+
* readiness through the very `platform` deps `planPlatform`/`applyPlatform`
|
|
379
|
+
* already use. With no platform wired or declared, {@link
|
|
380
|
+
* DEFAULT_ORG_PLATFORM_READINESS} reports every service not ready, so every
|
|
381
|
+
* org step that needs one is blocked.
|
|
382
|
+
*/
|
|
383
|
+
private orgPlatformReadiness;
|
|
384
|
+
/**
|
|
385
|
+
* #177: `AgoraHostValues` from the applied platform, the same
|
|
386
|
+
* `platformDeps` {@link orgPlatformReadiness} reads -- but only when the
|
|
387
|
+
* host supplies no `agora` port of its own. An explicit `agora` (#164)
|
|
388
|
+
* always wins, unchanged, so a host that already has its own values
|
|
389
|
+
* (perhaps not yet wired to this platform) keeps them.
|
|
390
|
+
*/
|
|
391
|
+
private orgAgoraHost;
|
|
392
|
+
/** #167: `FoundryPlanApplyOptions.platform`, or a plain `Error` naming what's missing. */
|
|
393
|
+
private requirePlatformDeps;
|
|
394
|
+
private computeFreshPlatformPlan;
|
|
395
|
+
private sfuBlockedReason;
|
|
396
|
+
/**
|
|
397
|
+
* {@link runSteps}, for the platform. The ids the steps return are recorded
|
|
398
|
+
* in `PlatformStore`'s applied state (`system:kind:key`, like an org's
|
|
399
|
+
* `serviceIds`), so a later plan finds a resource by its recorded id even
|
|
400
|
+
* when a provider's list has not caught up with a create.
|
|
401
|
+
*/
|
|
402
|
+
private runPlatformSteps;
|
|
217
403
|
}
|
|
404
|
+
/**
|
|
405
|
+
* #9 (resolved): whether any step in a plan needs its name typed before
|
|
406
|
+
* `apply` runs it. Shared by `apply` (the org's `displayName`) and
|
|
407
|
+
* `applyPlatform` (the literal word `platform`, #167) so the platform gets
|
|
408
|
+
* the identical gate an org already has, proven end-to-end above, rather
|
|
409
|
+
* than a second copy that could drift from it.
|
|
410
|
+
*/
|
|
411
|
+
export declare function planNeedsConfirmation(steps: readonly PlanStep[]): boolean;
|