@wtfalch/foundry 0.76.1 → 0.77.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/app-canary-worker.d.ts +35 -0
- package/dist/app-canary-worker.js +56 -0
- package/dist/audit/connector.d.ts +6 -2
- package/dist/audit/connector.js +3 -2
- package/dist/audit/evidence.d.ts +4 -0
- package/dist/audit/evidence.js +14 -2
- package/dist/audit/host-store.d.ts +58 -0
- package/dist/audit/host-store.js +147 -0
- package/dist/audit/host.d.ts +53 -0
- package/dist/audit/host.js +294 -0
- package/dist/audit/index.d.ts +2 -0
- package/dist/audit/index.js +2 -0
- package/dist/audit/registry.d.ts +14 -0
- package/dist/audit/sealer-role.d.ts +12 -0
- package/dist/audit/sealer-role.js +26 -2
- package/dist/audit/tick.d.ts +10 -2
- package/dist/audit/tick.js +15 -2
- package/dist/catalogue/copy-en.js +5 -0
- package/dist/catalogue/integrations.js +5 -0
- package/dist/entry-rules.d.ts +35 -0
- package/dist/entry-rules.js +35 -0
- package/dist/hosted-label.js +2 -1
- package/dist/index.d.ts +11 -3
- package/dist/index.js +6 -1
- package/dist/migrations/0072_audit_host.sql +46 -0
- package/dist/migrations/0074_platform_app_canary.sql +11 -0
- package/dist/modules/ai.js +15 -1
- package/dist/modules/assistants.js +3 -0
- package/dist/modules/email.d.ts +6 -1
- package/dist/modules/email.js +88 -5
- package/dist/modules/files.js +79 -2
- package/dist/modules/index.js +7 -0
- package/dist/modules/jobs.d.ts +5 -1
- package/dist/modules/jobs.js +74 -4
- package/dist/modules/reporting.d.ts +9 -0
- package/dist/modules/reporting.js +29 -0
- package/dist/modules/sign-in.js +20 -2
- package/dist/modules/types.d.ts +2 -0
- package/dist/modules/workspaces.js +3 -0
- package/dist/org.d.ts +114 -13
- package/dist/org.js +233 -20
- package/dist/plan/ai-boule-key-apply.d.ts +65 -0
- package/dist/plan/ai-boule-key-apply.js +169 -0
- package/dist/plan/ai-boule-key.d.ts +89 -0
- package/dist/plan/ai-boule-key.js +195 -0
- package/dist/plan/analytics.d.ts +8 -1
- package/dist/plan/analytics.js +8 -1
- package/dist/plan/app-canary-apply.d.ts +49 -0
- package/dist/plan/app-canary-apply.js +232 -0
- package/dist/plan/app-canary.d.ts +44 -0
- package/dist/plan/app-canary.js +110 -0
- package/dist/plan/blue-green-apply.d.ts +6 -2
- package/dist/plan/blue-green-apply.js +14 -3
- package/dist/plan/broker-entries.js +20 -5
- package/dist/plan/company-backups.js +2 -0
- package/dist/plan/diff.d.ts +22 -0
- package/dist/plan/diff.js +231 -18
- package/dist/plan/entry-files-key.d.ts +39 -3
- package/dist/plan/entry-files-key.js +103 -6
- package/dist/plan/entry-jobs-boule-key-apply.d.ts +65 -0
- package/dist/plan/entry-jobs-boule-key-apply.js +192 -0
- package/dist/plan/entry-jobs-boule-key.d.ts +59 -0
- package/dist/plan/entry-jobs-boule-key.js +141 -0
- package/dist/plan/entry-jobs.d.ts +2 -0
- package/dist/plan/entry-reporting-key.d.ts +36 -0
- package/dist/plan/entry-reporting-key.js +136 -0
- package/dist/plan/executor.d.ts +108 -1
- package/dist/plan/executor.js +669 -23
- package/dist/plan/files-boule-key-apply.d.ts +73 -0
- package/dist/plan/files-boule-key-apply.js +223 -0
- package/dist/plan/files-boule-key.d.ts +92 -0
- package/dist/plan/files-boule-key.js +175 -0
- package/dist/plan/hostnames.js +4 -0
- package/dist/plan/index.d.ts +4 -0
- package/dist/plan/index.js +2 -0
- package/dist/plan/jobs-scheduler-http.d.ts +13 -0
- package/dist/plan/jobs-scheduler-http.js +39 -0
- package/dist/plan/live.d.ts +1 -1
- package/dist/plan/locked-hostnames.d.ts +1 -1
- package/dist/plan/locked-hostnames.js +4 -1
- package/dist/plan/login-host.js +2 -2
- package/dist/plan/org-names.js +3 -0
- package/dist/plan/own-diff.js +3 -2
- package/dist/plan/own-jobs-route.d.ts +5 -2
- package/dist/plan/own-jobs-route.js +6 -2
- package/dist/plan/own-limits.d.ts +6 -0
- package/dist/plan/own-limits.js +1 -0
- package/dist/plan/plan-apply.d.ts +24 -0
- package/dist/plan/plan-apply.js +39 -4
- package/dist/plan/platform-diff.d.ts +2 -0
- package/dist/plan/platform-diff.js +15 -1
- package/dist/plan/platform-providers.d.ts +9 -0
- package/dist/plan/platform-providers.js +24 -0
- package/dist/plan/providers-readers.js +25 -1
- package/dist/plan/registered-app-env.d.ts +31 -0
- package/dist/plan/registered-app-env.js +63 -0
- package/dist/plan/registered-app.d.ts +121 -0
- package/dist/plan/registered-app.js +262 -0
- package/dist/plan/release-host.js +2 -1
- package/dist/plan/reporting-boule.d.ts +18 -0
- package/dist/plan/reporting-boule.js +44 -0
- package/dist/plan/reporting.d.ts +90 -0
- package/dist/plan/reporting.js +135 -0
- package/dist/plan/stage-env.js +1 -0
- package/dist/plan/stage-isolation.d.ts +3 -1
- package/dist/plan/stage-isolation.js +20 -2
- package/dist/plan/stage-services.d.ts +3 -1
- package/dist/plan/stage-services.js +8 -1
- package/dist/plan/stage-sink.d.ts +2 -2
- package/dist/plan/stage-sink.js +2 -2
- package/dist/plan/workspace-client.d.ts +1 -1
- package/dist/plan/workspace-client.js +2 -1
- package/dist/plan/workspace-sign-in.d.ts +1 -1
- package/dist/plan/workspace-sign-in.js +39 -7
- package/dist/platform.d.ts +25 -0
- package/dist/platform.js +20 -0
- package/dist/service-labels.d.ts +8 -0
- package/dist/service-labels.js +8 -0
- package/dist/slug.d.ts +1 -1
- package/dist/slug.js +5 -2
- package/dist/store/postgres.js +13 -0
- package/dist/store/schema.d.ts +485 -2
- package/dist/store/schema.js +38 -1
- package/dist/worker-app-config.js +2 -2
- package/package.json +1 -1
package/dist/plan/diff.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { VERSIONS } from '../bootstrap/versions.js';
|
|
2
2
|
import { NATIVE_BUILD_KEY_ENV } from '../native-build.js';
|
|
3
|
-
import { APP_ENTRY_KEY, ORG_POSTGRES_SERVER_SCOPE_KEY, ORG_SERVICE_SCHEMAS, appsOn, companyGithubFor, heldEnvRef, kosmosOn, mainEntry, ownerMailboxRecorded, ownerMailboxes, ownerNameProblem, ownsServices, } from '../org.js';
|
|
3
|
+
import { APP_ENTRY_KEY, MAIL_APP_KEY_ENV, ORG_POSTGRES_SERVER_SCOPE_KEY, ORG_SERVICE_SCHEMAS, appsOn, companyGithubFor, heldEnvRef, kosmosOn, mainEntry, ownerMailboxRecorded, ownerMailboxes, ownerNameProblem, ownsServices, sendsMail, signsInBoth, signsInCustomers, } from '../org.js';
|
|
4
4
|
import { entryHasDatabase } from '../worker-app-config.js';
|
|
5
5
|
import { AGORA_ACTIVATED_ENV_SCOPE_KEY, AGORA_APP_PORTS_EXPOSES, CALLS_INTROSPECTION_SCOPE_KEY, CALLS_INTROSPECTION_UNDELIVERED_SCOPE_KEY, CALLS_SERVICE_DEPLOYED_SCOPE_KEY, CHAT_CALLS_APPLIED_SCOPE_KEY, CHAT_CALLS_TOKEN_SCOPE_KEY, CHAT_CALLS_TOKEN_UNDELIVERED_SCOPE_KEY, CHAT_INTROSPECTION_SCOPE_KEY, NO_AGORA_HOST_VALUES, ZITADEL_ORG_SCOPE_KEY, agoraEnvDrift, agoraEnvRevision, agoraHostMissing, agoraHostnameFor, callsServiceHasName, callsServiceHasTokenEnv, callsTokenEnvMovedMessage, callsTokenHostFrom, chatCallsApplied, chatHasUpstreams, desiredAgoraEnv, entryClientIdScopeKey, withRecordedMessagingProject, } from './agora.js';
|
|
6
|
+
import { AI_OFFERS_SCOPE_KEY, aiKeyPermissions, aiOffers, planAiBouleKey } from './ai-boule-key.js';
|
|
6
7
|
import { planAnalyticsBoule } from './analytics-boule.js';
|
|
7
8
|
import { analyticsEnvDrift, analyticsWanted, desiredAnalyticsEnv } from './analytics.js';
|
|
8
9
|
import { planAppsDbRoute } from './apps-db-route.js';
|
|
@@ -25,9 +26,12 @@ import { authEventsAppliedScopeKey, authEventsBlocked, authEventsMarker, } from
|
|
|
25
26
|
import { CUSTOMER_GRANTS_ENV, CUSTOMER_GRANTS_WRITTEN_SCOPE_KEY, customerGrantsEntries, customerGrantsShapeMatches, customerGrantsToDrop, wantedCustomerGrants, } from './entry-customer-grants.js';
|
|
26
27
|
import { planEdgeReservedLabels, planEdgeSlug, planEntryEdgeKeys } from './entry-edge-key.js';
|
|
27
28
|
import { planFilesKeys } from './entry-files-key.js';
|
|
29
|
+
import { planEntryJobsBouleKeys } from './entry-jobs-boule-key.js';
|
|
28
30
|
import { planEntryJobs } from './entry-jobs.js';
|
|
29
31
|
import { planEntryRepoKeys } from './entry-repo-key.js';
|
|
32
|
+
import { planEntryReportingKeys } from './entry-reporting-key.js';
|
|
30
33
|
import { BROKER_WORKSPACES_DATABASE_ENV, workspacesDatabaseRef } from './entry-workspaces.js';
|
|
34
|
+
import { planFilesBouleKey } from './files-boule-key.js';
|
|
31
35
|
import { gateIdRecordKey, hasWildcardGate, planGates } from './gates.js';
|
|
32
36
|
import { githubSourceResourceKey, planGithubSource } from './github-source.js';
|
|
33
37
|
import { appHostname, tunnelDnsHostnames } from './hostnames.js';
|
|
@@ -53,7 +57,10 @@ import { DEFAULT_ORG_PLATFORM_READINESS, } from './platform-org.js';
|
|
|
53
57
|
import { PLATFORM_AUTH_BROKER_MAIL_KEY_REF } from './platform-providers.js';
|
|
54
58
|
import { companyAppPostLogoutUris, postLogoutUpdateOwed, zitadelAppPostLogoutAppliedScopeKey, } from './post-logout-uris.js';
|
|
55
59
|
import { CLOUDFLARE_TUNNEL_APPS_HOSTNAMES_SCOPE_KEY, CLOUDFLARE_TUNNEL_ENTRY_ROUTES_SCOPE_KEY, CLOUDFLARE_TUNNEL_OIKOS_HOSTNAMES_SCOPE_KEY, appLabelsAppliedScopeKey, entryAppsHostnames, entryLabelHash, entryLabelSpec, entryOikosHostnames, entryRoutes, entryRoutesMarker, } from './proxy-labels.js';
|
|
60
|
+
import { planRegisteredApps } from './registered-app.js';
|
|
56
61
|
import { planRemoteDatabase } from './remote-database.js';
|
|
62
|
+
import { planReportingBoule } from './reporting-boule.js';
|
|
63
|
+
import { REPORTING_MAIL_TOKEN_ENV, desiredReportingEnv, reportingEnvDrift, reportingWanted, } from './reporting.js';
|
|
57
64
|
import { ZITADEL_ORG_DOMAIN_BLOCKED_SCOPE_KEY, ZITADEL_ORG_DOMAIN_SCOPE_KEY, ZITADEL_ORG_REGISTRATION_CLOSED_SCOPE_KEY, signInDomainBlockedText, signInOrgDomain, } from './sign-in-org.js';
|
|
58
65
|
import { SIGN_IN_BASE_PATH, SIGN_IN_IMAGE_TAG_SUFFIX, SIGN_IN_IN_APP_SCOPE_KEY, SIGN_IN_LOGIN_APP_SCOPE_KEY, SIGN_IN_LOGIN_INTERNAL_NAME, SIGN_IN_RATE_LIMIT_SCOPE_KEY, SIGN_IN_TRUSTED_SCOPE_KEY, inAppSignIn, inAppStatePairs, inAppTargets, signInLoginNames, } from './sign-in.js';
|
|
59
66
|
import { withStepSystem } from './step-system.js';
|
|
@@ -446,13 +453,14 @@ export function computePlanDetail(declaredOrg, live, recordedIds = {}, agoraHost
|
|
|
446
453
|
const filesOrganisation = planFilesOrganisation(org, byKey, claimed, platformReadiness.filesReady, platformReadiness.missingKeys.files, platformReadiness.noKeyReader, ownsServices(org) && !recordedIds[`coolify:own-files:${org.id}`]);
|
|
447
454
|
if (filesOrganisation.step)
|
|
448
455
|
plannedSteps.push(filesOrganisation.step);
|
|
449
|
-
const
|
|
456
|
+
const aiBouleReads = options.aiBouleReads ?? false;
|
|
457
|
+
const aiOrganisation = planAiOrganisation(org, byKey, claimed, platformReadiness.aiReady, platformReadiness.missingKeys.ai, platformReadiness.noKeyReader, recordedIds, aiBouleReads);
|
|
450
458
|
if (aiOrganisation.step)
|
|
451
459
|
plannedSteps.push(aiOrganisation.step);
|
|
452
460
|
const aiBudget = planAiBudget(org, byKey, claimed, platformReadiness.aiReady, platformReadiness.missingKeys.ai, platformReadiness.noKeyReader);
|
|
453
461
|
if (aiBudget.step)
|
|
454
462
|
plannedSteps.push(aiBudget.step);
|
|
455
|
-
const aiKey = planAiKey(org, recordedIds, platformReadiness.aiReady);
|
|
463
|
+
const aiKey = planAiKey(org, recordedIds, platformReadiness.aiReady, aiBouleReads);
|
|
456
464
|
if (aiKey.step)
|
|
457
465
|
plannedSteps.push(aiKey.step);
|
|
458
466
|
// Before `planSite`'s own step (#139): `cmsSite`'s executor reads this
|
|
@@ -609,12 +617,14 @@ export function computePlanDetail(declaredOrg, live, recordedIds = {}, agoraHost
|
|
|
609
617
|
const marker = workspaceSignInMarker(declaredOrg, entry, recordedIds, authIssuer);
|
|
610
618
|
if (marker)
|
|
611
619
|
return marker;
|
|
612
|
-
const customers = entry.uses?.auth
|
|
620
|
+
const customers = signsInCustomers(entry.uses?.auth);
|
|
613
621
|
const orgKnown = customers
|
|
614
622
|
? Boolean(recordedIds[testCustomersOrgKey(org.id)]) ||
|
|
615
623
|
stepPlanned('zitadel', 'customers-test')
|
|
616
624
|
: Boolean(recordedIds.zitadel_org);
|
|
617
|
-
|
|
625
|
+
// ADR 0003: the broker makes the clients of a workspace both sign in to, once the stage exists.
|
|
626
|
+
const clientKnown = signsInBoth(entry.uses?.auth) ||
|
|
627
|
+
Boolean(recordedIds[entryClientIdScopeKey(workspaceClientKey(entry.key))]) ||
|
|
618
628
|
stepPlanned('zitadel', 'app', workspaceClientKey(entry.key));
|
|
619
629
|
const mcpKnown = !entry.uses?.auth?.mcp ||
|
|
620
630
|
Boolean(recordedIds[entryMcpProjectScopeKey(workspaceClientKey(entry.key))]) ||
|
|
@@ -737,6 +747,12 @@ export function computePlanDetail(declaredOrg, live, recordedIds = {}, agoraHost
|
|
|
737
747
|
}, customerGrantsShape);
|
|
738
748
|
if (boule.step)
|
|
739
749
|
plannedSteps.push(boule.step);
|
|
750
|
+
// foundry#1121: Boule's files key, after Boule and after the org's own files key it is minted below.
|
|
751
|
+
const filesBouleKey = planFilesBouleKey(org, recordedIds, plannedSteps, platformReadiness, (options.now ?? Date.now)());
|
|
752
|
+
plannedSteps.push(...filesBouleKey.steps);
|
|
753
|
+
// foundry#1122: Boule's ai key, after Boule and after the org's own ai key it is minted below.
|
|
754
|
+
const aiBouleKey = planAiBouleKey(org, recordedIds, plannedSteps, platformReadiness, (options.now ?? Date.now)(), aiBouleReads);
|
|
755
|
+
plannedSteps.push(...aiBouleKey.steps);
|
|
740
756
|
// foundry#949: the company's kosmos, only once it turns it on. Its fixed ZITADEL client first, the same
|
|
741
757
|
// reason Boule's and Agora's do: `coolifyKosmos` reads this step's recorded client id.
|
|
742
758
|
const kosmosClient = planKosmosClient(org, byKey, claimed);
|
|
@@ -754,6 +770,14 @@ export function computePlanDetail(declaredOrg, live, recordedIds = {}, agoraHost
|
|
|
754
770
|
plannedSteps.push(...planAnalyticsBoule(org, recordedIds, plannedSteps));
|
|
755
771
|
const entryAnalyticsKeys = planEntryAnalyticsKeys(org, recordedIds, plannedSteps, options.analyticsPort ?? false);
|
|
756
772
|
plannedSteps.push(...entryAnalyticsKeys.steps);
|
|
773
|
+
// The company's reporting backend, the same way: only while an entry's `uses.reporting` or Boule's apps
|
|
774
|
+
// use it, never parked once made, and before the site keys and Boule's env, which need its operator key.
|
|
775
|
+
const reporting = planReportingBackend(org, byKey, claimed, recordedIds, options.otherSlugs ?? [], platformReadiness);
|
|
776
|
+
if (reporting.step)
|
|
777
|
+
plannedSteps.push(reporting.step);
|
|
778
|
+
plannedSteps.push(...planReportingBoule(org, recordedIds, plannedSteps));
|
|
779
|
+
const entryReportingKeys = planEntryReportingKeys(org, recordedIds, plannedSteps, options.reportingPort ?? false);
|
|
780
|
+
plannedSteps.push(...entryReportingKeys.steps);
|
|
757
781
|
// Boule's, Agora's and kosmos's blue/green: after their own steps, which make the app it copies.
|
|
758
782
|
plannedSteps.push(...blueGreen.steps.filter((x) => x.phase === 'last').map(blueGreenDraft()));
|
|
759
783
|
// Boule owners, after the Boule step: needs the app deployed and the seeding secret in its env.
|
|
@@ -781,6 +805,9 @@ export function computePlanDetail(declaredOrg, live, recordedIds = {}, agoraHost
|
|
|
781
805
|
// A `uses.jobs` entry registers with the scheduler after its app, whose env the step writes.
|
|
782
806
|
const entryJobs = planEntryJobs(org, recordedIds, plannedSteps, platformReadiness, schedulerUsable);
|
|
783
807
|
plannedSteps.push(...entryJobs.steps);
|
|
808
|
+
// foundry#1119: Boule's key for each entry's project, after the project and after Boule.
|
|
809
|
+
const entryJobsBouleKeys = planEntryJobsBouleKeys(org, recordedIds, plannedSteps, platformReadiness, options.jobsProjectKeys ?? false);
|
|
810
|
+
plannedSteps.push(...entryJobsBouleKeys.steps);
|
|
784
811
|
// F12: a deploy key for an entry's own repo, after its app (whose env the step writes).
|
|
785
812
|
const entryRepoKeys = planEntryRepoKeys(org, recordedIds, plannedSteps, platformReadiness);
|
|
786
813
|
plannedSteps.push(...entryRepoKeys.steps);
|
|
@@ -792,6 +819,12 @@ export function computePlanDetail(declaredOrg, live, recordedIds = {}, agoraHost
|
|
|
792
819
|
// The company's registrar record in the broker, after Boule's own broker entry.
|
|
793
820
|
if (registrar.step)
|
|
794
821
|
plannedSteps.push(registrar.step);
|
|
822
|
+
// ADR 0003: after the registrar, the apps and the workspaces: the entries both sign in to are registered.
|
|
823
|
+
const registeredApps = planRegisteredApps(declaredOrg, recordedIds, plannedSteps, {
|
|
824
|
+
registrarApi: options.registrarApi ?? false,
|
|
825
|
+
authIssuer,
|
|
826
|
+
});
|
|
827
|
+
plannedSteps.push(...registeredApps.steps);
|
|
795
828
|
// #440: Agora's desktop sign-in backend, after Agora and Boule: its readiness probe goes through Agora's proxy, and
|
|
796
829
|
// Agora writes the upstream only once the service ran healthy (so a first apply needs a second to turn it on).
|
|
797
830
|
const desktopAuth = planDesktopAuth(org, recordedIds, {
|
|
@@ -1039,6 +1072,12 @@ export function computePlanDetail(declaredOrg, live, recordedIds = {}, agoraHost
|
|
|
1039
1072
|
if (analytics.blocked)
|
|
1040
1073
|
unmanaged.push({ description: analytics.blocked, key: 'coolify:analytics' });
|
|
1041
1074
|
unmanaged.push(...entryAnalyticsKeys.blocked);
|
|
1075
|
+
if (reporting.blocked)
|
|
1076
|
+
unmanaged.push({ description: reporting.blocked, key: 'coolify:reporting' });
|
|
1077
|
+
if (reporting.mailBlocked) {
|
|
1078
|
+
unmanaged.push({ description: reporting.mailBlocked, key: 'coolify:reporting:mail' });
|
|
1079
|
+
}
|
|
1080
|
+
unmanaged.push(...entryReportingKeys.blocked);
|
|
1042
1081
|
// Same for an ai key minted but never delivered: planning a new mint would
|
|
1043
1082
|
// orphan a second live key.
|
|
1044
1083
|
if (aiKey.blocked)
|
|
@@ -1046,7 +1085,9 @@ export function computePlanDetail(declaredOrg, live, recordedIds = {}, agoraHost
|
|
|
1046
1085
|
unmanaged.push(...blueGreen.unmanaged);
|
|
1047
1086
|
unmanaged.push(...workspaces.unmanaged);
|
|
1048
1087
|
unmanaged.push(...filesKeys.blocked);
|
|
1088
|
+
unmanaged.push(...filesBouleKey.blocked);
|
|
1049
1089
|
unmanaged.push(...entryAiKeys.blocked);
|
|
1090
|
+
unmanaged.push(...aiBouleKey.blocked);
|
|
1050
1091
|
unmanaged.push(...edgeWorkers.blocked);
|
|
1051
1092
|
unmanaged.push(...workerApps.blocked);
|
|
1052
1093
|
if (appsDb.blocked) {
|
|
@@ -1067,6 +1108,7 @@ export function computePlanDetail(declaredOrg, live, recordedIds = {}, agoraHost
|
|
|
1067
1108
|
unmanaged.push(...appsDbRoute.blocked);
|
|
1068
1109
|
unmanaged.push(...appsZones.blocked);
|
|
1069
1110
|
unmanaged.push(...entryJobs.blocked);
|
|
1111
|
+
unmanaged.push(...entryJobsBouleKeys.blocked);
|
|
1070
1112
|
unmanaged.push(...authEventsBlocked(org, recordedIds));
|
|
1071
1113
|
unmanaged.push(...entryAudit.blocked);
|
|
1072
1114
|
unmanaged.push(...entryRepoKeys.blocked);
|
|
@@ -1173,6 +1215,7 @@ export function computePlanDetail(declaredOrg, live, recordedIds = {}, agoraHost
|
|
|
1173
1215
|
unmanaged.push(...app.blocked);
|
|
1174
1216
|
unmanaged.push(...brokerEntries.blocked);
|
|
1175
1217
|
unmanaged.push(...workspaceBrokerEntries.blocked);
|
|
1218
|
+
unmanaged.push(...registeredApps.blocked);
|
|
1176
1219
|
unmanaged.push(...bouleBroker.blocked);
|
|
1177
1220
|
// F6: last, so every database step above runs before the backup of its database.
|
|
1178
1221
|
const backups = planCompanyBackups(org, recordedIds, options.backupStorageUuid);
|
|
@@ -2475,7 +2518,6 @@ function planCallsChat(org, recordedIds, upstreamPlanned, upstreamBlocked) {
|
|
|
2475
2518
|
* organisation bring its own provider credentials (BYOK) -- a separate,
|
|
2476
2519
|
* explicit opt-in this default deliberately leaves out.
|
|
2477
2520
|
*/
|
|
2478
|
-
const AI_DEFAULT_OFFERS = ['ai.offerings:invoke', 'ai.runs:create'];
|
|
2479
2521
|
/**
|
|
2480
2522
|
* #484: the spend cap foundry applies when an org declares no AI limit
|
|
2481
2523
|
* (`org.ai.limitMicros === null`) -- Archon creates every company that way,
|
|
@@ -2619,12 +2661,37 @@ ownFilesPending = false) {
|
|
|
2619
2661
|
* #177: blocked, not planned, while `aiReady` is false -- the platform's own
|
|
2620
2662
|
* AI host (#172) is off or not yet applied.
|
|
2621
2663
|
*/
|
|
2622
|
-
function planAiOrganisation(org, byKey, claimed, aiReady, missingKeyRef, noKeyReader = false) {
|
|
2664
|
+
function planAiOrganisation(org, byKey, claimed, aiReady, missingKeyRef, noKeyReader = false, recordedIds = {}, bouleReads = false) {
|
|
2623
2665
|
const key = resourceKey('ai', 'organisation', org.id);
|
|
2624
2666
|
const live = byKey.get(key);
|
|
2667
|
+
const offers = aiOffers(bouleReads);
|
|
2625
2668
|
if (live) {
|
|
2626
2669
|
claimed.add(key);
|
|
2627
|
-
|
|
2670
|
+
// foundry#1122: once ai offers Boule's reads, an organisation offered fewer is offered them (both calls
|
|
2671
|
+
// are repeatable). Without the host's word nothing changes.
|
|
2672
|
+
if (!bouleReads || !aiReady || recordedIds[AI_OFFERS_SCOPE_KEY] === JSON.stringify(offers)) {
|
|
2673
|
+
return { step: null, blocked: null };
|
|
2674
|
+
}
|
|
2675
|
+
return {
|
|
2676
|
+
step: {
|
|
2677
|
+
action: {
|
|
2678
|
+
system: 'ai',
|
|
2679
|
+
kind: 'organisation',
|
|
2680
|
+
key: org.id,
|
|
2681
|
+
desired: {
|
|
2682
|
+
provision: true,
|
|
2683
|
+
limitMicros: effectiveAiLimitMicros(org.ai.limitMicros),
|
|
2684
|
+
offers,
|
|
2685
|
+
},
|
|
2686
|
+
},
|
|
2687
|
+
step: {
|
|
2688
|
+
description: `offer ${org.slug}'s AI organisation the read permissions Boule's AI page needs`,
|
|
2689
|
+
destroys: false,
|
|
2690
|
+
migrates: false,
|
|
2691
|
+
},
|
|
2692
|
+
},
|
|
2693
|
+
blocked: null,
|
|
2694
|
+
};
|
|
2628
2695
|
}
|
|
2629
2696
|
if (!aiReady) {
|
|
2630
2697
|
return {
|
|
@@ -2641,7 +2708,7 @@ function planAiOrganisation(org, byKey, claimed, aiReady, missingKeyRef, noKeyRe
|
|
|
2641
2708
|
desired: {
|
|
2642
2709
|
provision: true,
|
|
2643
2710
|
limitMicros: effectiveAiLimitMicros(org.ai.limitMicros),
|
|
2644
|
-
offers
|
|
2711
|
+
offers,
|
|
2645
2712
|
},
|
|
2646
2713
|
},
|
|
2647
2714
|
step: {
|
|
@@ -2725,17 +2792,24 @@ function planAiBudget(org, byKey, claimed, aiReady, missingKeyRef, noKeyReader =
|
|
|
2725
2792
|
* organisation to mint a key inside yet, and {@link planAiOrganisation}
|
|
2726
2793
|
* already reports the platform's AI host as missing once.
|
|
2727
2794
|
*/
|
|
2728
|
-
function planAiKey(org, recordedIds, aiReady) {
|
|
2795
|
+
function planAiKey(org, recordedIds, aiReady, bouleReads = false) {
|
|
2729
2796
|
const key = resourceKey('ai', 'key', org.id);
|
|
2730
|
-
|
|
2797
|
+
const permissions = aiOffers(bouleReads);
|
|
2798
|
+
// foundry#1122: a delivered key that holds fewer than the organisation now offers is minted again with them,
|
|
2799
|
+
// so Boule's key can be minted below it. Without the host's word a delivered key plans nothing.
|
|
2800
|
+
const widen = Boolean(recordedIds[key]) &&
|
|
2801
|
+
bouleReads &&
|
|
2802
|
+
aiKeyPermissions(recordedIds) !== JSON.stringify(permissions);
|
|
2803
|
+
if (recordedIds[key] && !widen)
|
|
2731
2804
|
return { step: null, blocked: null }; // delivered -- a re-run plans nothing.
|
|
2732
2805
|
if (!aiReady)
|
|
2733
2806
|
return { step: null, blocked: null };
|
|
2734
2807
|
// Minted but never delivered (`executor.ts`'s `aiKey`): nobody holds the
|
|
2735
2808
|
// secret and ai has no per-key revoke yet, so a new mint would only add
|
|
2736
|
-
// another orphan. Reported, not planned.
|
|
2809
|
+
// another orphan. Reported, not planned. (The delivered key's own id stays
|
|
2810
|
+
// recorded as undelivered; it blocks nothing.)
|
|
2737
2811
|
const undelivered = recordedIds[AI_KEY_UNDELIVERED_SCOPE_KEY];
|
|
2738
|
-
if (undelivered) {
|
|
2812
|
+
if (undelivered && undelivered !== recordedIds[key]) {
|
|
2739
2813
|
return {
|
|
2740
2814
|
step: null,
|
|
2741
2815
|
blocked: `${org.slug}'s ai key ${undelivered} was minted but not delivered and must be revoked in ai before a new one is minted (https://github.com/wtfalch/ai/issues/138)`,
|
|
@@ -2751,11 +2825,14 @@ function planAiKey(org, recordedIds, aiReady) {
|
|
|
2751
2825
|
desired: {
|
|
2752
2826
|
name: `${org.slug}-app`,
|
|
2753
2827
|
expiresDays: 365,
|
|
2754
|
-
permissions
|
|
2828
|
+
permissions,
|
|
2829
|
+
...(widen ? { mode: 'widen' } : {}),
|
|
2755
2830
|
},
|
|
2756
2831
|
},
|
|
2757
2832
|
step: {
|
|
2758
|
-
description:
|
|
2833
|
+
description: widen
|
|
2834
|
+
? `mint ${org.slug}'s own ai key again with the read permissions Boule's key needs; the old one stays live until it expires`
|
|
2835
|
+
: `mint ${org.slug}'s own ai key`,
|
|
2759
2836
|
destroys: false,
|
|
2760
2837
|
migrates: false,
|
|
2761
2838
|
},
|
|
@@ -3332,6 +3409,120 @@ function planAnalyticsBackend(org, byKey, claimed, recordedIds, otherSlugs) {
|
|
|
3332
3409
|
blocked: null,
|
|
3333
3410
|
};
|
|
3334
3411
|
}
|
|
3412
|
+
/**
|
|
3413
|
+
* The company's reporting backend, the same shape as `planAnalyticsBackend`: a per-org singleton keyed by the
|
|
3414
|
+
* org id under its own `reporting` kind, built from https://github.com/wtfalch/reporting's root Dockerfile
|
|
3415
|
+
* (`executor.ts`'s `coolifyReporting`) with a database of its own. Planned only while `reportingWanted(org)`; once live it is kept and never parked. A live
|
|
3416
|
+
* app is planned again when its `APP_URL` or `AUTH_ORGANIZATION_ID` is missing or different. It needs no
|
|
3417
|
+
* sign-in client and no issuer; only the company's sign-in organisation id (recorded by an earlier step).
|
|
3418
|
+
*
|
|
3419
|
+
* Mail (reporting#103, foundry#1128), on the same terms as Boule's: a hosted-mail company whose domain is
|
|
3420
|
+
* recorded gives the backend `MAIL_FROM` (`reportingMailFrom`) and its `<slug>-app` Lettermint token, as a
|
|
3421
|
+
* consumer of the shared mint rule. Blocked, not failing, while the platform cannot supply the token: the
|
|
3422
|
+
* backend is planned without mail and the reason is its own line. The token is compared by presence
|
|
3423
|
+
* (`config.mailTokenHeld`), never by value, and a reader that did not say reads as holding it, so a backend
|
|
3424
|
+
* that lacks either is planned once and converges.
|
|
3425
|
+
*/
|
|
3426
|
+
function planReportingBackend(org, byKey, claimed, recordedIds, otherSlugs, readiness = DEFAULT_ORG_PLATFORM_READINESS) {
|
|
3427
|
+
const key = resourceKey('coolify', 'reporting', org.id);
|
|
3428
|
+
const live = byKey.get(key);
|
|
3429
|
+
if (live)
|
|
3430
|
+
claimed.add(key);
|
|
3431
|
+
if (!reportingWanted(org))
|
|
3432
|
+
return { step: null, blocked: null, mailBlocked: null };
|
|
3433
|
+
const tooLong = [
|
|
3434
|
+
[`${org.slug}-reporting`, 'the reporting host label and app name'],
|
|
3435
|
+
[`${org.slug}_reporting_db_rt`, 'the reporting database role'],
|
|
3436
|
+
].filter(([name]) => name.length > 63);
|
|
3437
|
+
if (tooLong.length > 0) {
|
|
3438
|
+
return {
|
|
3439
|
+
step: null,
|
|
3440
|
+
mailBlocked: null,
|
|
3441
|
+
blocked: `${org.slug}'s reporting backend is blocked, not planned: ${tooLong.map(([n, what]) => `${what} '${n}' is over 63 characters`).join(', ')}; shorten the slug`,
|
|
3442
|
+
};
|
|
3443
|
+
}
|
|
3444
|
+
if (otherSlugs.includes(`${org.slug}-reporting`)) {
|
|
3445
|
+
return {
|
|
3446
|
+
step: null,
|
|
3447
|
+
mailBlocked: null,
|
|
3448
|
+
blocked: `${org.slug}'s reporting backend is blocked, not planned: another declared org has the slug '${org.slug}-reporting', which is this backend's hostname`,
|
|
3449
|
+
};
|
|
3450
|
+
}
|
|
3451
|
+
const mailDomain = recordedIds[MAIL_DOMAIN_SCOPE_KEY]?.toLowerCase();
|
|
3452
|
+
const mailWanted = bouleMailWanted(org, recordedIds) && Boolean(mailDomain);
|
|
3453
|
+
const isApexOrg = Boolean(mailDomain && readiness.platformApex && mailDomain === readiness.platformApex.toLowerCase());
|
|
3454
|
+
let mailBlockedReason = null;
|
|
3455
|
+
if (mailWanted && isApexOrg) {
|
|
3456
|
+
if (!readiness.platformLettermintProjectId) {
|
|
3457
|
+
mailBlockedReason = 'platform.mail.lettermintProjectId is not declared yet';
|
|
3458
|
+
}
|
|
3459
|
+
else if (readiness.mailKeyHeld !== true) {
|
|
3460
|
+
mailBlockedReason = `the platform's Lettermint key is not held yet (held-key ref ${PLATFORM_AUTH_BROKER_MAIL_KEY_REF})`;
|
|
3461
|
+
}
|
|
3462
|
+
}
|
|
3463
|
+
else if (mailWanted) {
|
|
3464
|
+
mailBlockedReason = edgeNeeded(readiness, ['zones', 'dns']);
|
|
3465
|
+
}
|
|
3466
|
+
const mailBlocked = mailBlockedReason
|
|
3467
|
+
? `${org.slug}'s reporting backend mail is blocked, not planned: ${mailBlockedReason}`
|
|
3468
|
+
: null;
|
|
3469
|
+
const mailOn = mailWanted && !mailBlocked;
|
|
3470
|
+
// `coolifyReporting` writes nothing without the sign-in organisation, so the step waits for it here.
|
|
3471
|
+
const { env, missing } = desiredReportingEnv(org, recordedIds, mailOn ? (recordedIds[MAIL_DOMAIN_SCOPE_KEY] ?? null) : null);
|
|
3472
|
+
if (missing.length > 0) {
|
|
3473
|
+
return {
|
|
3474
|
+
step: null,
|
|
3475
|
+
mailBlocked: null,
|
|
3476
|
+
blocked: `${org.slug}'s reporting backend is blocked, not planned: the company's sign-in organisation is not recorded yet`,
|
|
3477
|
+
};
|
|
3478
|
+
}
|
|
3479
|
+
const action = {
|
|
3480
|
+
system: 'coolify',
|
|
3481
|
+
kind: 'reporting',
|
|
3482
|
+
key: org.id,
|
|
3483
|
+
// Only a backend that gets mail carries the mail fields, so every other plan is unchanged. The platform
|
|
3484
|
+
// values go in like Boule's: the executor writes what the plan showed.
|
|
3485
|
+
desired: mailOn
|
|
3486
|
+
? {
|
|
3487
|
+
mail: true,
|
|
3488
|
+
platformApex: readiness.platformApex ?? null,
|
|
3489
|
+
platformLettermintProjectId: readiness.platformLettermintProjectId ?? null,
|
|
3490
|
+
}
|
|
3491
|
+
: {},
|
|
3492
|
+
};
|
|
3493
|
+
if (!live) {
|
|
3494
|
+
return {
|
|
3495
|
+
step: {
|
|
3496
|
+
action,
|
|
3497
|
+
step: {
|
|
3498
|
+
description: `provision ${org.slug}'s reporting backend`,
|
|
3499
|
+
destroys: false,
|
|
3500
|
+
migrates: false,
|
|
3501
|
+
},
|
|
3502
|
+
},
|
|
3503
|
+
blocked: null,
|
|
3504
|
+
mailBlocked,
|
|
3505
|
+
};
|
|
3506
|
+
}
|
|
3507
|
+
const drift = reportingEnvDrift(env, (live.config.env ?? {}));
|
|
3508
|
+
// The token by presence only; a reader that did not say reads as holding it.
|
|
3509
|
+
if (mailOn && live.config.mailTokenHeld === false)
|
|
3510
|
+
drift.push(REPORTING_MAIL_TOKEN_ENV);
|
|
3511
|
+
if (drift.length === 0)
|
|
3512
|
+
return { step: null, blocked: null, mailBlocked };
|
|
3513
|
+
return {
|
|
3514
|
+
step: {
|
|
3515
|
+
action,
|
|
3516
|
+
step: {
|
|
3517
|
+
description: `update ${org.slug}'s reporting backend config (${drift.join(', ')} missing or different)`,
|
|
3518
|
+
destroys: false,
|
|
3519
|
+
migrates: false,
|
|
3520
|
+
},
|
|
3521
|
+
},
|
|
3522
|
+
blocked: null,
|
|
3523
|
+
mailBlocked,
|
|
3524
|
+
};
|
|
3525
|
+
}
|
|
3335
3526
|
/**
|
|
3336
3527
|
* C3: which credentials the org's Boule is given, and why one is not. Archon's credential goes to every
|
|
3337
3528
|
* Boule the host can mint it for; the customers directory credential only while `customers.enabled`. Each
|
|
@@ -3883,6 +4074,26 @@ function planApp(org, entryKey, byKey, claimed, recordedIds, plannedSteps, authI
|
|
|
3883
4074
|
blocked,
|
|
3884
4075
|
};
|
|
3885
4076
|
}
|
|
4077
|
+
// Rule 4, from live state: an entry that sends mail and whose app has no
|
|
4078
|
+
// `MAIL_APP_KEY` yet gets it pushed once (`pushAppEnv` writes it on every
|
|
4079
|
+
// run). Its value is the entry key, so the name alone says it is there. A
|
|
4080
|
+
// reader that did not list env names reads as compliant, as above.
|
|
4081
|
+
if (entry &&
|
|
4082
|
+
sendsMail(entry) &&
|
|
4083
|
+
Array.isArray(envNames) &&
|
|
4084
|
+
!envNames.includes(MAIL_APP_KEY_ENV)) {
|
|
4085
|
+
return {
|
|
4086
|
+
step: {
|
|
4087
|
+
action,
|
|
4088
|
+
step: {
|
|
4089
|
+
description: `give ${org.slug}'s ${label} its ${MAIL_APP_KEY_ENV} (the key its mail is tagged with) and restart it`,
|
|
4090
|
+
destroys: false,
|
|
4091
|
+
migrates: false,
|
|
4092
|
+
},
|
|
4093
|
+
},
|
|
4094
|
+
blocked,
|
|
4095
|
+
};
|
|
4096
|
+
}
|
|
3886
4097
|
// #243 (rule 3): a changed entry `env` reaches an app that is already
|
|
3887
4098
|
// live. `pushAppEnv` records what it pushed, so this plans the same step
|
|
3888
4099
|
// again, with a redeploy, only once the resolved map differs from that.
|
|
@@ -4551,7 +4762,8 @@ function planIssuerApps(org, byKey, claimed) {
|
|
|
4551
4762
|
function planEntryClients(org, byKey, claimed) {
|
|
4552
4763
|
const steps = [];
|
|
4553
4764
|
for (const entry of org.hosting.entries) {
|
|
4554
|
-
|
|
4765
|
+
// ADR 0003: the broker makes the clients of an entry both sign in to when it is registered.
|
|
4766
|
+
if (!entry.uses?.auth || signsInBoth(entry.uses.auth))
|
|
4555
4767
|
continue;
|
|
4556
4768
|
const key = resourceKey('zitadel', 'app', entry.key);
|
|
4557
4769
|
if (byKey.has(key)) {
|
|
@@ -4582,7 +4794,8 @@ export function planBrokerEntries(org, recordedIds, plannedSteps, brokerWired, b
|
|
|
4582
4794
|
const blocked = [];
|
|
4583
4795
|
for (const entry of org.hosting.entries) {
|
|
4584
4796
|
const auth = entry.uses?.auth;
|
|
4585
|
-
|
|
4797
|
+
// An entry both sign in to is registered (`registered-app.ts`), not an `AUTH_APPS` entry.
|
|
4798
|
+
if (!auth || signsInBoth(auth))
|
|
4586
4799
|
continue;
|
|
4587
4800
|
const at = `${org.slug}'s ${entry.key} broker entry`;
|
|
4588
4801
|
const key = `coolify:broker-entry:${entry.key}`;
|
|
@@ -4739,7 +4952,7 @@ function planBouleBrokerEntry(org, recordedIds, plannedSteps, brokerWired) {
|
|
|
4739
4952
|
function planCustomers(org, live, claimed, recordedIds, metadataWired, policyAvailable = true) {
|
|
4740
4953
|
const signUp = org.customers?.signUp === true;
|
|
4741
4954
|
const wanted = org.customers?.enabled === true ||
|
|
4742
|
-
org.hosting.entries.some((entry) => entry.uses?.auth
|
|
4955
|
+
org.hosting.entries.some((entry) => signsInCustomers(entry.uses?.auth));
|
|
4743
4956
|
// The sign-in page reads this from the customers organisation: any entry with an emailed code turns it on.
|
|
4744
4957
|
const wantedSignIn = wanted && org.hosting.entries.some((entry) => entry.uses?.auth?.signIn === 'email-code')
|
|
4745
4958
|
? 'email-code'
|
|
@@ -11,8 +11,11 @@ import type { OrgPlatformReadiness } from './platform-org.js';
|
|
|
11
11
|
export declare const FILES_KEY_DAYS = 365;
|
|
12
12
|
/** A key with fewer days left than this is renewed. */
|
|
13
13
|
export declare const FILES_KEY_RENEW_WINDOW_DAYS = 30;
|
|
14
|
-
/**
|
|
15
|
-
|
|
14
|
+
/**
|
|
15
|
+
* What the org's own key holds: it mints and revokes, and holds what its entries and Boule's key may hold
|
|
16
|
+
* (foundry#1121: `files.usage:read`, `files.shares:read` and `files.files:share` for Boule's Files page).
|
|
17
|
+
*/
|
|
18
|
+
export declare const FILES_ROOT_PERMISSIONS: readonly ["credentials:create", "credentials:revoke", "files.collections:read", "files.files:read", "files.files:download", "files.files:upload", "files.files:replace", "files.files:delete", "files.usage:read", "files.shares:read", "files.files:share"];
|
|
16
19
|
/**
|
|
17
20
|
* Flat scope ids (no `:`). The org's own key: the id minted last (it blocks
|
|
18
21
|
* only while it differs from the recorded key, which means it was never
|
|
@@ -20,6 +23,12 @@ export declare const FILES_ROOT_PERMISSIONS: readonly ["credentials:create", "cr
|
|
|
20
23
|
*/
|
|
21
24
|
export declare const FILES_ROOT_UNDELIVERED_SCOPE_KEY = "files_key_undelivered";
|
|
22
25
|
export declare const FILES_ROOT_EXPIRES_SCOPE_KEY = "files_key_expires";
|
|
26
|
+
/**
|
|
27
|
+
* foundry#1121: the permissions the recorded org key was minted with, as JSON. A key minted before this was
|
|
28
|
+
* recorded, or with fewer permissions than {@link FILES_ROOT_PERMISSIONS}, is renewed: a key below it can
|
|
29
|
+
* hold only what it holds.
|
|
30
|
+
*/
|
|
31
|
+
export declare const FILES_ROOT_PERMISSIONS_SCOPE_KEY = "files_key_permissions";
|
|
23
32
|
/** Ids of org keys replaced by a renewal, as a JSON array, revoked once every entry holds a key below the new one. */
|
|
24
33
|
export declare const FILES_ROOT_PREVIOUS_SCOPE_KEY = "files_key_previous";
|
|
25
34
|
/** Ids of replaced org keys whose revoke failed, as a JSON array. They expire on their own; the plan lists each. */
|
|
@@ -47,6 +56,26 @@ export declare const entryFilesRetiredScopeKey: (entryKey: string) => string;
|
|
|
47
56
|
export declare const entryFilesUnrevokedScopeKey: (entryKey: string) => string;
|
|
48
57
|
export declare const ENTRY_FILES_UNREVOKED_PREFIX = "files_entry_unrevoked_";
|
|
49
58
|
export declare const ENTRY_FILES_UNDELIVERED_PREFIX = "files_entry_undelivered_";
|
|
59
|
+
/** foundry#1121 (`files-boule-key.ts`). Boule's key is minted, not held yet: its id, never the key. */
|
|
60
|
+
export declare const FILES_BOULE_UNDELIVERED_SCOPE_KEY = "files_boule_key_undelivered";
|
|
61
|
+
/** The id of Boule's key once it is held. */
|
|
62
|
+
export declare const FILES_BOULE_KEY_SCOPE_KEY = "files_boule_key";
|
|
63
|
+
/** The id of the org's own files key Boule's key was minted below. */
|
|
64
|
+
export declare const FILES_BOULE_MINTER_SCOPE_KEY = "files_boule_key_minter";
|
|
65
|
+
/** Ids of Boule keys a renewal replaced, as a JSON array, revoked once the new one is in Boule's env. */
|
|
66
|
+
export declare const FILES_BOULE_PREVIOUS_SCOPE_KEY = "files_boule_key_previous";
|
|
67
|
+
/** Per entry: label to collection name in files, every name the entry ever had. */
|
|
68
|
+
export declare const entryFilesCollectionsScopeKey: (entryKey: string) => string;
|
|
69
|
+
/** The name a new collection of an entry gets in files. */
|
|
70
|
+
export declare const entryCollectionName: (entryKey: string, label: string) => string;
|
|
71
|
+
/**
|
|
72
|
+
* Every collection name the entry has had, label to name: the recorded map, or, before one is recorded, each
|
|
73
|
+
* label of the entry's last applied files marker under its own name (the name files was given before
|
|
74
|
+
* collections were named per app).
|
|
75
|
+
*/
|
|
76
|
+
export declare function knownCollectionNames(entryKey: string, recordedIds: Readonly<Record<string, string>>): Record<string, string>;
|
|
77
|
+
/** The files name of each collection the entry declares now, label to name, in the declared order. */
|
|
78
|
+
export declare function entryCollectionNames(entry: Pick<HostingEntry, 'key' | 'uses'>, recordedIds: Readonly<Record<string, string>>): Record<string, string>;
|
|
50
79
|
export declare function parseIdList(raw: string | undefined): string[];
|
|
51
80
|
export declare const filesRootKeyName: (org: DeclaredOrg) => string;
|
|
52
81
|
export declare const entryFilesKeyName: (org: DeclaredOrg, entry: HostingEntry) => string;
|
|
@@ -58,8 +87,15 @@ export interface EntryFilesWant {
|
|
|
58
87
|
filesUrl: string;
|
|
59
88
|
/** `https://share.<domain>`, set only when the entry declares `shareHost`. */
|
|
60
89
|
shareOrigin?: string;
|
|
90
|
+
/** foundry#1121: each collection's name in files, label to name (`files-boule-key.ts`). */
|
|
91
|
+
collectionNames: Record<string, string>;
|
|
61
92
|
}
|
|
62
|
-
export declare function wantedEntryFiles(entry: HostingEntry, filesUrl: string, shareHostname?: string | null): EntryFilesWant;
|
|
93
|
+
export declare function wantedEntryFiles(entry: HostingEntry, filesUrl: string, shareHostname?: string | null, recordedIds?: Readonly<Record<string, string>>): EntryFilesWant;
|
|
94
|
+
/**
|
|
95
|
+
* foundry#1121: Boule's files key, when it has one, is below `rootKeyId` and delivered, so revoking the org
|
|
96
|
+
* key it replaced revokes nothing Boule still uses.
|
|
97
|
+
*/
|
|
98
|
+
export declare function bouleKeyBelow(recordedIds: Readonly<Record<string, string>>, rootKeyId: string | undefined): boolean;
|
|
63
99
|
export type EntryFilesMode = 'mint' | 'renew' | 'settings' | 'finish' | 'retire';
|
|
64
100
|
type Blocked = Array<{
|
|
65
101
|
description: string;
|