@wtfalch/foundry 0.60.0 → 0.61.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/apps/cloudflare.d.ts +173 -0
- package/dist/apps/cloudflare.js +17 -0
- package/dist/apps/deploy.d.ts +116 -0
- package/dist/apps/deploy.js +791 -0
- package/dist/apps/github.d.ts +47 -0
- package/dist/apps/github.js +201 -0
- package/dist/apps/index.d.ts +9 -0
- package/dist/apps/index.js +9 -0
- package/dist/apps/migrations.d.ts +110 -0
- package/dist/apps/migrations.js +458 -0
- package/dist/audit/connector.d.ts +43 -0
- package/dist/audit/connector.js +22 -0
- package/dist/audit/evidence.d.ts +49 -0
- package/dist/audit/evidence.js +56 -0
- package/dist/audit/index.d.ts +9 -0
- package/dist/audit/index.js +9 -0
- package/dist/audit/migrations.d.ts +5 -0
- package/dist/audit/migrations.js +5 -0
- package/dist/audit/registry.d.ts +49 -0
- package/dist/audit/registry.js +44 -0
- package/dist/audit/seal.d.ts +49 -0
- package/dist/audit/seal.js +131 -0
- package/dist/audit/sealer-role.d.ts +33 -0
- package/dist/audit/sealer-role.js +56 -0
- package/dist/audit/signer.d.ts +40 -0
- package/dist/audit/signer.js +78 -0
- package/dist/audit/tick.d.ts +36 -0
- package/dist/audit/tick.js +87 -0
- package/dist/catalogue/copy-en.d.ts +1 -0
- package/dist/catalogue/copy-en.js +129 -0
- package/dist/catalogue/index.d.ts +9 -0
- package/dist/catalogue/index.js +21 -0
- package/dist/catalogue/integrations.d.ts +4 -0
- package/dist/catalogue/integrations.js +104 -0
- package/dist/catalogue/systems.d.ts +7 -0
- package/dist/catalogue/systems.js +31 -0
- package/dist/catalogue/types.d.ts +110 -0
- package/dist/catalogue/types.js +1 -0
- package/dist/entry-rules.d.ts +53 -0
- package/dist/entry-rules.js +71 -0
- package/dist/hosted-label.d.ts +13 -0
- package/dist/hosted-label.js +81 -0
- package/dist/index.d.ts +10 -5
- package/dist/index.js +5 -3
- package/dist/migrations/0062_hosting_github.sql +6 -0
- package/dist/migrations/0063_boule_apps.sql +7 -0
- package/dist/migrations/0064_requested_changes_boule_apps.sql +6 -0
- package/dist/modules/ai.d.ts +3 -0
- package/dist/modules/ai.js +64 -0
- package/dist/modules/assistants.d.ts +3 -0
- package/dist/modules/assistants.js +72 -0
- package/dist/modules/audit-ledger.d.ts +29 -0
- package/dist/modules/audit-ledger.js +49 -0
- package/dist/modules/audit.d.ts +7 -0
- package/dist/modules/audit.js +44 -0
- package/dist/modules/database.d.ts +23 -0
- package/dist/modules/database.js +189 -0
- package/dist/modules/email.d.ts +3 -0
- package/dist/modules/email.js +18 -0
- package/dist/modules/entry-parts.d.ts +23 -0
- package/dist/modules/entry-parts.js +63 -0
- package/dist/modules/files.d.ts +3 -0
- package/dist/modules/files.js +64 -0
- package/dist/modules/index.d.ts +74 -0
- package/dist/modules/index.js +774 -0
- package/dist/modules/jobs.d.ts +3 -0
- package/dist/modules/jobs.js +23 -0
- package/dist/modules/organisations.d.ts +3 -0
- package/dist/modules/organisations.js +20 -0
- package/dist/modules/outputs.d.ts +9 -0
- package/dist/modules/outputs.js +38 -0
- package/dist/modules/sign-in.d.ts +3 -0
- package/dist/modules/sign-in.js +197 -0
- package/dist/modules/signed-records.d.ts +3 -0
- package/dist/modules/signed-records.js +32 -0
- package/dist/modules/types.d.ts +194 -0
- package/dist/modules/types.js +1 -0
- package/dist/modules/workspaces.d.ts +3 -0
- package/dist/modules/workspaces.js +75 -0
- package/dist/org.d.ts +163 -22
- package/dist/org.js +419 -47
- package/dist/plan/apps-db-route.d.ts +90 -0
- package/dist/plan/apps-db-route.js +168 -0
- package/dist/plan/apps-zones.d.ts +56 -0
- package/dist/plan/apps-zones.js +118 -0
- package/dist/plan/archon-blue-green.js +11 -1
- package/dist/plan/blue-green-apply.d.ts +37 -1
- package/dist/plan/blue-green-apply.js +55 -15
- package/dist/plan/blue-green.d.ts +41 -1
- package/dist/plan/blue-green.js +80 -2
- package/dist/plan/boule.d.ts +23 -1
- package/dist/plan/boule.js +35 -2
- package/dist/plan/broker-entries.d.ts +28 -3
- package/dist/plan/broker-entries.js +74 -3
- package/dist/plan/broker-registrar.d.ts +168 -0
- package/dist/plan/broker-registrar.js +440 -0
- package/dist/plan/broker-workspaces-database.d.ts +48 -0
- package/dist/plan/broker-workspaces-database.js +87 -0
- package/dist/plan/desktop-callback.js +12 -7
- package/dist/plan/diff.d.ts +8 -2
- package/dist/plan/diff.js +373 -42
- package/dist/plan/edge-access.d.ts +6 -0
- package/dist/plan/edge-dns.d.ts +6 -0
- package/dist/plan/edge-dns.js +9 -7
- package/dist/plan/entry-ai-key.d.ts +1 -1
- package/dist/plan/entry-ai-key.js +2 -2
- package/dist/plan/entry-audit.d.ts +58 -0
- package/dist/plan/entry-audit.js +122 -0
- package/dist/plan/entry-customer-grants.d.ts +75 -0
- package/dist/plan/entry-customer-grants.js +154 -0
- package/dist/plan/entry-edge-key.d.ts +26 -1
- package/dist/plan/entry-edge-key.js +42 -3
- package/dist/plan/entry-jobs.d.ts +64 -0
- package/dist/plan/entry-jobs.js +92 -0
- package/dist/plan/entry-repo-key-apply.d.ts +55 -0
- package/dist/plan/entry-repo-key-apply.js +185 -0
- package/dist/plan/entry-repo-key.d.ts +92 -0
- package/dist/plan/entry-repo-key.js +235 -0
- package/dist/plan/entry-workspaces.d.ts +55 -0
- package/dist/plan/entry-workspaces.js +90 -0
- package/dist/plan/executor.d.ts +191 -1
- package/dist/plan/executor.js +1505 -90
- package/dist/plan/gates.d.ts +1 -1
- package/dist/plan/gates.js +62 -9
- package/dist/plan/github-deploy-keys.d.ts +78 -0
- package/dist/plan/github-deploy-keys.js +305 -0
- package/dist/plan/github-source.d.ts +53 -0
- package/dist/plan/github-source.js +140 -0
- package/dist/plan/held-key-refs.d.ts +12 -0
- package/dist/plan/held-key-refs.js +12 -0
- package/dist/plan/hostnames.d.ts +9 -0
- package/dist/plan/hostnames.js +28 -1
- package/dist/plan/index.d.ts +9 -0
- package/dist/plan/index.js +6 -0
- package/dist/plan/jobs-scheduler-http.js +3 -0
- package/dist/plan/keys-registrar-http.d.ts +32 -0
- package/dist/plan/keys-registrar-http.js +105 -0
- package/dist/plan/keys-registrar.d.ts +67 -0
- package/dist/plan/keys-registrar.js +103 -0
- package/dist/plan/live.d.ts +1 -1
- package/dist/plan/locked-hostnames.d.ts +2 -2
- package/dist/plan/locked-hostnames.js +2 -2
- package/dist/plan/login-host.js +11 -5
- package/dist/plan/machine-paths.d.ts +36 -0
- package/dist/plan/machine-paths.js +137 -0
- package/dist/plan/mcp.d.ts +1 -1
- package/dist/plan/mcp.js +49 -17
- package/dist/plan/org-apps-admin.d.ts +108 -0
- package/dist/plan/org-apps-admin.js +434 -0
- package/dist/plan/org-names.js +12 -1
- package/dist/plan/own-diff.d.ts +23 -0
- package/dist/plan/own-diff.js +53 -0
- package/dist/plan/own-hostnames.d.ts +2 -0
- package/dist/plan/own-hostnames.js +1 -0
- package/dist/plan/own-jobs-route.d.ts +47 -0
- package/dist/plan/own-jobs-route.js +91 -0
- package/dist/plan/own-services.d.ts +4 -0
- package/dist/plan/own-services.js +4 -0
- package/dist/plan/plan-apply.d.ts +39 -0
- package/dist/plan/plan-apply.js +163 -9
- package/dist/plan/platform-diff.d.ts +2 -2
- package/dist/plan/platform-diff.js +7 -2
- package/dist/plan/platform-org.d.ts +30 -0
- package/dist/plan/platform-org.js +12 -1
- package/dist/plan/platform-providers.d.ts +2 -0
- package/dist/plan/platform-providers.js +7 -2
- package/dist/plan/post-logout-uris.js +7 -1
- package/dist/plan/private-pages.d.ts +26 -0
- package/dist/plan/private-pages.js +39 -0
- package/dist/plan/proxy-labels.d.ts +12 -1
- package/dist/plan/proxy-labels.js +32 -2
- package/dist/plan/release-host.d.ts +6 -2
- package/dist/plan/release-host.js +36 -8
- package/dist/plan/stage-env.d.ts +19 -1
- package/dist/plan/stage-env.js +25 -1
- package/dist/plan/stage-guards.d.ts +20 -0
- package/dist/plan/stage-guards.js +24 -0
- package/dist/plan/stage-isolation.d.ts +24 -0
- package/dist/plan/stage-isolation.js +29 -1
- package/dist/plan/worker-app-database.d.ts +46 -42
- package/dist/plan/worker-app-database.js +150 -153
- package/dist/plan/worker-app.d.ts +5 -65
- package/dist/plan/worker-app.js +28 -105
- package/dist/plan/worker.d.ts +2 -2
- package/dist/plan/worker.js +2 -1
- package/dist/plan/workspace-client.d.ts +25 -0
- package/dist/plan/workspace-client.js +48 -0
- package/dist/plan/workspace-sign-in.d.ts +55 -0
- package/dist/plan/workspace-sign-in.js +273 -0
- package/dist/plan/workspace-worker.d.ts +4 -0
- package/dist/plan/workspace-worker.js +13 -0
- package/dist/plan/workspace.d.ts +101 -0
- package/dist/plan/workspace.js +305 -0
- package/dist/release/decide.d.ts +30 -1
- package/dist/release/decide.js +73 -11
- package/dist/release/health.d.ts +8 -3
- package/dist/release/health.js +58 -5
- package/dist/release/run.d.ts +2 -5
- package/dist/release/run.js +4 -0
- package/dist/release/state.d.ts +3 -1
- package/dist/release/state.js +6 -0
- package/dist/release/types.d.ts +20 -0
- package/dist/requested-changes.d.ts +11 -2
- package/dist/requested-changes.js +11 -0
- package/dist/service-labels.d.ts +4 -0
- package/dist/service-labels.js +11 -0
- package/dist/slug.d.ts +2 -0
- package/dist/slug.js +10 -6
- package/dist/store/postgres.js +12 -2
- package/dist/store/schema.d.ts +37 -1
- package/dist/store/schema.js +4 -0
- package/dist/worker-app-config.d.ts +1 -11
- package/dist/worker-app-config.js +4 -17
- package/dist/worker-app-migrations.d.ts +105 -0
- package/dist/worker-app-migrations.js +166 -0
- package/dist/worker-app-upload.d.ts +103 -0
- package/dist/worker-app-upload.js +128 -0
- package/package.json +18 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export type { OrgId, MailMode, MailConfig, ChatConfig, CallsConfig, IssuerConfig, SiteConfig, IssuerAppKind, IssuerApp, HostingConfig, HostingEntry, BlueGreenConfig, OrgBuiltinApp, OrgRefusal, OrgRefusalCode, WorkerAppConfig, EntryGate, EntryVolume, HostingEntryUses, EntryAuth, EntryAuthRoom, EntryFiles, EntryAi, EnvValue, EnvHeldSecret, SsoConfig, GuestsConfig, SoloConfig, StorageConfig, AiConfig, BuyerAddress, BuyerIdentity, AdoptedAppForm, AdoptedApp, OwnerIdentity, OwnerMailbox, DeclaredOrg, OrgServiceDatabase, OrgServiceSchema, ServicesConfig, SignInConfig, VersionPin, VersionPinPart, } from './org.js';
|
|
2
|
-
export { APP_ENTRY_KEY, DEFAULT_GATE_LIMIT, FILES_ENTRY_PERMISSIONS, AI_ENTRY_PERMISSIONS, DEFAULT_MAIL_APPS, ORG_SERVICE_SCHEMAS, orgServiceDatabase, orgServiceDatabases, SFU_SLOT_MAX, VERSION_PIN_PARTS, isMainEntry, mainEntry, heldEnvRef, ownerMailboxes, ownsServices, validateBuyerIdentity, validateHosting, normalizeHostingRepos, validateMail, validateOwners, validateServices, validateCustomers, validateSignIn, validateVersionPin, validateSite, } from './org.js';
|
|
1
|
+
export type { OrgId, MailMode, MailConfig, ChatConfig, CallsConfig, IssuerConfig, SiteConfig, IssuerAppKind, IssuerApp, HostingConfig, HostingEntry, HostingGithub, BlueGreenConfig, OrgBuiltinApp, BouleApp, OrgRefusal, OrgRefusalCode, WorkerAppConfig, EntryGate, EntryVolume, HostingEntryUses, EntryAuth, EntryAuthRoom, EntryFiles, EntryAi, EnvValue, EnvHeldSecret, SsoConfig, GuestsConfig, SoloConfig, StorageConfig, AiConfig, BuyerAddress, BuyerIdentity, AdoptedAppForm, AdoptedApp, OwnerIdentity, OwnerMailbox, DeclaredOrg, OrgServiceDatabase, OrgServiceSchema, EntryWorkspace, ServicesConfig, SignInConfig, VersionPin, VersionPinPart, } from './org.js';
|
|
2
|
+
export { APP_ENTRY_KEY, APPS_DOMAIN, DEFAULT_GATE_LIMIT, MAX_WORKSPACE_TESTERS, isLiveEntry, normalizeTesters, FILES_ENTRY_PERMISSIONS, AI_ENTRY_PERMISSIONS, DEFAULT_MAIL_APPS, ORG_SERVICE_SCHEMAS, orgServiceDatabase, orgServiceDatabases, SFU_SLOT_MAX, VERSION_PIN_PARTS, isMainEntry, mainEntry, companyGithubFor, heldEnvRef, ownerMailboxes, ownsServices, validateBuyerIdentity, validateHosting, normalizeHostingRepos, validateMail, validateOwners, validateServices, validateCustomers, validateSignIn, validateVersionPin, validateSite, } from './org.js';
|
|
3
3
|
export { refuseOrg } from './refuse-org.js';
|
|
4
4
|
export { workerAppHasBuild } from './worker-app-config.js';
|
|
5
5
|
export type { DeclaredPlatform, PlatformService, PlatformServiceKey, PlatformSfuService, HeartbeatsProvider, } from './platform.js';
|
|
@@ -53,13 +53,17 @@ export type { FilesHostInput, JobsHostDeps, JobsHostInput, OwnAuthInstance, Plan
|
|
|
53
53
|
export { OWN_AUTH_APPLIED_GIT_REF_SCOPE_KEY, OWN_AUTH_APPLIED_HOSTNAME_SCOPE_KEY, OWN_AUTH_BLOCKED_KEY, OWN_FILES_BLOCKED_KEY, OWN_FILES_R2_BLOCKED_KEY, OWN_JOBS_BLOCKED_KEY, OWN_MAIL_BLOCKED_KEY, planOwnAuth, planOwnFiles, planOwnFilesR2, planOwnJobs, planOwnMail, provisionFilesHost, provisionJobsHost, provisionMailServer, } from './plan/index.js';
|
|
54
54
|
export type { PlanOwnSfuOptions, SfuAppDeps, SfuAppInput } from './plan/index.js';
|
|
55
55
|
export { OWN_SFU_BLOCKED_KEY, OWN_SFU_FIREWALL_KEY, planOwnSfu, provisionSfuApp, sfuConfig, sfuCompose, } from './plan/index.js';
|
|
56
|
-
export type { CoolifyPort, CloudflarePort, ZitadelPort, CallsSfuPort, JobsSchedulerPort, FilesOrganisationPort, AiOrganisationPort, FoundryStepExecutorPorts, PlatformOrgPortSource, AgoraHostPort, AgoraHostValues, LoginHostBinding, LoginHostBindingsPort, LoginHostDiscoveryPort, } from './plan/index.js';
|
|
56
|
+
export type { CoolifyPort, CloudflarePort, ZitadelPort, CallsSfuPort, JobsSchedulerPort, FilesOrganisationPort, AiOrganisationPort, FoundryStepExecutorPorts, KeysRegistrarPort, PlatformOrgPortSource, AgoraHostPort, AgoraHostValues, LoginHostBinding, LoginHostBindingsPort, LoginHostDiscoveryPort, } from './plan/index.js';
|
|
57
57
|
export { FoundryStepExecutor, NotBuiltError, healthUrls, loginHostnameFor } from './plan/index.js';
|
|
58
|
-
export { NO_DOMAIN_ZONE, ownHostnames } from './plan/index.js';
|
|
58
|
+
export { NO_DOMAIN_ZONE, entryMachinePaths, ownHostnames, privatePageGateKey, workspaceHost, } from './plan/index.js';
|
|
59
|
+
export type { PrivatePageScope } from './plan/index.js';
|
|
60
|
+
export type { HostedLabelRefusal } from './hosted-label.js';
|
|
61
|
+
export { hostedLabelRefusal } from './hosted-label.js';
|
|
59
62
|
export type { OwnHostnames } from './plan/index.js';
|
|
60
63
|
export type { EnsureSignInOrgOptions } from './plan/index.js';
|
|
61
64
|
export { ZITADEL_ORG_DOMAIN_SCOPE_KEY, ensureSignInOrg, registerSignInDomain, signInOrgDomain, } from './plan/index.js';
|
|
62
65
|
export { HttpJobsSchedulerClient } from './plan/index.js';
|
|
66
|
+
export { httpKeysRegistrar, keysRegistrarFromConfig, KEYS_PLATFORM_TOKEN_ENV, KEYS_SERVICE_URL_ENV, } from './plan/index.js';
|
|
63
67
|
export type { HealthTarget, HealthTargetDepth, HealthTargetExpect } from './plan/index.js';
|
|
64
68
|
export { healthTargets, platformHealthTargets } from './plan/index.js';
|
|
65
69
|
export type { ScopeResolver, RecordedIds, DnsRecordOwners } from './plan/index.js';
|
|
@@ -69,8 +73,9 @@ export { adaptZitadelListApps } from './plan/index.js';
|
|
|
69
73
|
export type { MailIdentityApp, MailIdentityAudienceCheck } from './plan/index.js';
|
|
70
74
|
export { checkMailIdentityAudience } from './plan/index.js';
|
|
71
75
|
export type { MailAllowlistApp } from './plan/index.js';
|
|
76
|
+
export type { DeployKeyTarget, GithubDeployKey, GithubDeployKeysPort } from './plan/index.js';
|
|
72
77
|
export { checkMailAppsAllowlist } from './plan/index.js';
|
|
73
|
-
export { FoundryPlatformStepExecutor, ProvidersPlatformCloudflareReader, ProvidersPlatformCoolifyReader, createZitadelAdminMinter, createZitadelLoginPolicy, createZitadelOrgMetadata, createZitadelProjectRoles, PLATFORM_AUTH_KEY, PLATFORM_MAIL_KEY, PLATFORM_MAIL_RELAY_KEY, PLATFORM_MAIL_RELAY_ROUTES_SCOPE_KEY, PLATFORM_MAIL_ADMIN_KEY_DESCRIPTION, } from './plan/index.js';
|
|
78
|
+
export { FoundryPlatformStepExecutor, ProvidersPlatformCloudflareReader, ProvidersPlatformCoolifyReader, createGithubDeployKeys, createZitadelAdminMinter, createZitadelLoginPolicy, createZitadelOrgMetadata, createZitadelProjectRoles, PLATFORM_AUTH_KEY, PLATFORM_MAIL_KEY, PLATFORM_MAIL_RELAY_KEY, PLATFORM_MAIL_RELAY_ROUTES_SCOPE_KEY, PLATFORM_MAIL_ADMIN_KEY_DESCRIPTION, } from './plan/index.js';
|
|
74
79
|
export type { PlatformAuthDeps, PlatformAuthSmtp, PlatformAuthRuntime, PlatformFilesDeps, PlatformFilesRuntime, PlatformEdgeDeps, PlatformDnsFactory, PlatformAccessFactory, PlatformUptimeAccessFactory, PlatformUptimeAccessPort, PlatformUptimeChatDeps, PlatformUptimeDeps, PlatformGithubReleaseHookDeps, PlatformWorkersFactory, PlatformWorkersPort, PlatformAccessPort, PlatformTunnelsFactory, PlatformEdgeRuntime, } from './plan/index.js';
|
|
75
80
|
export type { PlatformMailDeps, PlatformMailAdminPort, PlatformMailRuntime, } from './plan/index.js';
|
|
76
81
|
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';
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { APP_ENTRY_KEY, DEFAULT_GATE_LIMIT, FILES_ENTRY_PERMISSIONS, AI_ENTRY_PERMISSIONS, DEFAULT_MAIL_APPS, ORG_SERVICE_SCHEMAS, orgServiceDatabase, orgServiceDatabases, SFU_SLOT_MAX, VERSION_PIN_PARTS, isMainEntry, mainEntry, heldEnvRef, ownerMailboxes, ownsServices, validateBuyerIdentity, validateHosting, normalizeHostingRepos, validateMail, validateOwners, validateServices, validateCustomers, validateSignIn, validateVersionPin, validateSite, } from './org.js';
|
|
1
|
+
export { APP_ENTRY_KEY, APPS_DOMAIN, DEFAULT_GATE_LIMIT, MAX_WORKSPACE_TESTERS, isLiveEntry, normalizeTesters, FILES_ENTRY_PERMISSIONS, AI_ENTRY_PERMISSIONS, DEFAULT_MAIL_APPS, ORG_SERVICE_SCHEMAS, orgServiceDatabase, orgServiceDatabases, SFU_SLOT_MAX, VERSION_PIN_PARTS, isMainEntry, mainEntry, companyGithubFor, heldEnvRef, ownerMailboxes, ownsServices, validateBuyerIdentity, validateHosting, normalizeHostingRepos, validateMail, validateOwners, validateServices, validateCustomers, validateSignIn, validateVersionPin, validateSite, } from './org.js';
|
|
2
2
|
export { refuseOrg } from './refuse-org.js';
|
|
3
3
|
export { workerAppHasBuild } from './worker-app-config.js';
|
|
4
4
|
export { PLATFORM_SERVICE_KEYS, HEARTBEATS_PROVIDERS, defaultPlatformHostname, validatePlatform, } from './platform.js';
|
|
@@ -32,15 +32,17 @@ export { OWN_FILES_R2_SCOPE_KEY, OWN_FILES_R2_UNDELIVERED_SCOPE_KEY, OWN_SERVICE
|
|
|
32
32
|
export { OWN_AUTH_APPLIED_GIT_REF_SCOPE_KEY, OWN_AUTH_APPLIED_HOSTNAME_SCOPE_KEY, OWN_AUTH_BLOCKED_KEY, OWN_FILES_BLOCKED_KEY, OWN_FILES_R2_BLOCKED_KEY, OWN_JOBS_BLOCKED_KEY, OWN_MAIL_BLOCKED_KEY, planOwnAuth, planOwnFiles, planOwnFilesR2, planOwnJobs, planOwnMail, provisionFilesHost, provisionJobsHost, provisionMailServer, } from './plan/index.js';
|
|
33
33
|
export { OWN_SFU_BLOCKED_KEY, OWN_SFU_FIREWALL_KEY, planOwnSfu, provisionSfuApp, sfuConfig, sfuCompose, } from './plan/index.js';
|
|
34
34
|
export { FoundryStepExecutor, NotBuiltError, healthUrls, loginHostnameFor } from './plan/index.js';
|
|
35
|
-
export { NO_DOMAIN_ZONE, ownHostnames } from './plan/index.js';
|
|
35
|
+
export { NO_DOMAIN_ZONE, entryMachinePaths, ownHostnames, privatePageGateKey, workspaceHost, } from './plan/index.js';
|
|
36
|
+
export { hostedLabelRefusal } from './hosted-label.js';
|
|
36
37
|
export { ZITADEL_ORG_DOMAIN_SCOPE_KEY, ensureSignInOrg, registerSignInDomain, signInOrgDomain, } from './plan/index.js';
|
|
37
38
|
export { HttpJobsSchedulerClient } from './plan/index.js';
|
|
39
|
+
export { httpKeysRegistrar, keysRegistrarFromConfig, KEYS_PLATFORM_TOKEN_ENV, KEYS_SERVICE_URL_ENV, } from './plan/index.js';
|
|
38
40
|
export { healthTargets, platformHealthTargets } from './plan/index.js';
|
|
39
41
|
export { ProvidersAiReader, ProvidersCloudflareReader, ProvidersCmsReader, ProvidersCoolifyReader, ProvidersZitadelReader, storeLoginHostResolver, storeScopeResolver, storeRecordedIds, } from './plan/index.js';
|
|
40
42
|
export { adaptZitadelListApps } from './plan/index.js';
|
|
41
43
|
export { checkMailIdentityAudience } from './plan/index.js';
|
|
42
44
|
export { checkMailAppsAllowlist } from './plan/index.js';
|
|
43
|
-
export { FoundryPlatformStepExecutor, ProvidersPlatformCloudflareReader, ProvidersPlatformCoolifyReader, createZitadelAdminMinter, createZitadelLoginPolicy, createZitadelOrgMetadata, createZitadelProjectRoles, PLATFORM_AUTH_KEY, PLATFORM_MAIL_KEY, PLATFORM_MAIL_RELAY_KEY, PLATFORM_MAIL_RELAY_ROUTES_SCOPE_KEY, PLATFORM_MAIL_ADMIN_KEY_DESCRIPTION, } from './plan/index.js';
|
|
45
|
+
export { FoundryPlatformStepExecutor, ProvidersPlatformCloudflareReader, ProvidersPlatformCoolifyReader, createGithubDeployKeys, createZitadelAdminMinter, createZitadelLoginPolicy, createZitadelOrgMetadata, createZitadelProjectRoles, PLATFORM_AUTH_KEY, PLATFORM_MAIL_KEY, PLATFORM_MAIL_RELAY_KEY, PLATFORM_MAIL_RELAY_ROUTES_SCOPE_KEY, PLATFORM_MAIL_ADMIN_KEY_DESCRIPTION, } from './plan/index.js';
|
|
44
46
|
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';
|
|
45
47
|
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';
|
|
46
48
|
export { MissingCallsSfuConnectionError, applyCallsSfuPlatformStep, callsSfuAppliedFingerprint, } from './plan/index.js';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
-- @wtfalch/foundry: the company's installation of the platform's GitHub App (`HostingConfig.github`).
|
|
2
|
+
-- Mirrors `src/store/schema.ts`'s `hostingGithub`. Additive and nullable, like 0060_hosting_repos.sql:
|
|
3
|
+
-- a row written before it reads as no installation, and an older reader ignores the column.
|
|
4
|
+
|
|
5
|
+
alter table foundry_declared_orgs
|
|
6
|
+
add column if not exists hosting_github jsonb;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
-- @wtfalch/foundry: `boule_apps` is `org.boule.apps` (F3, F5, F7): the company's Boule installs and runs apps, so
|
|
2
|
+
-- foundry gives it the managed-apps platform keys (edge, sign-in broker registrar, signed-records registrar).
|
|
3
|
+
-- Default false. Additive like 0059_org_builtin_blue_green.sql: `if not exists`, safe to run at container start on
|
|
4
|
+
-- live rows. Mirrors src/store/schema.ts's `declaredOrgs`.
|
|
5
|
+
|
|
6
|
+
alter table foundry_declared_orgs
|
|
7
|
+
add column if not exists boule_apps boolean not null default false;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
-- @wtfalch/foundry: a company's Boule can request `bouleApps` (turn `org.boule.apps` on or off).
|
|
2
|
+
alter table foundry_requested_changes
|
|
3
|
+
drop constraint if exists foundry_requested_changes_field_check;
|
|
4
|
+
alter table foundry_requested_changes
|
|
5
|
+
add constraint foundry_requested_changes_field_check
|
|
6
|
+
check (field in ('calls', 'customers', 'storageLimit', 'aiLimit', 'domain', 'mailTier', 'app', 'build', 'bouleApps'));
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { AI_ENTRY_PERMISSIONS, AI_WEBHOOK_PATH } from '../entry-rules.js';
|
|
2
|
+
import { list, setUse, text } from './entry-parts.js';
|
|
3
|
+
/** `uses.ai`: the app calls the AI tasks set up for the company. */
|
|
4
|
+
export const ai = {
|
|
5
|
+
view: {
|
|
6
|
+
id: 'ai',
|
|
7
|
+
name: 'AI',
|
|
8
|
+
line: 'The app uses AI to do work for people, such as writing a summary or reading a document.',
|
|
9
|
+
category: 'AI',
|
|
10
|
+
icon: 'sparkles',
|
|
11
|
+
runtimes: ['server'],
|
|
12
|
+
requires: [],
|
|
13
|
+
availability: 'ready',
|
|
14
|
+
fields: [
|
|
15
|
+
{
|
|
16
|
+
key: 'offerings',
|
|
17
|
+
label: 'AI tasks the app may use',
|
|
18
|
+
help: 'The names of the AI tasks set up for your company.',
|
|
19
|
+
kind: 'list',
|
|
20
|
+
required: true,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
key: 'webhookPath',
|
|
24
|
+
label: 'Page that receives finished AI work',
|
|
25
|
+
help: 'A page on your app, such as /api/ai/done. Leave it empty if the app waits for the answer.',
|
|
26
|
+
kind: 'path',
|
|
27
|
+
advanced: true,
|
|
28
|
+
pattern: AI_WEBHOOK_PATH.source,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
key: 'permissions',
|
|
32
|
+
label: 'What the app may do',
|
|
33
|
+
help: 'Leave it empty to allow all of them.',
|
|
34
|
+
kind: 'list',
|
|
35
|
+
advanced: true,
|
|
36
|
+
choices: AI_ENTRY_PERMISSIONS.map((value) => ({ value, label: value })),
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
adds: [{ name: 'AI tasks', hint: "Run on your company's own AI account." }],
|
|
40
|
+
},
|
|
41
|
+
toEntry(entry, values) {
|
|
42
|
+
if (values === null)
|
|
43
|
+
return setUse(entry, 'ai', undefined);
|
|
44
|
+
const next = { offerings: list(values, 'offerings') ?? [] };
|
|
45
|
+
const permissions = list(values, 'permissions');
|
|
46
|
+
const webhookPath = text(values, 'webhookPath');
|
|
47
|
+
if (permissions)
|
|
48
|
+
next.permissions = permissions;
|
|
49
|
+
if (webhookPath !== undefined)
|
|
50
|
+
next.webhookPath = webhookPath;
|
|
51
|
+
return setUse(entry, 'ai', next);
|
|
52
|
+
},
|
|
53
|
+
fromEntry(entry) {
|
|
54
|
+
const used = entry.uses?.ai;
|
|
55
|
+
if (!used)
|
|
56
|
+
return null;
|
|
57
|
+
const values = { offerings: [...used.offerings] };
|
|
58
|
+
if (used.permissions?.length)
|
|
59
|
+
values.permissions = [...used.permissions];
|
|
60
|
+
if (used.webhookPath !== undefined)
|
|
61
|
+
values.webhookPath = used.webhookPath;
|
|
62
|
+
return values;
|
|
63
|
+
},
|
|
64
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { MCP_PATH, isPagePath } from '../entry-rules.js';
|
|
2
|
+
import { problem, setAuthPart, text } from './entry-parts.js';
|
|
3
|
+
/** `uses.auth.mcp`: the app serves AI assistants (MCP clients) that sign the customer in. */
|
|
4
|
+
export const assistants = {
|
|
5
|
+
view: {
|
|
6
|
+
id: 'assistants',
|
|
7
|
+
name: 'Help for AI assistants',
|
|
8
|
+
line: 'Your customers connect their own AI assistant, such as Claude or ChatGPT, to the app and use it from their chat.',
|
|
9
|
+
category: 'AI',
|
|
10
|
+
icon: 'bot',
|
|
11
|
+
runtimes: ['server', 'worker'],
|
|
12
|
+
requires: ['sign-in'],
|
|
13
|
+
availability: 'ready',
|
|
14
|
+
fields: [
|
|
15
|
+
{
|
|
16
|
+
key: 'path',
|
|
17
|
+
label: 'Where assistants connect',
|
|
18
|
+
help: 'The address on your app that assistants connect to. Most apps use /mcp.',
|
|
19
|
+
kind: 'path',
|
|
20
|
+
required: true,
|
|
21
|
+
default: '/mcp',
|
|
22
|
+
pattern: MCP_PATH.source,
|
|
23
|
+
max: 200,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
key: 'onboardingPath',
|
|
27
|
+
label: 'Page shown after the first sign-in',
|
|
28
|
+
help: 'A page on your app that tells a new customer what to do next, such as /onboarding. Leave it empty for none.',
|
|
29
|
+
kind: 'path',
|
|
30
|
+
advanced: true,
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
adds: [
|
|
34
|
+
{
|
|
35
|
+
name: 'A connection for AI assistants',
|
|
36
|
+
hint: 'Each customer signs in once from their assistant; the assistant then acts as them.',
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
check(values, modules) {
|
|
41
|
+
const signIn = modules['sign-in'];
|
|
42
|
+
if (signIn?.users !== 'customers' || signIn?.registration !== 'open') {
|
|
43
|
+
return problem(null, 'Help for AI assistants needs Sign-in for your customers, where anyone can make an account: each customer connects their own assistant. In Sign-in, choose "Your customers" and "Anyone".');
|
|
44
|
+
}
|
|
45
|
+
const onboarding = text(values, 'onboardingPath');
|
|
46
|
+
if (onboarding !== undefined && !isPagePath(onboarding)) {
|
|
47
|
+
return problem('onboardingPath', 'The page must start with "/", such as /onboarding, with no spaces, "?", "#" or "..".');
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
},
|
|
51
|
+
toEntry(entry, values) {
|
|
52
|
+
if (values === null)
|
|
53
|
+
return setAuthPart(entry, 'mcp', undefined);
|
|
54
|
+
if (!entry.uses?.auth) {
|
|
55
|
+
return problem(null, 'Help for AI assistants needs Sign-in. Turn on Sign-in first.');
|
|
56
|
+
}
|
|
57
|
+
const onboardingPath = text(values, 'onboardingPath');
|
|
58
|
+
return setAuthPart(entry, 'mcp', {
|
|
59
|
+
path: values.path,
|
|
60
|
+
...(onboardingPath !== undefined ? { onboardingPath } : {}),
|
|
61
|
+
});
|
|
62
|
+
},
|
|
63
|
+
fromEntry(entry) {
|
|
64
|
+
const mcp = entry.uses?.auth?.mcp;
|
|
65
|
+
if (!mcp)
|
|
66
|
+
return null;
|
|
67
|
+
const values = { path: mcp.path };
|
|
68
|
+
if (mcp.onboardingPath !== undefined)
|
|
69
|
+
values.onboardingPath = mcp.onboardingPath;
|
|
70
|
+
return values;
|
|
71
|
+
},
|
|
72
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { WorkerAppConfig } from '../worker-app-config.js';
|
|
2
|
+
/**
|
|
3
|
+
* What `uses.audit` derives from the pinned `@wtfalch/audit`, so an app never lists it by hand (foundry#929).
|
|
4
|
+
*
|
|
5
|
+
* The package is installed under the alias `@wtfalch/audit-sealing` because foundry's own ledger still runs the
|
|
6
|
+
* 0.6.0 migrations (`@wtfalch/audit`, migrations 0012 and 0024-0028 here); the sealing service needs the
|
|
7
|
+
* checkpoint, anchor and bundle code that came after. `audit/ledger-set.test.ts` checks every name below against the
|
|
8
|
+
* installed package's own migrations, so a bump that renames one fails there and not in a company's database.
|
|
9
|
+
*/
|
|
10
|
+
/** The version the lists below were read from. */
|
|
11
|
+
export declare const AUDIT_PACKAGE_VERSION = "0.14.0";
|
|
12
|
+
/** Insert and read only: the runtime role has UPDATE, DELETE and TRUNCATE revoked on each. */
|
|
13
|
+
export declare const AUDIT_APPEND_ONLY_TABLES: readonly string[];
|
|
14
|
+
/**
|
|
15
|
+
* What the app's runtime role may run: the two functions the chain is written through. Not granted: the erase
|
|
16
|
+
* functions (`audit_erase_person`, `audit_seal_erasure`, `audit_pending_erasures`), because they change what the
|
|
17
|
+
* ledger shows and whether the app may call them is an open decision (foundry#929 item 10); and the sealing
|
|
18
|
+
* functions (`audit_chain_leaves`, `audit_retire_signing_key`), which only the platform's sealing role runs.
|
|
19
|
+
*/
|
|
20
|
+
export declare const AUDIT_APP_GRANTS: readonly string[];
|
|
21
|
+
/** What the platform's sealing role runs, beside the app's own: reads every leaf, retires a signing key. */
|
|
22
|
+
export declare const AUDIT_SEALER_GRANTS: readonly string[];
|
|
23
|
+
type Database = NonNullable<WorkerAppConfig['database']>;
|
|
24
|
+
/**
|
|
25
|
+
* The database part with the derived tables and grants in (`on`) or out (`off`). Hand-listed names are kept, a
|
|
26
|
+
* name is never listed twice, and turning it off takes out exactly the derived names. Never changes `database`.
|
|
27
|
+
*/
|
|
28
|
+
export declare function withAuditDatabase(database: Database, on: boolean): Database;
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What `uses.audit` derives from the pinned `@wtfalch/audit`, so an app never lists it by hand (foundry#929).
|
|
3
|
+
*
|
|
4
|
+
* The package is installed under the alias `@wtfalch/audit-sealing` because foundry's own ledger still runs the
|
|
5
|
+
* 0.6.0 migrations (`@wtfalch/audit`, migrations 0012 and 0024-0028 here); the sealing service needs the
|
|
6
|
+
* checkpoint, anchor and bundle code that came after. `audit/ledger-set.test.ts` checks every name below against the
|
|
7
|
+
* installed package's own migrations, so a bump that renames one fails there and not in a company's database.
|
|
8
|
+
*/
|
|
9
|
+
/** The version the lists below were read from. */
|
|
10
|
+
export const AUDIT_PACKAGE_VERSION = '0.14.0';
|
|
11
|
+
/** Insert and read only: the runtime role has UPDATE, DELETE and TRUNCATE revoked on each. */
|
|
12
|
+
export const AUDIT_APPEND_ONLY_TABLES = [
|
|
13
|
+
'audit_events',
|
|
14
|
+
'audit_checkpoints',
|
|
15
|
+
'audit_signing_keys',
|
|
16
|
+
'audit_anchors',
|
|
17
|
+
];
|
|
18
|
+
/**
|
|
19
|
+
* What the app's runtime role may run: the two functions the chain is written through. Not granted: the erase
|
|
20
|
+
* functions (`audit_erase_person`, `audit_seal_erasure`, `audit_pending_erasures`), because they change what the
|
|
21
|
+
* ledger shows and whether the app may call them is an open decision (foundry#929 item 10); and the sealing
|
|
22
|
+
* functions (`audit_chain_leaves`, `audit_retire_signing_key`), which only the platform's sealing role runs.
|
|
23
|
+
*/
|
|
24
|
+
export const AUDIT_APP_GRANTS = ['audit_chain_tail()', 'audit_chain_tail_v2()'];
|
|
25
|
+
/** What the platform's sealing role runs, beside the app's own: reads every leaf, retires a signing key. */
|
|
26
|
+
export const AUDIT_SEALER_GRANTS = [
|
|
27
|
+
'audit_chain_leaves(bigint, integer)',
|
|
28
|
+
'audit_retire_signing_key(text)',
|
|
29
|
+
];
|
|
30
|
+
const without = (list, drop) => (list ?? []).filter((item) => !drop.includes(item));
|
|
31
|
+
/**
|
|
32
|
+
* The database part with the derived tables and grants in (`on`) or out (`off`). Hand-listed names are kept, a
|
|
33
|
+
* name is never listed twice, and turning it off takes out exactly the derived names. Never changes `database`.
|
|
34
|
+
*/
|
|
35
|
+
export function withAuditDatabase(database, on) {
|
|
36
|
+
const { appendOnly: _tables, grants: _grants, ...rest } = database;
|
|
37
|
+
const next = rest;
|
|
38
|
+
const appendOnly = on
|
|
39
|
+
? [...AUDIT_APPEND_ONLY_TABLES, ...without(database.appendOnly, AUDIT_APPEND_ONLY_TABLES)]
|
|
40
|
+
: without(database.appendOnly, AUDIT_APPEND_ONLY_TABLES);
|
|
41
|
+
const grants = on
|
|
42
|
+
? [...AUDIT_APP_GRANTS, ...without(database.grants, AUDIT_APP_GRANTS)]
|
|
43
|
+
: without(database.grants, AUDIT_APP_GRANTS);
|
|
44
|
+
if (appendOnly.length > 0)
|
|
45
|
+
next.appendOnly = appendOnly;
|
|
46
|
+
if (grants.length > 0)
|
|
47
|
+
next.grants = grants;
|
|
48
|
+
return next;
|
|
49
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AppModule } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* `uses.audit`: the app keeps a tamper-evident audit log. Applied after `database`, which it needs: it adds the
|
|
4
|
+
* derived append-only tables and chain functions to the Worker app's database part (`modules/audit-ledger.ts`) and
|
|
5
|
+
* registers the app's ledger for the platform's sealing service. Turning it off takes out only what it added.
|
|
6
|
+
*/
|
|
7
|
+
export declare const audit: AppModule;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { withAuditDatabase } from './audit-ledger.js';
|
|
2
|
+
import { setUse, withWorkerApp } from './entry-parts.js';
|
|
3
|
+
/**
|
|
4
|
+
* `uses.audit`: the app keeps a tamper-evident audit log. Applied after `database`, which it needs: it adds the
|
|
5
|
+
* derived append-only tables and chain functions to the Worker app's database part (`modules/audit-ledger.ts`) and
|
|
6
|
+
* registers the app's ledger for the platform's sealing service. Turning it off takes out only what it added.
|
|
7
|
+
*/
|
|
8
|
+
export const audit = {
|
|
9
|
+
view: {
|
|
10
|
+
id: 'audit',
|
|
11
|
+
name: 'Audit log',
|
|
12
|
+
line: 'The app keeps a record of what happened that nobody can quietly change afterwards, not even us or your developers.',
|
|
13
|
+
category: 'Data',
|
|
14
|
+
icon: 'shield',
|
|
15
|
+
runtimes: ['worker'],
|
|
16
|
+
requires: ['database'],
|
|
17
|
+
availability: 'ready',
|
|
18
|
+
fields: [],
|
|
19
|
+
adds: [
|
|
20
|
+
{
|
|
21
|
+
name: 'A tamper-evident log',
|
|
22
|
+
hint: 'Each entry is chained to the one before it. The app can add and read entries but never change or delete them.',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: 'Regular seals and outside time stamps',
|
|
26
|
+
hint: 'The platform signs the log on a schedule with a key the app never holds, and has an independent time authority stamp each seal.',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: 'A proof you can check yourself',
|
|
30
|
+
hint: 'Download the log with its seals and check it offline, without trusting us.',
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
toEntry(entry, values) {
|
|
35
|
+
const on = values !== null;
|
|
36
|
+
// Off takes out the derived names only when the entry had the module on: an app that still lists the
|
|
37
|
+
// same tables and grants by hand, and has not switched, keeps them.
|
|
38
|
+
if (!on && entry.uses?.audit !== true)
|
|
39
|
+
return setUse(entry, 'audit', undefined);
|
|
40
|
+
const withUse = setUse(entry, 'audit', on ? true : undefined);
|
|
41
|
+
return withWorkerApp(withUse, (app) => app.database ? { ...app, database: withAuditDatabase(app.database, on) } : app);
|
|
42
|
+
},
|
|
43
|
+
fromEntry: (entry) => (entry.uses?.audit === true ? {} : null),
|
|
44
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { AppModule, ModuleProblem, ModuleValues } from './types.js';
|
|
2
|
+
/** Which copy of a Worker app a database is for. */
|
|
3
|
+
export type DatabaseCopy = 'workspace' | 'live';
|
|
4
|
+
/** One copy's Postgres: migrations run as the owner, the app (through Hyperdrive) as the runtime user. */
|
|
5
|
+
export interface DatabaseAddresses {
|
|
6
|
+
ownerUrl: string;
|
|
7
|
+
runtimeUrl: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The owner and runtime addresses of one copy's Postgres, for `applyWorkerAppMigrations` and `deployWorkerApp`
|
|
11
|
+
* (`database.url = runtimeUrl`). Null when the values give neither address of that copy; a problem, in plain
|
|
12
|
+
* words that never repeat an address, when one is missing, is not a Postgres address, uses the owner's user,
|
|
13
|
+
* or reaches another database than the owner.
|
|
14
|
+
*/
|
|
15
|
+
export declare function databaseAddressesOf(values: ModuleValues, copy: DatabaseCopy): DatabaseAddresses | ModuleProblem | null;
|
|
16
|
+
/**
|
|
17
|
+
* A Postgres database for the app. A server app: `entry.database`. A Worker app: `workerApp.database`, reached
|
|
18
|
+
* through Hyperdrive, with its append-only tables and function grants. `migrationsSha256` is not a field: it
|
|
19
|
+
* comes from the build (`build.json`) and is kept as the entry holds it. A Worker app uses the company's own
|
|
20
|
+
* Postgres: an owner and an app address per copy, `secret` fields the caller holds and delivers itself. They
|
|
21
|
+
* never go into the entry, so `fromEntry` (and `modulesOf`) never gives them back.
|
|
22
|
+
*/
|
|
23
|
+
export declare const database: AppModule;
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { WORKER_APP_APPEND_ONLY_MAX, WORKER_APP_FUNCTION_SIGNATURE, WORKER_APP_GRANTS_MAX, WORKER_APP_TABLE_NAME, } from '../entry-rules.js';
|
|
2
|
+
import { clone, list, omit, problem, withWorkerApp } from './entry-parts.js';
|
|
3
|
+
const ADDRESS_HELP = 'From your Postgres provider. The owner can change tables; the app address is a separate user with less access. Use a different database for the workspace than for live.';
|
|
4
|
+
const ADDRESS_MAX = 2000;
|
|
5
|
+
const COPY_WORDS = { workspace: 'the workspace', live: 'live' };
|
|
6
|
+
const ADDRESS_KEYS = {
|
|
7
|
+
workspace: { owner: 'workspaceOwnerUrl', app: 'workspaceAppUrl' },
|
|
8
|
+
live: { owner: 'liveOwnerUrl', app: 'liveAppUrl' },
|
|
9
|
+
};
|
|
10
|
+
const addressLabel = (copy, role) => `Database address for ${COPY_WORDS[copy]} (${role})`;
|
|
11
|
+
const addressField = (copy, role) => ({
|
|
12
|
+
key: ADDRESS_KEYS[copy][role],
|
|
13
|
+
label: addressLabel(copy, role),
|
|
14
|
+
help: ADDRESS_HELP,
|
|
15
|
+
kind: 'secret',
|
|
16
|
+
required: true,
|
|
17
|
+
max: ADDRESS_MAX,
|
|
18
|
+
runtimes: ['worker'],
|
|
19
|
+
});
|
|
20
|
+
/** The user and the database (host, port, name) of a Postgres address, or null when it is not one. */
|
|
21
|
+
function parseAddress(value) {
|
|
22
|
+
let url;
|
|
23
|
+
try {
|
|
24
|
+
url = new URL(value);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
if (url.protocol !== 'postgres:' && url.protocol !== 'postgresql:')
|
|
30
|
+
return null;
|
|
31
|
+
const name = url.pathname.replace(/^\//, '');
|
|
32
|
+
if (url.username === '' || url.hostname === '' || name === '' || name.includes('/'))
|
|
33
|
+
return null;
|
|
34
|
+
let user;
|
|
35
|
+
try {
|
|
36
|
+
user = decodeURIComponent(url.username);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
return { user, place: `${url.hostname.toLowerCase()}:${url.port || '5432'}/${name}` };
|
|
42
|
+
}
|
|
43
|
+
/** One copy's addresses read and checked, with the database they reach; null when neither is given. */
|
|
44
|
+
function readCopy(values, copy) {
|
|
45
|
+
const keys = ADDRESS_KEYS[copy];
|
|
46
|
+
const owner = values[keys.owner];
|
|
47
|
+
const app = values[keys.app];
|
|
48
|
+
const given = (value) => typeof value === 'string' && value !== '';
|
|
49
|
+
if (!given(owner) && !given(app))
|
|
50
|
+
return null;
|
|
51
|
+
if (!given(owner)) {
|
|
52
|
+
return problem(keys.owner, `"${addressLabel(copy, 'owner')}" is needed with the app address.`);
|
|
53
|
+
}
|
|
54
|
+
if (!given(app)) {
|
|
55
|
+
return problem(keys.app, `"${addressLabel(copy, 'app')}" is needed with the owner address.`);
|
|
56
|
+
}
|
|
57
|
+
const shape = (role) => `"${addressLabel(copy, role)}" is not a Postgres address. It starts with postgres:// or postgresql:// and names a user, a server and a database. Copy it again from your Postgres provider.`;
|
|
58
|
+
const ownerParts = parseAddress(owner);
|
|
59
|
+
if (!ownerParts)
|
|
60
|
+
return problem(keys.owner, shape('owner'));
|
|
61
|
+
const appParts = parseAddress(app);
|
|
62
|
+
if (!appParts)
|
|
63
|
+
return problem(keys.app, shape('app'));
|
|
64
|
+
if (ownerParts.user === appParts.user) {
|
|
65
|
+
return problem(keys.app, `The owner and app addresses for ${COPY_WORDS[copy]} use the same user. The app needs a separate user with less access.`);
|
|
66
|
+
}
|
|
67
|
+
if (ownerParts.place !== appParts.place) {
|
|
68
|
+
return problem(keys.app, `The app address for ${COPY_WORDS[copy]} reaches another database than the owner address. Both must reach the same database.`);
|
|
69
|
+
}
|
|
70
|
+
return { addresses: { ownerUrl: owner, runtimeUrl: app }, place: ownerParts.place };
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The owner and runtime addresses of one copy's Postgres, for `applyWorkerAppMigrations` and `deployWorkerApp`
|
|
74
|
+
* (`database.url = runtimeUrl`). Null when the values give neither address of that copy; a problem, in plain
|
|
75
|
+
* words that never repeat an address, when one is missing, is not a Postgres address, uses the owner's user,
|
|
76
|
+
* or reaches another database than the owner.
|
|
77
|
+
*/
|
|
78
|
+
export function databaseAddressesOf(values, copy) {
|
|
79
|
+
const read = readCopy(values, copy);
|
|
80
|
+
if (read === null || 'message' in read)
|
|
81
|
+
return read;
|
|
82
|
+
return read.addresses;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* A Postgres database for the app. A server app: `entry.database`. A Worker app: `workerApp.database`, reached
|
|
86
|
+
* through Hyperdrive, with its append-only tables and function grants. `migrationsSha256` is not a field: it
|
|
87
|
+
* comes from the build (`build.json`) and is kept as the entry holds it. A Worker app uses the company's own
|
|
88
|
+
* Postgres: an owner and an app address per copy, `secret` fields the caller holds and delivers itself. They
|
|
89
|
+
* never go into the entry, so `fromEntry` (and `modulesOf`) never gives them back.
|
|
90
|
+
*/
|
|
91
|
+
export const database = {
|
|
92
|
+
view: {
|
|
93
|
+
id: 'database',
|
|
94
|
+
name: 'Database',
|
|
95
|
+
line: "A database for the app's own records, kept safe with daily backups.",
|
|
96
|
+
category: 'Data',
|
|
97
|
+
icon: 'database',
|
|
98
|
+
runtimes: ['server', 'worker'],
|
|
99
|
+
requires: [],
|
|
100
|
+
availability: 'ready',
|
|
101
|
+
fields: [
|
|
102
|
+
addressField('workspace', 'owner'),
|
|
103
|
+
addressField('workspace', 'app'),
|
|
104
|
+
addressField('live', 'owner'),
|
|
105
|
+
addressField('live', 'app'),
|
|
106
|
+
{
|
|
107
|
+
key: 'appendOnly',
|
|
108
|
+
label: 'Tables that keep every row',
|
|
109
|
+
help: 'The app can add and read rows in these tables but never change or delete them, as for an audit trail. Only for apps that run as a Worker app.',
|
|
110
|
+
kind: 'list',
|
|
111
|
+
advanced: true,
|
|
112
|
+
pattern: WORKER_APP_TABLE_NAME.source,
|
|
113
|
+
max: WORKER_APP_APPEND_ONLY_MAX,
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
key: 'grants',
|
|
117
|
+
label: 'Database functions the app may run',
|
|
118
|
+
help: 'Functions your database setup locks down, written as name(type, ...), such as audit_chain_tail(). Only for apps that run as a Worker app.',
|
|
119
|
+
kind: 'list',
|
|
120
|
+
advanced: true,
|
|
121
|
+
pattern: WORKER_APP_FUNCTION_SIGNATURE.source,
|
|
122
|
+
max: WORKER_APP_GRANTS_MAX,
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
adds: [
|
|
126
|
+
{
|
|
127
|
+
name: 'A Postgres database',
|
|
128
|
+
hint: 'Only this app reaches it. The app gets its address itself.',
|
|
129
|
+
},
|
|
130
|
+
{ name: 'Backups', hint: 'A copy is made every day.' },
|
|
131
|
+
],
|
|
132
|
+
},
|
|
133
|
+
check(values, _modules, ctx) {
|
|
134
|
+
if (ctx.runtime === 'server') {
|
|
135
|
+
for (const key of ['appendOnly', 'grants']) {
|
|
136
|
+
if (list(values, key) !== undefined) {
|
|
137
|
+
return problem(key, 'Tables that keep every row and database functions can only be set for an app that runs as a Worker app.');
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
const workspace = readCopy(values, 'workspace');
|
|
143
|
+
if (workspace && 'message' in workspace)
|
|
144
|
+
return workspace;
|
|
145
|
+
const live = readCopy(values, 'live');
|
|
146
|
+
if (live && 'message' in live)
|
|
147
|
+
return live;
|
|
148
|
+
if (workspace && live && workspace.place === live.place) {
|
|
149
|
+
return problem(ADDRESS_KEYS.workspace.owner, 'The workspace and live use the same database. Use a different database for the workspace than for live.');
|
|
150
|
+
}
|
|
151
|
+
return null;
|
|
152
|
+
},
|
|
153
|
+
toEntry(entry, values, ctx) {
|
|
154
|
+
if (ctx.runtime === 'server') {
|
|
155
|
+
const next = clone(entry);
|
|
156
|
+
next.database = values !== null;
|
|
157
|
+
return next;
|
|
158
|
+
}
|
|
159
|
+
return withWorkerApp(entry, (app) => {
|
|
160
|
+
if (values === null)
|
|
161
|
+
return omit(app, 'database');
|
|
162
|
+
const next = {};
|
|
163
|
+
const appendOnly = list(values, 'appendOnly');
|
|
164
|
+
const grants = list(values, 'grants');
|
|
165
|
+
if (appendOnly)
|
|
166
|
+
next.appendOnly = appendOnly;
|
|
167
|
+
if (grants)
|
|
168
|
+
next.grants = grants;
|
|
169
|
+
const pinned = app.database?.migrationsSha256;
|
|
170
|
+
if (pinned !== undefined)
|
|
171
|
+
next.migrationsSha256 = pinned;
|
|
172
|
+
return { ...app, database: next };
|
|
173
|
+
});
|
|
174
|
+
},
|
|
175
|
+
fromEntry(entry) {
|
|
176
|
+
if (entry.workerApp) {
|
|
177
|
+
const db = entry.workerApp.database;
|
|
178
|
+
if (!db)
|
|
179
|
+
return null;
|
|
180
|
+
const values = {};
|
|
181
|
+
if (db.appendOnly?.length)
|
|
182
|
+
values.appendOnly = [...db.appendOnly];
|
|
183
|
+
if (db.grants?.length)
|
|
184
|
+
values.grants = [...db.grants];
|
|
185
|
+
return values;
|
|
186
|
+
}
|
|
187
|
+
return entry.database ? {} : null;
|
|
188
|
+
},
|
|
189
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { setUse } from './entry-parts.js';
|
|
2
|
+
/** `uses.mail`: the app sends mail from the company's mail domain. */
|
|
3
|
+
export const email = {
|
|
4
|
+
view: {
|
|
5
|
+
id: 'email',
|
|
6
|
+
name: 'Email',
|
|
7
|
+
line: "The app sends email from your company's address, such as receipts and notices.",
|
|
8
|
+
category: 'Messages',
|
|
9
|
+
icon: 'mail',
|
|
10
|
+
runtimes: ['server'],
|
|
11
|
+
requires: [],
|
|
12
|
+
availability: 'ready',
|
|
13
|
+
fields: [],
|
|
14
|
+
adds: [{ name: 'Sending email', hint: "From your company's own mail address." }],
|
|
15
|
+
},
|
|
16
|
+
toEntry: (entry, values) => setUse(entry, 'mail', values === null ? undefined : true),
|
|
17
|
+
fromEntry: (entry) => (entry.uses?.mail === true ? {} : null),
|
|
18
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { EntryAuth, HostingEntry, HostingEntryUses } from '../org.js';
|
|
2
|
+
import type { WorkerAppConfig } from '../worker-app-config.js';
|
|
3
|
+
import type { ModuleProblem, ModuleValue, ModuleValues } from './types.js';
|
|
4
|
+
/** Helpers the modules share to change one part of an entry without touching the caller's copy. */
|
|
5
|
+
export declare const clone: <T>(value: T) => T;
|
|
6
|
+
/** A copy of `value` without `keys`. */
|
|
7
|
+
export declare function omit<T extends object, K extends keyof T>(value: T, ...keys: K[]): Omit<T, K>;
|
|
8
|
+
export declare const isProblem: (value: HostingEntry | ModuleProblem) => value is ModuleProblem;
|
|
9
|
+
/** The entry with `uses` replaced; an empty `uses` is left out. */
|
|
10
|
+
export declare function withUses(entry: HostingEntry, uses: HostingEntryUses): HostingEntry;
|
|
11
|
+
/** The entry with `uses[key]` set, or removed when `value` is undefined. */
|
|
12
|
+
export declare function setUse<K extends keyof HostingEntryUses>(entry: HostingEntry, key: K, value: HostingEntryUses[K] | undefined): HostingEntry;
|
|
13
|
+
/** The entry with `uses.auth[key]` changed. Without `uses.auth` the entry comes back unchanged. */
|
|
14
|
+
export declare function setAuthPart<K extends keyof EntryAuth>(entry: HostingEntry, key: K, value: EntryAuth[K] | undefined): HostingEntry;
|
|
15
|
+
/** The entry with its `workerApp` replaced by `change`'s result. Without a `workerApp` the entry comes back unchanged. */
|
|
16
|
+
export declare function withWorkerApp(entry: HostingEntry, change: (app: WorkerAppConfig) => WorkerAppConfig): HostingEntry;
|
|
17
|
+
/** A text value, or undefined for an absent or empty one. */
|
|
18
|
+
export declare function text(values: ModuleValues, key: string): string | undefined;
|
|
19
|
+
/** A list value, or undefined for an absent or empty one. */
|
|
20
|
+
export declare function list(values: ModuleValues, key: string): string[] | undefined;
|
|
21
|
+
/** `values` without the keys whose value is undefined. */
|
|
22
|
+
export declare function defined(values: Record<string, ModuleValue | undefined>): ModuleValues;
|
|
23
|
+
export declare const problem: (field: string | null, message: string) => ModuleProblem;
|