@wtfalch/foundry 0.16.16 → 0.17.1
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/apply.d.ts +22 -1
- package/dist/bootstrap/bootstrap.d.ts +6 -1
- package/dist/bootstrap/bootstrap.js +26 -3
- package/dist/bootstrap/declare.d.ts +11 -1
- package/dist/bootstrap/declare.js +18 -6
- package/dist/index.d.ts +11 -6
- package/dist/index.js +8 -1
- package/dist/migrations/0038_calls_version_pin.sql +8 -0
- package/dist/migrations/0039_destroy_record.sql +7 -0
- package/dist/migrations/0040_company_included_uuid.sql +72 -0
- package/dist/migrations/0041_company_included_uuid_keys.sql +132 -0
- package/dist/migrations/0042_mutation_leases.sql +12 -0
- package/dist/migrations/0043_platform_names.sql +25 -0
- package/dist/migrations/0044_platform_mail_lettermint_project.sql +11 -0
- package/dist/org.d.ts +128 -3
- package/dist/org.js +186 -1
- package/dist/plan/adopt.d.ts +76 -0
- package/dist/plan/adopt.js +184 -0
- package/dist/plan/agora.d.ts +21 -0
- package/dist/plan/agora.js +37 -0
- package/dist/plan/broker-entries.d.ts +9 -1
- package/dist/plan/broker-entries.js +13 -5
- package/dist/plan/chat-matrix.d.ts +1 -2
- package/dist/plan/chat-matrix.js +15 -4
- package/dist/plan/core-files.d.ts +25 -0
- package/dist/plan/core-files.js +25 -0
- package/dist/plan/diff.d.ts +27 -3
- package/dist/plan/diff.js +264 -86
- package/dist/plan/edge-worker.d.ts +114 -0
- package/dist/plan/edge-worker.js +201 -0
- package/dist/plan/entry-ai-key.d.ts +3 -3
- package/dist/plan/entry-files-key.d.ts +3 -3
- package/dist/plan/entry-names-key.d.ts +73 -0
- package/dist/plan/entry-names-key.js +236 -0
- package/dist/plan/executor.d.ts +292 -24
- package/dist/plan/executor.js +745 -77
- package/dist/plan/index.d.ts +6 -2
- package/dist/plan/index.js +3 -1
- package/dist/plan/live.d.ts +9 -2
- package/dist/plan/login-host.d.ts +13 -2
- package/dist/plan/login-host.js +26 -7
- package/dist/plan/plan-apply.d.ts +153 -6
- package/dist/plan/plan-apply.js +639 -78
- package/dist/plan/platform-diff.d.ts +106 -1
- package/dist/plan/platform-diff.js +182 -2
- package/dist/plan/platform-org.d.ts +45 -1
- package/dist/plan/platform-org.js +34 -3
- package/dist/plan/platform-providers.d.ts +45 -1
- package/dist/plan/platform-providers.js +585 -16
- package/dist/plan/providers-readers.d.ts +2 -0
- package/dist/plan/providers-readers.js +4 -0
- package/dist/plan/rollout.d.ts +52 -0
- package/dist/plan/rollout.js +37 -0
- package/dist/plan/step-system.d.ts +22 -0
- package/dist/plan/step-system.js +38 -0
- package/dist/plan.d.ts +3 -0
- package/dist/platform.d.ts +22 -3
- package/dist/platform.js +6 -0
- package/dist/state.d.ts +13 -0
- package/dist/store/audit.js +15 -1
- package/dist/store/postgres.d.ts +1 -1
- package/dist/store/postgres.js +142 -15
- package/dist/store/schema.d.ts +202 -5
- package/dist/store/schema.js +18 -1
- package/dist/store.d.ts +47 -4
- package/dist/store.js +9 -1
- package/package.json +1 -1
package/dist/apply.d.ts
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import type { OrgId } from './org.js';
|
|
2
2
|
import type { Plan } from './plan.js';
|
|
3
3
|
import type { AppliedState } from './state.js';
|
|
4
|
+
/** One destroy step starting, finishing or failing; `index` counts from 1 up to `total`. */
|
|
5
|
+
export interface DestroyProgress {
|
|
6
|
+
step: string;
|
|
7
|
+
index: number;
|
|
8
|
+
total: number;
|
|
9
|
+
status: 'started' | 'finished' | 'failed';
|
|
10
|
+
error?: string;
|
|
11
|
+
}
|
|
4
12
|
/**
|
|
5
13
|
* The plan/apply seam https://github.com/wtfalch/foundry/issues/19
|
|
6
14
|
* implements.
|
|
@@ -37,5 +45,18 @@ export interface PlanApply {
|
|
|
37
45
|
* declared org in place with whatever remains recorded, so a retried
|
|
38
46
|
* `destroy` only tears down what's left.
|
|
39
47
|
*/
|
|
40
|
-
destroy(id: OrgId, confirmedOrgName: string
|
|
48
|
+
destroy(id: OrgId, confirmedOrgName: string, options?: {
|
|
49
|
+
onProgress?: (event: DestroyProgress) => void;
|
|
50
|
+
}): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* The steps `destroy` will run, in order, for a test org (same refusal as
|
|
53
|
+
* `destroy`: any other org throws). Read-only. A stopped destroy's
|
|
54
|
+
* finished and failed steps are `AppliedState.destroy`. `steps` are exactly
|
|
55
|
+
* what `destroy` runs, in order; `kept` lists the adopted resources it
|
|
56
|
+
* leaves alone (archon#271). Refuses when an adopted record cannot be read.
|
|
57
|
+
*/
|
|
58
|
+
planDestroy(id: OrgId): Promise<{
|
|
59
|
+
steps: string[];
|
|
60
|
+
kept: string[];
|
|
61
|
+
}>;
|
|
41
62
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Plan } from '../plan.js';
|
|
2
|
+
import type { PlatformLiveReaders, PlatformStepExecutor, StepSystem } from '../plan/index.js';
|
|
2
3
|
import type { FoundryPlatformStepExecutorOptions } from '../plan/platform-providers.js';
|
|
3
4
|
import type { FoundryStoreDb } from '../store/postgres.js';
|
|
4
5
|
import type { BootstrapCompany, BootstrapOwner } from './declare.js';
|
|
@@ -29,6 +30,8 @@ export interface RootCredentials {
|
|
|
29
30
|
/** What Archon's onboarding screen shows: one event per phase or round. */
|
|
30
31
|
export interface BootstrapProgress {
|
|
31
32
|
phase: 'declare' | 'plan' | 'apply' | 'done';
|
|
33
|
+
/** The system the event touches: the steps' own when a round's steps share one, `postgres` for a declare, `foundry` otherwise. */
|
|
34
|
+
system: StepSystem;
|
|
32
35
|
round?: number;
|
|
33
36
|
message: string;
|
|
34
37
|
steps?: string[];
|
|
@@ -100,6 +103,8 @@ export declare class Redactor {
|
|
|
100
103
|
addDeep(value: unknown, depth?: number, seen?: Set<unknown>): void;
|
|
101
104
|
text(input: string): string;
|
|
102
105
|
}
|
|
106
|
+
/** The one system every step of `plan` touches, or `foundry` when they differ or there are none. */
|
|
107
|
+
export declare function roundSystem(plan: Plan): StepSystem;
|
|
103
108
|
/** Wraps the secret-carrying ports inside one step's deps so what they deliver or resolve is redacted. */
|
|
104
109
|
export declare function guardStepDeps(executor: BootstrapExecutorDeps | undefined, redactor: Redactor, deliverSecret: (ref: string, secret: string) => Promise<void>, heldKeys: BootstrapPlatformDeps['heldKeys']): BootstrapExecutorDeps;
|
|
105
110
|
/**
|
|
@@ -61,6 +61,12 @@ const unusedOrgExecutor = {
|
|
|
61
61
|
throw new Error('bootstrapPlatform never destroys an org');
|
|
62
62
|
},
|
|
63
63
|
};
|
|
64
|
+
/** The one system every step of `plan` touches, or `foundry` when they differ or there are none. */
|
|
65
|
+
export function roundSystem(plan) {
|
|
66
|
+
const systems = new Set(plan.steps.map((s) => s.system));
|
|
67
|
+
const [only] = systems;
|
|
68
|
+
return systems.size === 1 && only ? only : 'foundry';
|
|
69
|
+
}
|
|
64
70
|
const unusedOrgReader = { listResources: async () => [] };
|
|
65
71
|
/** The audit log with every free-text field run through the redactor before it is stored. */
|
|
66
72
|
function redactingAudit(inner, redactor) {
|
|
@@ -76,6 +82,10 @@ function redactingAudit(inner, redactor) {
|
|
|
76
82
|
plan: plan(entry.plan),
|
|
77
83
|
...(entry.reason === undefined ? {} : { reason: redactor.text(entry.reason) }),
|
|
78
84
|
}),
|
|
85
|
+
recordAdoption: (entry) => inner.recordAdoption({
|
|
86
|
+
...entry,
|
|
87
|
+
...(entry.reason === undefined ? {} : { reason: redactor.text(entry.reason) }),
|
|
88
|
+
}),
|
|
79
89
|
recordDestroy: (entry) => inner.recordDestroy({
|
|
80
90
|
...entry,
|
|
81
91
|
...(entry.reason === undefined ? {} : { reason: redactor.text(entry.reason) }),
|
|
@@ -189,20 +199,29 @@ export async function bootstrapPlatform(input, deps) {
|
|
|
189
199
|
const orgStore = createPostgresDeclaredOrgStore(deps.db);
|
|
190
200
|
const platformStore = createPostgresPlatformStore(deps.db);
|
|
191
201
|
if (await orgStore.get(COMPANY_INCLUDED_ORG_ID)) {
|
|
192
|
-
emit({ phase: 'declare', message: 'declared org already written' });
|
|
202
|
+
emit({ phase: 'declare', system: 'postgres', message: 'declared org already written' });
|
|
193
203
|
}
|
|
194
204
|
else {
|
|
195
205
|
await orgStore.create(declaredCompanyOrg(input.company, input.owner));
|
|
196
|
-
emit({
|
|
206
|
+
emit({
|
|
207
|
+
phase: 'declare',
|
|
208
|
+
system: 'postgres',
|
|
209
|
+
message: `declared org "${input.company.name}" written`,
|
|
210
|
+
});
|
|
197
211
|
}
|
|
198
212
|
const existing = await platformStore.get();
|
|
199
213
|
if (existing) {
|
|
200
|
-
emit({
|
|
214
|
+
emit({
|
|
215
|
+
phase: 'declare',
|
|
216
|
+
system: 'postgres',
|
|
217
|
+
message: `declared platform already written for ${existing.apex}`,
|
|
218
|
+
});
|
|
201
219
|
// Only serverIpv4, through the validating `put`; no other field is touched.
|
|
202
220
|
if (input.serverIpv4 && input.serverIpv4 !== existing.serverIpv4) {
|
|
203
221
|
await platformStore.put({ ...existing, serverIpv4: input.serverIpv4 });
|
|
204
222
|
emit({
|
|
205
223
|
phase: 'declare',
|
|
224
|
+
system: 'postgres',
|
|
206
225
|
message: `declared platform serverIpv4 set to ${input.serverIpv4}`,
|
|
207
226
|
});
|
|
208
227
|
}
|
|
@@ -211,6 +230,7 @@ export async function bootstrapPlatform(input, deps) {
|
|
|
211
230
|
await platformStore.put(declaredPlatformFor(input.company.apex, input.serverIpv4));
|
|
212
231
|
emit({
|
|
213
232
|
phase: 'declare',
|
|
233
|
+
system: 'postgres',
|
|
214
234
|
message: `declared platform written for ${input.company.apex}`,
|
|
215
235
|
});
|
|
216
236
|
}
|
|
@@ -278,12 +298,14 @@ export async function bootstrapPlatform(input, deps) {
|
|
|
278
298
|
maxRounds: deps.maxRounds,
|
|
279
299
|
onPlan: (round, plan) => emit({
|
|
280
300
|
phase: 'plan',
|
|
301
|
+
system: roundSystem(plan),
|
|
281
302
|
round,
|
|
282
303
|
message: `round ${round}: ${plan.steps.length} step(s) planned`,
|
|
283
304
|
steps: plan.steps.map((s) => s.description),
|
|
284
305
|
}),
|
|
285
306
|
onApplied: (round, plan) => emit({
|
|
286
307
|
phase: 'apply',
|
|
308
|
+
system: roundSystem(plan),
|
|
287
309
|
round,
|
|
288
310
|
message: `round ${round}: applied`,
|
|
289
311
|
steps: plan.steps.map((s) => s.description),
|
|
@@ -310,6 +332,7 @@ export async function bootstrapPlatform(input, deps) {
|
|
|
310
332
|
};
|
|
311
333
|
emit({
|
|
312
334
|
phase: 'done',
|
|
335
|
+
system: 'foundry',
|
|
313
336
|
message: driven.failed
|
|
314
337
|
? `stopped: ${result.failed?.message}`
|
|
315
338
|
: `${result.applied.length} step(s) applied, ${blocked.length} blocked`,
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import type { DeclaredOrg, OrgId, OwnerIdentity } from '../org.js';
|
|
2
2
|
import type { DeclaredPlatform } from '../platform.js';
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* Company Included's own org id, the same literal `test/fakes.ts`'s
|
|
5
|
+
* `basePlatform()` uses for `ownerOrgId`. Must be a uuid: Archon's company
|
|
6
|
+
* pages resolve a declared org only by uuid (archon issue #40,
|
|
7
|
+
* `src/lib/companies/index.ts`'s `getCompany`/`isUuid`), so a non-uuid id
|
|
8
|
+
* 404s every one of them, Company Included's included (foundry#435).
|
|
9
|
+
* `migrations/0040_company_included_uuid.sql` rewrites the live row and
|
|
10
|
+
* every column that references it from the old literal
|
|
11
|
+
* `org_company_included` to this value; this constant and that migration's
|
|
12
|
+
* literal must agree.
|
|
13
|
+
*/
|
|
4
14
|
export declare const COMPANY_INCLUDED_ORG_ID: OrgId;
|
|
5
15
|
export declare const DEFAULT_GIT_REF = "main";
|
|
6
16
|
/**
|
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
import { validateOwners } from '../org.js';
|
|
2
2
|
import { PLATFORM_SERVICE_KEYS, defaultPlatformHostname } from '../platform.js';
|
|
3
3
|
import { VERSIONS } from './versions.js';
|
|
4
|
-
/**
|
|
5
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Company Included's own org id, the same literal `test/fakes.ts`'s
|
|
6
|
+
* `basePlatform()` uses for `ownerOrgId`. Must be a uuid: Archon's company
|
|
7
|
+
* pages resolve a declared org only by uuid (archon issue #40,
|
|
8
|
+
* `src/lib/companies/index.ts`'s `getCompany`/`isUuid`), so a non-uuid id
|
|
9
|
+
* 404s every one of them, Company Included's included (foundry#435).
|
|
10
|
+
* `migrations/0040_company_included_uuid.sql` rewrites the live row and
|
|
11
|
+
* every column that references it from the old literal
|
|
12
|
+
* `org_company_included` to this value; this constant and that migration's
|
|
13
|
+
* literal must agree.
|
|
14
|
+
*/
|
|
15
|
+
export const COMPANY_INCLUDED_ORG_ID = 'e4d20889-a63d-42b6-b670-2bdd4472449b';
|
|
6
16
|
export const DEFAULT_GIT_REF = 'main';
|
|
7
17
|
/**
|
|
8
18
|
* The Stalwart image tag the platform's mail step runs. `platform.mail.gitRef`
|
|
@@ -13,10 +23,12 @@ export const DEFAULT_GIT_REF = 'main';
|
|
|
13
23
|
*/
|
|
14
24
|
export const DEFAULT_MAIL_IMAGE_TAG = VERSIONS.stalwart;
|
|
15
25
|
/**
|
|
16
|
-
* Every platform service except `sfu` is enabled, like the other
|
|
17
|
-
* service whose dependencies the host did not supply is reported
|
|
18
|
-
* planned. `sfu` stays off because it needs a held SSH key and
|
|
19
|
-
* fingerprint, a separate human step (issue 170).
|
|
26
|
+
* Every platform service except `sfu` and `names` is enabled, like the other
|
|
27
|
+
* steps: a service whose dependencies the host did not supply is reported
|
|
28
|
+
* blocked, not planned. `sfu` stays off because it needs a held SSH key and
|
|
29
|
+
* host fingerprint, a separate human step (issue 170). `names` stays off
|
|
30
|
+
* because the names service (N1) does not exist yet -- a human deploys it
|
|
31
|
+
* and sets its hostname before enabling it (#317's own "Human steps").
|
|
20
32
|
*/
|
|
21
33
|
const SERVICES_ENABLED_BY_DEFAULT = new Set([
|
|
22
34
|
'auth',
|
package/dist/index.d.ts
CHANGED
|
@@ -2,20 +2,25 @@ export type { OrgId, MailMode, MailConfig, ChatConfig, CallsConfig, IssuerConfig
|
|
|
2
2
|
export { APP_ENTRY_KEY, FILES_ENTRY_PERMISSIONS, AI_ENTRY_PERMISSIONS, DEFAULT_MAIL_APPS, appEntry, ownerMailboxes, validateBuyerIdentity, validateHosting, validateMail, validateOwners, validateSite, } from './org.js';
|
|
3
3
|
export type { DeclaredPlatform, PlatformService, PlatformServiceKey } from './platform.js';
|
|
4
4
|
export { PLATFORM_SERVICE_KEYS, defaultPlatformHostname, validatePlatform, } from './platform.js';
|
|
5
|
+
import { type PinnedVersions } from './bootstrap/versions.js';
|
|
6
|
+
export type { PinnedVersions };
|
|
7
|
+
export declare const PINNED_VERSIONS: Readonly<PinnedVersions>;
|
|
5
8
|
export type { RequestableField, CallsChange, StorageLimitChange, AiLimitChange, DomainChange, MailTierChange, RequestedChangeValue, ChangeClassification, RequestedChangeState, RequestedChange, } from './requested-changes.js';
|
|
6
9
|
export { REQUESTABLE_FIELDS, classifyChange, RequestedChangeError } from './requested-changes.js';
|
|
7
10
|
export type { SlugRejectionReason, SlugRejection } from './slug.js';
|
|
8
11
|
export { validateSlug, slugFromName, RESERVED_SLUGS } from './slug.js';
|
|
9
|
-
export type { Checkpoint, LastApplied, AppliedState } from './state.js';
|
|
12
|
+
export type { Checkpoint, LastApplied, AppliedState, DestroyRecord, DestroyStepRecord, } from './state.js';
|
|
10
13
|
export type { PlanStep, UnmanagedItem, Plan } from './plan.js';
|
|
11
|
-
export type { AppliedVersion, AppliedVersionsStore, DeclaredOrgStore, PlatformStore, RequestedChangeStore, StaleUpdate, UpdateOptions, } from './store.js';
|
|
12
|
-
export
|
|
14
|
+
export type { AppliedVersion, AppliedVersionsStore, DeclaredOrgStore, HeldLease, MutationLease, PlatformStore, RequestedChangeStore, StaleUpdate, UpdateOptions, } from './store.js';
|
|
15
|
+
export { StaleLeaseError } from './store.js';
|
|
16
|
+
export type { PlanApply, DestroyProgress } from './apply.js';
|
|
13
17
|
export { createPostgresAppliedVersionsStore, createPostgresDeclaredOrgStore, createPostgresPlatformStore, createPostgresRequestedChangeStore, DeclaredOrgError, PlatformError, } from './store/postgres.js';
|
|
14
18
|
export { createPostgresAuditLog } from './store/audit.js';
|
|
15
19
|
export type { FoundryStoreDb } from './store/postgres.js';
|
|
16
20
|
export { declaredOrgs, foundryAppliedState, foundryAppliedVersions, foundryAuditEvents, foundryDeclaredPlatform, foundryRequestedChanges, type AuditEventRow, type DeclaredOrgRow, type NewDeclaredOrgRow, type AppliedStateRow, type NewAppliedStateRow, type AppliedVersionRow, type NewAppliedVersionRow, type DeclaredPlatformRow, type NewDeclaredPlatformRow, type RequestedChangeRow, type NewRequestedChangeRow, } from './store/schema.js';
|
|
17
|
-
export type { LiveResource, ZitadelReader, CoolifyReader, CloudflareReader, FilesReader, AiReader, CmsReader, LiveReaders, PlatformCloudflareReader, PlatformCoolifyReader, PlatformLiveReaders, StepAction, PlannedStep, PlanDetail, PlatformPlanDetail, AuditActor, AuditLog, AuthzGate, StepExecutor, StepDestroyer, DestroyTarget, StepExecutionContext, PlatformStepExecutor, PlatformStepExecutionContext, FoundryPlanApplyOptions, } from './plan/index.js';
|
|
18
|
-
export {
|
|
21
|
+
export type { LiveResource, ZitadelReader, CoolifyReader, CloudflareReader, FilesReader, AiReader, CmsReader, LiveReaders, PlatformCloudflareReader, PlatformCoolifyReader, PlatformLiveReaders, StepAction, PlannedStep, StepSystem, PlanDetail, PlatformPlanDetail, AdoptCandidate, AdoptInput, AdoptPlan, AdoptSnapshot, AuditActor, AuditLog, AuthzGate, StepExecutor, StepDestroyer, DestroyTarget, StepExecutionContext, PlatformStepExecutor, PlatformStepExecutionContext, FoundryPlanApplyOptions, } from './plan/index.js';
|
|
22
|
+
export { AdoptRefusedError } from './plan/index.js';
|
|
23
|
+
export { readLiveResources, readLivePlatformResources, computePlanDetail, toPlan, planPlatform, toPlatformPlan, JOBS_SCHEDULER_KEY, PLATFORM_AI_KEY, PLATFORM_AI_INTERNAL_URL_SCOPE_KEY, PLATFORM_AI_MANAGEMENT_KEY_SCOPE_KEY, PLATFORM_AI_ORGANISATION_SCOPE_KEY, PLATFORM_FILES_KEY, PLATFORM_FILES_INTERNAL_URL_SCOPE_KEY, PLATFORM_FILES_ZITADEL_PROJECT_SCOPE_KEY, PLATFORM_MAIL_INTERNAL_URL_SCOPE_KEY, PLATFORM_MAIL_ZITADEL_PROJECT_SCOPE_KEY, PLATFORM_NAMES_KEY, PLATFORM_NAMES_KEY_REF, PLATFORM_NAMES_CLOUDFLARE_TOKEN_REF, FoundryPlanApply, DeclaredOrgNotFoundError, StalePlanError, ConfirmationRequiredError, NotAuthorizedError, NotATestOrgError, DeclaredPlatformNotFoundError, PlatformStalePlanError, PlatformConfirmationRequiredError, planNeedsConfirmation, } from './plan/index.js';
|
|
19
24
|
export type { HeldKeyResolver, OrgPlatformReadiness } from './plan/index.js';
|
|
20
25
|
export { DEFAULT_ORG_PLATFORM_READINESS, ORG_PLATFORM_KEY_REFS, agoraHostValuesFromPlatform, heldPlatformKeyRefs, orgPlatformReadinessFrom, } from './plan/index.js';
|
|
21
26
|
export type { PlatformOrgClientFactories } from './plan/index.js';
|
|
@@ -30,7 +35,7 @@ export { checkMailIdentityAudience } from './plan/index.js';
|
|
|
30
35
|
export type { MailAllowlistApp } from './plan/index.js';
|
|
31
36
|
export { checkMailAppsAllowlist } from './plan/index.js';
|
|
32
37
|
export { FoundryPlatformStepExecutor, ProvidersPlatformCloudflareReader, ProvidersPlatformCoolifyReader, createZitadelAdminMinter, createZitadelProjectRoles, PLATFORM_AUTH_KEY, PLATFORM_MAIL_KEY, PLATFORM_MAIL_ADMIN_KEY_DESCRIPTION, } from './plan/index.js';
|
|
33
|
-
export type { PlatformAuthDeps, PlatformAuthRuntime, PlatformFilesDeps, PlatformFilesRuntime, } from './plan/index.js';
|
|
38
|
+
export type { PlatformAuthDeps, PlatformAuthRuntime, PlatformFilesDeps, PlatformFilesRuntime, PlatformNamesDeps, PlatformNamesRuntime, } from './plan/index.js';
|
|
34
39
|
export type { PlatformMailDeps, PlatformMailAdminPort, PlatformMailRuntime, } from './plan/index.js';
|
|
35
40
|
export { FILES_INTROSPECTION_SECRET_REF, FILES_MANAGEMENT_ISSUER, FILES_MANAGEMENT_KEY_ID, FILES_MANAGEMENT_KEY_REF, PLATFORM_AUTH_INSTANCE_BEARER_REF, } from './plan/index.js';
|
|
36
41
|
export { AI_MANAGEMENT_ISSUER, AI_MANAGEMENT_KEY_ID, AI_SIGNING_KEY_REF, AiManagementKeyExistsError, createAiManagementKeyMinter, PLATFORM_AI_MANAGEMENT_KEY_REF, PLATFORM_AI_MANAGEMENT_PERMISSIONS, } from './plan/index.js';
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
export { APP_ENTRY_KEY, FILES_ENTRY_PERMISSIONS, AI_ENTRY_PERMISSIONS, DEFAULT_MAIL_APPS, appEntry, ownerMailboxes, validateBuyerIdentity, validateHosting, validateMail, validateOwners, validateSite, } from './org.js';
|
|
2
2
|
export { PLATFORM_SERVICE_KEYS, defaultPlatformHostname, validatePlatform, } from './platform.js';
|
|
3
|
+
// archon#273: the pinned platform service versions the planner deploys, so a
|
|
4
|
+
// host can show declared against running. Readonly: it is the planner's own
|
|
5
|
+
// object, not a copy.
|
|
6
|
+
import { VERSIONS } from './bootstrap/versions.js';
|
|
7
|
+
export const PINNED_VERSIONS = VERSIONS;
|
|
3
8
|
export { REQUESTABLE_FIELDS, classifyChange, RequestedChangeError } from './requested-changes.js';
|
|
4
9
|
export { validateSlug, slugFromName, RESERVED_SLUGS } from './slug.js';
|
|
10
|
+
export { StaleLeaseError } from './store.js';
|
|
5
11
|
export { createPostgresAppliedVersionsStore, createPostgresDeclaredOrgStore, createPostgresPlatformStore, createPostgresRequestedChangeStore, DeclaredOrgError, PlatformError, } from './store/postgres.js';
|
|
6
12
|
export { createPostgresAuditLog } from './store/audit.js';
|
|
7
13
|
export { declaredOrgs, foundryAppliedState, foundryAppliedVersions, foundryAuditEvents, foundryDeclaredPlatform, foundryRequestedChanges, } from './store/schema.js';
|
|
8
|
-
export {
|
|
14
|
+
export { AdoptRefusedError } from './plan/index.js';
|
|
15
|
+
export { readLiveResources, readLivePlatformResources, computePlanDetail, toPlan, planPlatform, toPlatformPlan, JOBS_SCHEDULER_KEY, PLATFORM_AI_KEY, PLATFORM_AI_INTERNAL_URL_SCOPE_KEY, PLATFORM_AI_MANAGEMENT_KEY_SCOPE_KEY, PLATFORM_AI_ORGANISATION_SCOPE_KEY, PLATFORM_FILES_KEY, PLATFORM_FILES_INTERNAL_URL_SCOPE_KEY, PLATFORM_FILES_ZITADEL_PROJECT_SCOPE_KEY, PLATFORM_MAIL_INTERNAL_URL_SCOPE_KEY, PLATFORM_MAIL_ZITADEL_PROJECT_SCOPE_KEY, PLATFORM_NAMES_KEY, PLATFORM_NAMES_KEY_REF, PLATFORM_NAMES_CLOUDFLARE_TOKEN_REF, FoundryPlanApply, DeclaredOrgNotFoundError, StalePlanError, ConfirmationRequiredError, NotAuthorizedError, NotATestOrgError, DeclaredPlatformNotFoundError, PlatformStalePlanError, PlatformConfirmationRequiredError, planNeedsConfirmation, } from './plan/index.js';
|
|
9
16
|
export { DEFAULT_ORG_PLATFORM_READINESS, ORG_PLATFORM_KEY_REFS, agoraHostValuesFromPlatform, heldPlatformKeyRefs, orgPlatformReadinessFrom, } from './plan/index.js';
|
|
10
17
|
export { PlatformOrgBlockedError, PlatformOrgPorts } from './plan/index.js';
|
|
11
18
|
export { FoundryStepExecutor, NotBuiltError, healthUrls, loginHostnameFor } from './plan/index.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
-- @wtfalch/foundry: #259 -- per-org calls Matrix version pin, holding an
|
|
2
|
+
-- org's Synapse/MAS release during an ordered rollout. Null holds no pin:
|
|
3
|
+
-- the stack follows versions.json, same as every org today. Mirrors
|
|
4
|
+
-- src/store/schema.ts's `declaredOrgs` table additions.
|
|
5
|
+
|
|
6
|
+
alter table foundry_declared_orgs
|
|
7
|
+
add column if not exists calls_version_pin_synapse text,
|
|
8
|
+
add column if not exists calls_version_pin_mas text;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
-- @wtfalch/foundry: which steps of a destroy have finished, and which failed.
|
|
2
|
+
-- Sits beside `0002_applied_state.sql`'s `checkpoints` (an apply's finished
|
|
3
|
+
-- steps) and is read the same way, from the org's applied state. `DestroyRecord`
|
|
4
|
+
-- in `state.ts`. Null until a destroy has started; a destroy that finishes
|
|
5
|
+
-- deletes the org and this row with it.
|
|
6
|
+
|
|
7
|
+
alter table foundry_applied_state add column if not exists destroy jsonb;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
-- @wtfalch/foundry: Company Included's declared org id was minted by hand as
|
|
2
|
+
-- the literal 'org_company_included' rather than a uuid (bootstrap/declare.ts).
|
|
3
|
+
-- Archon resolves a declared org only by uuid (archon issue #40, `isUuid` in
|
|
4
|
+
-- `src/lib/companies/index.ts`'s `getCompany`), so every company page for our
|
|
5
|
+
-- own company 404s (foundry#435). This rewrites the live row, and every
|
|
6
|
+
-- column across this package's own tables that carries that same literal, to
|
|
7
|
+
-- the uuid `declare.ts`'s `COMPANY_INCLUDED_ORG_ID` now exports. The two
|
|
8
|
+
-- literals below must agree with that constant.
|
|
9
|
+
--
|
|
10
|
+
-- Idempotent: every statement is a no-op once the row has already moved,
|
|
11
|
+
-- because each WHERE clause only ever matches the old literal. Safe for a
|
|
12
|
+
-- still-running old release during the rolling deploy: nothing outside this
|
|
13
|
+
-- package's own one-time `bootstrapPlatform` idempotency check
|
|
14
|
+
-- (`bootstrap.ts`'s `orgStore.get(COMPANY_INCLUDED_ORG_ID)`) reads this id as
|
|
15
|
+
-- a hardcoded literal rather than from the declared platform row it is
|
|
16
|
+
-- already running against (`plan/platform-providers.ts`'s
|
|
17
|
+
-- `context.platform.ownerOrgId`), and Company Included's platform is already
|
|
18
|
+
-- bootstrapped -- that check has nothing left to do either way.
|
|
19
|
+
--
|
|
20
|
+
-- `foundry_applied_state` and `foundry_requested_changes` both carry a
|
|
21
|
+
-- foreign key to `foundry_declared_orgs (id)` with no `ON UPDATE CASCADE`,
|
|
22
|
+
-- so the primary key cannot move while they still point at the old value.
|
|
23
|
+
-- Each is dropped and re-added around the rewrite rather than made
|
|
24
|
+
-- permanently cascading, which would let a future bug silently rename an
|
|
25
|
+
-- org's rows out from under its history instead of failing loudly.
|
|
26
|
+
-- (`foundry_adoptions` is gone -- 0032 dropped it -- so it needs no such
|
|
27
|
+
-- treatment here.)
|
|
28
|
+
|
|
29
|
+
alter table foundry_applied_state drop constraint if exists foundry_applied_state_org_fkey;
|
|
30
|
+
alter table foundry_requested_changes drop constraint if exists foundry_requested_changes_org_fkey;
|
|
31
|
+
|
|
32
|
+
update foundry_declared_orgs
|
|
33
|
+
set id = 'e4d20889-a63d-42b6-b670-2bdd4472449b'
|
|
34
|
+
where id = 'org_company_included';
|
|
35
|
+
|
|
36
|
+
update foundry_applied_state
|
|
37
|
+
set org_id = 'e4d20889-a63d-42b6-b670-2bdd4472449b'
|
|
38
|
+
where org_id = 'org_company_included';
|
|
39
|
+
|
|
40
|
+
update foundry_requested_changes
|
|
41
|
+
set org_id = 'e4d20889-a63d-42b6-b670-2bdd4472449b'
|
|
42
|
+
where org_id = 'org_company_included';
|
|
43
|
+
|
|
44
|
+
update foundry_runs
|
|
45
|
+
set org_id = 'e4d20889-a63d-42b6-b670-2bdd4472449b'
|
|
46
|
+
where org_id = 'org_company_included';
|
|
47
|
+
|
|
48
|
+
update foundry_audit_events
|
|
49
|
+
set org_id = 'e4d20889-a63d-42b6-b670-2bdd4472449b'
|
|
50
|
+
where org_id = 'org_company_included';
|
|
51
|
+
|
|
52
|
+
update foundry_declared_platform
|
|
53
|
+
set owner_org_id = 'e4d20889-a63d-42b6-b670-2bdd4472449b'
|
|
54
|
+
where owner_org_id = 'org_company_included';
|
|
55
|
+
|
|
56
|
+
update foundry_applied_versions
|
|
57
|
+
set service = 'e4d20889-a63d-42b6-b670-2bdd4472449b'
|
|
58
|
+
where service = 'org_company_included';
|
|
59
|
+
|
|
60
|
+
do $$
|
|
61
|
+
begin
|
|
62
|
+
if not exists (select 1 from pg_constraint where conname = 'foundry_applied_state_org_fkey') then
|
|
63
|
+
alter table foundry_applied_state
|
|
64
|
+
add constraint foundry_applied_state_org_fkey foreign key (org_id)
|
|
65
|
+
references foundry_declared_orgs (id);
|
|
66
|
+
end if;
|
|
67
|
+
if not exists (select 1 from pg_constraint where conname = 'foundry_requested_changes_org_fkey') then
|
|
68
|
+
alter table foundry_requested_changes
|
|
69
|
+
add constraint foundry_requested_changes_org_fkey foreign key (org_id)
|
|
70
|
+
references foundry_declared_orgs (id);
|
|
71
|
+
end if;
|
|
72
|
+
end $$;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
-- @wtfalch/foundry: migration 0040 moved Company Included's declared org id
|
|
2
|
+
-- from the literal 'org_company_included' to the uuid
|
|
3
|
+
-- 'e4d20889-a63d-42b6-b670-2bdd4472449b' in every org_id (and org_id-shaped)
|
|
4
|
+
-- column, but never looked inside a jsonb value. A hosting step's key is
|
|
5
|
+
-- `<orgId>:<entryKey>` (platform-diff.ts's `appStepKey`), recorded as
|
|
6
|
+
-- `foundry_applied_state.service_ids`'s own keys
|
|
7
|
+
-- (`coolify:app:<orgId>:<entryKey>`, `cloudflare:access:<orgId>:<entryKey>`)
|
|
8
|
+
-- and as `foundry_runs.step_ids` array entries of the identical shape. After
|
|
9
|
+
-- 0040, the planner looks for `coolify:app:e4d20889-...:<entry>`, finds
|
|
10
|
+
-- nothing under the still-literal key, and can plan to create Company
|
|
11
|
+
-- Included's Coolify apps a second time (#452).
|
|
12
|
+
--
|
|
13
|
+
-- `foundry_runs.checkpoints` carries the same step strings
|
|
14
|
+
-- (`runner/runner.ts`'s `Checkpoint.step`) and is compared by exact string
|
|
15
|
+
-- against `step_ids` to decide what a resumed run has already done
|
|
16
|
+
-- (`done.has(stepId)`); rewriting `step_ids` alone would make an
|
|
17
|
+
-- already-completed step look undone and run it again. `failure.step` names
|
|
18
|
+
-- which step actually failed, same shape. `foundry_applied_state.checkpoints`
|
|
19
|
+
-- and `.destroy` (`state.ts`'s `Checkpoint`/`DestroyRecord`) carry the same
|
|
20
|
+
-- step strings too, as history rather than anything matched against, but are
|
|
21
|
+
-- rewritten the same way so nothing here still reads as Company Included's
|
|
22
|
+
-- old id.
|
|
23
|
+
--
|
|
24
|
+
-- Every WHERE clause below matches only a key, array element or `step` field
|
|
25
|
+
-- that still contains the old literal, so a second run is a no-op.
|
|
26
|
+
|
|
27
|
+
update foundry_applied_state
|
|
28
|
+
set service_ids = (
|
|
29
|
+
select coalesce(
|
|
30
|
+
jsonb_object_agg(
|
|
31
|
+
replace(key, 'org_company_included', 'e4d20889-a63d-42b6-b670-2bdd4472449b'),
|
|
32
|
+
value
|
|
33
|
+
),
|
|
34
|
+
'{}'::jsonb
|
|
35
|
+
)
|
|
36
|
+
from jsonb_each(service_ids)
|
|
37
|
+
)
|
|
38
|
+
where exists (
|
|
39
|
+
select 1 from jsonb_object_keys(service_ids) k
|
|
40
|
+
where k like '%org_company_included%'
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
update foundry_applied_state
|
|
44
|
+
set checkpoints = (
|
|
45
|
+
select coalesce(
|
|
46
|
+
jsonb_agg(
|
|
47
|
+
case
|
|
48
|
+
when (c ->> 'step') like '%org_company_included%'
|
|
49
|
+
then jsonb_set(c, '{step}',
|
|
50
|
+
to_jsonb(replace(c ->> 'step', 'org_company_included',
|
|
51
|
+
'e4d20889-a63d-42b6-b670-2bdd4472449b')))
|
|
52
|
+
else c
|
|
53
|
+
end
|
|
54
|
+
),
|
|
55
|
+
'[]'::jsonb
|
|
56
|
+
)
|
|
57
|
+
from jsonb_array_elements(checkpoints) as c
|
|
58
|
+
)
|
|
59
|
+
where exists (
|
|
60
|
+
select 1 from jsonb_array_elements(checkpoints) c
|
|
61
|
+
where (c ->> 'step') like '%org_company_included%'
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
update foundry_applied_state
|
|
65
|
+
set destroy = jsonb_set(
|
|
66
|
+
destroy,
|
|
67
|
+
'{steps}',
|
|
68
|
+
(select coalesce(
|
|
69
|
+
jsonb_agg(
|
|
70
|
+
case
|
|
71
|
+
when (s ->> 'step') like '%org_company_included%'
|
|
72
|
+
then jsonb_set(s, '{step}',
|
|
73
|
+
to_jsonb(replace(s ->> 'step', 'org_company_included',
|
|
74
|
+
'e4d20889-a63d-42b6-b670-2bdd4472449b')))
|
|
75
|
+
else s
|
|
76
|
+
end
|
|
77
|
+
),
|
|
78
|
+
'[]'::jsonb
|
|
79
|
+
)
|
|
80
|
+
from jsonb_array_elements(destroy -> 'steps') as s)
|
|
81
|
+
)
|
|
82
|
+
where destroy is not null
|
|
83
|
+
and exists (
|
|
84
|
+
select 1 from jsonb_array_elements(destroy -> 'steps') s
|
|
85
|
+
where (s ->> 'step') like '%org_company_included%'
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
update foundry_runs
|
|
89
|
+
set step_ids = (
|
|
90
|
+
select coalesce(
|
|
91
|
+
jsonb_agg(
|
|
92
|
+
replace(elem, 'org_company_included', 'e4d20889-a63d-42b6-b670-2bdd4472449b')
|
|
93
|
+
),
|
|
94
|
+
'[]'::jsonb
|
|
95
|
+
)
|
|
96
|
+
from jsonb_array_elements_text(step_ids) as elem
|
|
97
|
+
)
|
|
98
|
+
where exists (
|
|
99
|
+
select 1 from jsonb_array_elements_text(step_ids) e
|
|
100
|
+
where e like '%org_company_included%'
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
update foundry_runs
|
|
104
|
+
set checkpoints = (
|
|
105
|
+
select coalesce(
|
|
106
|
+
jsonb_agg(
|
|
107
|
+
case
|
|
108
|
+
when (c ->> 'step') like '%org_company_included%'
|
|
109
|
+
then jsonb_set(c, '{step}',
|
|
110
|
+
to_jsonb(replace(c ->> 'step', 'org_company_included',
|
|
111
|
+
'e4d20889-a63d-42b6-b670-2bdd4472449b')))
|
|
112
|
+
else c
|
|
113
|
+
end
|
|
114
|
+
),
|
|
115
|
+
'[]'::jsonb
|
|
116
|
+
)
|
|
117
|
+
from jsonb_array_elements(checkpoints) as c
|
|
118
|
+
)
|
|
119
|
+
where exists (
|
|
120
|
+
select 1 from jsonb_array_elements(checkpoints) c
|
|
121
|
+
where (c ->> 'step') like '%org_company_included%'
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
update foundry_runs
|
|
125
|
+
set failure = jsonb_set(
|
|
126
|
+
failure,
|
|
127
|
+
'{step}',
|
|
128
|
+
to_jsonb(replace(failure ->> 'step', 'org_company_included',
|
|
129
|
+
'e4d20889-a63d-42b6-b670-2bdd4472449b'))
|
|
130
|
+
)
|
|
131
|
+
where failure is not null
|
|
132
|
+
and (failure ->> 'step') like '%org_company_included%';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
-- @wtfalch/foundry: the lock behind `FoundryPlanApply`'s apply, destroy, adopt
|
|
2
|
+
-- and release (#441). One row per scope: `company:<orgId>` or `platform`.
|
|
3
|
+
-- `token` is the fencing token; a row is never deleted, so it only rises, and
|
|
4
|
+
-- a stale holder's `putAppliedState` / `putServiceIds` is refused by
|
|
5
|
+
-- comparing against it. A released lease has `expires_at` set to now.
|
|
6
|
+
|
|
7
|
+
create table if not exists foundry_mutation_leases (
|
|
8
|
+
scope text primary key,
|
|
9
|
+
token bigint not null,
|
|
10
|
+
holder text not null,
|
|
11
|
+
expires_at timestamptz not null
|
|
12
|
+
);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
-- @wtfalch/foundry: #317 (F7) -- the names service, which mints an entry's
|
|
2
|
+
-- own names key and holds the Cloudflare token `<username>.<org domain>`
|
|
3
|
+
-- claims use, added to the declared platform the same way every other
|
|
4
|
+
-- shared service here was (#165). Mirrors `src/store/schema.ts`'s
|
|
5
|
+
-- `foundryDeclaredPlatform` table additions.
|
|
6
|
+
--
|
|
7
|
+
-- names_hostname has no static default the way every other service's
|
|
8
|
+
-- column did at table creation -- #317 is the first service added after
|
|
9
|
+
-- the table already carries its one live row, so a column that must not be
|
|
10
|
+
-- empty (src/platform.ts's `validatePlatform`) cannot default to ''.
|
|
11
|
+
-- Backfilled from this row's own apex before the column is required, so an
|
|
12
|
+
-- existing platform keeps validating unchanged, at the default hostname an
|
|
13
|
+
-- operator can still override afterwards.
|
|
14
|
+
|
|
15
|
+
alter table foundry_declared_platform
|
|
16
|
+
add column if not exists names_enabled boolean not null default false,
|
|
17
|
+
add column if not exists names_hostname text,
|
|
18
|
+
add column if not exists names_git_ref text not null default '';
|
|
19
|
+
|
|
20
|
+
update foundry_declared_platform
|
|
21
|
+
set names_hostname = 'names.' || apex
|
|
22
|
+
where names_hostname is null;
|
|
23
|
+
|
|
24
|
+
alter table foundry_declared_platform
|
|
25
|
+
alter column names_hostname set not null;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
-- @wtfalch/foundry: #491 -- the Lettermint project id of the platform's own
|
|
2
|
+
-- org (an org whose mail domain equals the platform apex, e.g. Company
|
|
3
|
+
-- Included on companyincluded.com), so that org shares the platform's
|
|
4
|
+
-- existing Lettermint project instead of getting a second `<slug>-app` one
|
|
5
|
+
-- of its own. Declared, never discovered: null until an operator sets it
|
|
6
|
+
-- (src/platform.ts's `validatePlatform` refuses a present-but-blank value,
|
|
7
|
+
-- not this column). Mirrors src/store/schema.ts's `foundryDeclaredPlatform`
|
|
8
|
+
-- table addition.
|
|
9
|
+
|
|
10
|
+
alter table foundry_declared_platform
|
|
11
|
+
add column if not exists mail_lettermint_project_id text;
|