@bevel-software/platform-core-backend 0.20.0 → 0.21.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/core/catalog-cache-invalidation.d.ts +7 -2
- package/dist/core/catalog-cache-invalidation.d.ts.map +1 -1
- package/dist/core/catalog-cache-invalidation.js +8 -9
- package/dist/core/catalog-cache-invalidation.js.map +1 -1
- package/dist/core/core-ports.d.ts +19 -0
- package/dist/core/core-ports.d.ts.map +1 -1
- package/dist/core/core-ports.js.map +1 -1
- package/dist/core/create-core-server.d.ts +2 -0
- package/dist/core/create-core-server.d.ts.map +1 -1
- package/dist/core/create-core-server.js +23 -105
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +30 -5
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +136 -81
- package/dist/core/create-core-services.js.map +1 -1
- package/dist/core/lifecycle.d.ts +74 -28
- package/dist/core/lifecycle.d.ts.map +1 -1
- package/dist/core/lifecycle.js +157 -20
- package/dist/core/lifecycle.js.map +1 -1
- package/dist/core/public-config.d.ts +5 -1
- package/dist/core/public-config.d.ts.map +1 -1
- package/dist/core/public-config.js +7 -5
- package/dist/core/public-config.js.map +1 -1
- package/dist/core-config.d.ts +95 -1
- package/dist/core-config.d.ts.map +1 -1
- package/dist/core-config.js +24 -8
- package/dist/core-config.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/dist/modules/access/access-control.service.d.ts +18 -1
- package/dist/modules/access/access-control.service.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.js +19 -8
- package/dist/modules/access/access-control.service.js.map +1 -1
- package/dist/modules/access/access.routes.d.ts +2 -1
- package/dist/modules/access/access.routes.d.ts.map +1 -1
- package/dist/modules/access/access.routes.js +10 -10
- package/dist/modules/access/access.routes.js.map +1 -1
- package/dist/modules/access/change-read-gate.d.ts +6 -3
- package/dist/modules/access/change-read-gate.d.ts.map +1 -1
- package/dist/modules/access/change-read-gate.js +9 -6
- package/dist/modules/access/change-read-gate.js.map +1 -1
- package/dist/modules/access/creator-access.d.ts +4 -2
- package/dist/modules/access/creator-access.d.ts.map +1 -1
- package/dist/modules/access/creator-access.js +7 -4
- package/dist/modules/access/creator-access.js.map +1 -1
- package/dist/modules/agent-instructions/read-preamble.d.ts +2 -1
- package/dist/modules/agent-instructions/read-preamble.d.ts.map +1 -1
- package/dist/modules/agent-instructions/read-preamble.js +6 -9
- package/dist/modules/agent-instructions/read-preamble.js.map +1 -1
- package/dist/modules/auth/account-admission.d.ts +40 -0
- package/dist/modules/auth/account-admission.d.ts.map +1 -0
- package/dist/modules/auth/account-admission.js +17 -0
- package/dist/modules/auth/account-admission.js.map +1 -0
- package/dist/modules/auth/account.routes.d.ts.map +1 -1
- package/dist/modules/auth/account.routes.js +4 -1
- package/dist/modules/auth/account.routes.js.map +1 -1
- package/dist/modules/auth/auth.service.d.ts +19 -1
- package/dist/modules/auth/auth.service.d.ts.map +1 -1
- package/dist/modules/auth/auth.service.js +33 -1
- package/dist/modules/auth/auth.service.js.map +1 -1
- package/dist/modules/auth/oidc-auth-provider.d.ts.map +1 -1
- package/dist/modules/auth/oidc-auth-provider.js +9 -0
- package/dist/modules/auth/oidc-auth-provider.js.map +1 -1
- package/dist/modules/connection-probe/connection-probe.service.d.ts +13 -1
- package/dist/modules/connection-probe/connection-probe.service.d.ts.map +1 -1
- package/dist/modules/connection-probe/connection-probe.service.js +10 -2
- package/dist/modules/connection-probe/connection-probe.service.js.map +1 -1
- package/dist/modules/database/advisory-lock.d.ts +17 -0
- package/dist/modules/database/advisory-lock.d.ts.map +1 -1
- package/dist/modules/database/advisory-lock.js +24 -3
- package/dist/modules/database/advisory-lock.js.map +1 -1
- package/dist/modules/database/connection.d.ts +67 -2
- package/dist/modules/database/connection.d.ts.map +1 -1
- package/dist/modules/database/connection.js +118 -12
- package/dist/modules/database/connection.js.map +1 -1
- package/dist/modules/database/migrate.d.ts +1 -1
- package/dist/modules/database/migrate.d.ts.map +1 -1
- package/dist/modules/database/migrate.js +27 -3
- package/dist/modules/database/migrate.js.map +1 -1
- package/dist/modules/kb-fs/branch-name.d.ts +0 -1
- package/dist/modules/kb-fs/branch-name.d.ts.map +1 -1
- package/dist/modules/kb-fs/branch-name.js +3 -3
- package/dist/modules/kb-fs/branch-name.js.map +1 -1
- package/dist/modules/kb-fs/clone-config.d.ts +9 -8
- package/dist/modules/kb-fs/clone-config.d.ts.map +1 -1
- package/dist/modules/kb-fs/clone-config.js +14 -12
- package/dist/modules/kb-fs/clone-config.js.map +1 -1
- package/dist/modules/mcp/mcp.service.d.ts +7 -0
- package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.service.js +4 -4
- package/dist/modules/mcp/mcp.service.js.map +1 -1
- package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts +4 -4
- package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts.map +1 -1
- package/dist/modules/plugins/compile/marketplace-compiler.service.js +7 -10
- package/dist/modules/plugins/compile/marketplace-compiler.service.js.map +1 -1
- package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts +4 -1
- package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts.map +1 -1
- package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js +5 -3
- package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js.map +1 -1
- package/dist/modules/plugins/discovery/kb-plugin-source.d.ts +6 -1
- package/dist/modules/plugins/discovery/kb-plugin-source.d.ts.map +1 -1
- package/dist/modules/plugins/discovery/kb-plugin-source.js +14 -9
- package/dist/modules/plugins/discovery/kb-plugin-source.js.map +1 -1
- package/dist/modules/plugins/discovery/native.source.d.ts +4 -1
- package/dist/modules/plugins/discovery/native.source.d.ts.map +1 -1
- package/dist/modules/plugins/discovery/native.source.js +4 -2
- package/dist/modules/plugins/discovery/native.source.js.map +1 -1
- package/dist/modules/plugins/index.d.ts +1 -1
- package/dist/modules/plugins/index.d.ts.map +1 -1
- package/dist/modules/plugins/index.js +1 -1
- package/dist/modules/plugins/index.js.map +1 -1
- package/dist/modules/plugins/join-request-jobs.service.d.ts +3 -1
- package/dist/modules/plugins/join-request-jobs.service.d.ts.map +1 -1
- package/dist/modules/plugins/join-request-jobs.service.js +6 -6
- package/dist/modules/plugins/join-request-jobs.service.js.map +1 -1
- package/dist/modules/plugins/join-requests.service.d.ts +3 -1
- package/dist/modules/plugins/join-requests.service.d.ts.map +1 -1
- package/dist/modules/plugins/join-requests.service.js +9 -8
- package/dist/modules/plugins/join-requests.service.js.map +1 -1
- package/dist/modules/plugins/plugin-links.d.ts +65 -10
- package/dist/modules/plugins/plugin-links.d.ts.map +1 -1
- package/dist/modules/plugins/plugin-links.js +136 -31
- package/dist/modules/plugins/plugin-links.js.map +1 -1
- package/dist/modules/plugins/plugin-links.service.d.ts +107 -5
- package/dist/modules/plugins/plugin-links.service.d.ts.map +1 -1
- package/dist/modules/plugins/plugin-links.service.js +151 -21
- package/dist/modules/plugins/plugin-links.service.js.map +1 -1
- package/dist/modules/plugins/plugin-provision.service.d.ts +10 -3
- package/dist/modules/plugins/plugin-provision.service.d.ts.map +1 -1
- package/dist/modules/plugins/plugin-provision.service.js +33 -26
- package/dist/modules/plugins/plugin-provision.service.js.map +1 -1
- package/dist/modules/plugins/plugin-rename.service.d.ts +4 -2
- package/dist/modules/plugins/plugin-rename.service.d.ts.map +1 -1
- package/dist/modules/plugins/plugin-rename.service.js +10 -12
- package/dist/modules/plugins/plugin-rename.service.js.map +1 -1
- package/dist/modules/plugins/plugins.routes.d.ts +3 -0
- package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
- package/dist/modules/plugins/plugins.routes.js +45 -15
- package/dist/modules/plugins/plugins.routes.js.map +1 -1
- package/dist/modules/plugins/plugins.service.d.ts +4 -4
- package/dist/modules/plugins/plugins.service.d.ts.map +1 -1
- package/dist/modules/plugins/plugins.service.js +7 -10
- package/dist/modules/plugins/plugins.service.js.map +1 -1
- package/dist/modules/plugins/teams.routes.d.ts +4 -1
- package/dist/modules/plugins/teams.routes.d.ts.map +1 -1
- package/dist/modules/plugins/teams.routes.js +11 -9
- package/dist/modules/plugins/teams.routes.js.map +1 -1
- package/dist/modules/secrets-vault/index.d.ts +1 -1
- package/dist/modules/secrets-vault/index.d.ts.map +1 -1
- package/dist/modules/secrets-vault/index.js +1 -1
- package/dist/modules/secrets-vault/index.js.map +1 -1
- package/dist/modules/secrets-vault/secrets-variable-loader.d.ts +29 -11
- package/dist/modules/secrets-vault/secrets-variable-loader.d.ts.map +1 -1
- package/dist/modules/secrets-vault/secrets-variable-loader.js +46 -21
- package/dist/modules/secrets-vault/secrets-variable-loader.js.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.routes.d.ts +3 -0
- package/dist/modules/secrets-vault/secrets-vault.routes.d.ts.map +1 -1
- package/dist/modules/secrets-vault/secrets-vault.routes.js +4 -6
- package/dist/modules/secrets-vault/secrets-vault.routes.js.map +1 -1
- package/dist/modules/settings/deployment-settings.service.d.ts +12 -8
- package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
- package/dist/modules/settings/deployment-settings.service.js +20 -22
- package/dist/modules/settings/deployment-settings.service.js.map +1 -1
- package/dist/modules/settings/setup.routes.d.ts +9 -2
- package/dist/modules/settings/setup.routes.d.ts.map +1 -1
- package/dist/modules/settings/setup.routes.js +20 -14
- package/dist/modules/settings/setup.routes.js.map +1 -1
- package/dist/modules/skills/allowed-tools-check.d.ts +4 -3
- package/dist/modules/skills/allowed-tools-check.d.ts.map +1 -1
- package/dist/modules/skills/allowed-tools-check.js +8 -8
- package/dist/modules/skills/allowed-tools-check.js.map +1 -1
- package/dist/modules/skills/pending-skills.service.d.ts +3 -1
- package/dist/modules/skills/pending-skills.service.d.ts.map +1 -1
- package/dist/modules/skills/pending-skills.service.js +9 -6
- package/dist/modules/skills/pending-skills.service.js.map +1 -1
- package/dist/modules/skills/skill-access-requests.routes.d.ts +2 -1
- package/dist/modules/skills/skill-access-requests.routes.d.ts.map +1 -1
- package/dist/modules/skills/skill-access-requests.routes.js +6 -6
- package/dist/modules/skills/skill-access-requests.routes.js.map +1 -1
- package/dist/modules/skills/skills.routes.d.ts +4 -1
- package/dist/modules/skills/skills.routes.d.ts.map +1 -1
- package/dist/modules/skills/skills.routes.js +4 -4
- package/dist/modules/skills/skills.routes.js.map +1 -1
- package/dist/modules/skills/skills.service.d.ts +4 -2
- package/dist/modules/skills/skills.service.d.ts.map +1 -1
- package/dist/modules/skills/skills.service.js +13 -11
- package/dist/modules/skills/skills.service.js.map +1 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.d.ts +2 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.d.ts.map +1 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.js +6 -5
- package/dist/modules/tool-manuals/mcp-json-discovery.js.map +1 -1
- package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts +4 -2
- package/dist/modules/tool-manuals/mcp-server-edit.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/mcp-server-edit.service.js +12 -10
- package/dist/modules/tool-manuals/mcp-server-edit.service.js.map +1 -1
- package/dist/modules/tool-manuals/pending-tools.service.d.ts +3 -1
- package/dist/modules/tool-manuals/pending-tools.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/pending-tools.service.js +19 -16
- package/dist/modules/tool-manuals/pending-tools.service.js.map +1 -1
- package/dist/modules/tool-manuals/tool-delete.service.d.ts +4 -2
- package/dist/modules/tool-manuals/tool-delete.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-delete.service.js +14 -12
- package/dist/modules/tool-manuals/tool-delete.service.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.routes.d.ts +3 -1
- package/dist/modules/tool-manuals/tool-manuals.routes.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.routes.js +7 -7
- package/dist/modules/tool-manuals/tool-manuals.routes.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts +4 -2
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.js +15 -12
- package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.d.ts +3 -0
- package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.js +4 -6
- package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts +3 -2
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.js +9 -9
- package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
- package/dist/modules/workflow/git/git.service.d.ts +11 -5
- package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/git.service.js +23 -14
- package/dist/modules/workflow/git/git.service.js.map +1 -1
- package/dist/modules/workflow/git/node-git-runner.d.ts +26 -2
- package/dist/modules/workflow/git/node-git-runner.d.ts.map +1 -1
- package/dist/modules/workflow/git/node-git-runner.js +35 -5
- package/dist/modules/workflow/git/node-git-runner.js.map +1 -1
- package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/review-workflow/review-workflow.service.js +9 -4
- package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -1
- package/dist/modules/workflow/session-ontology.service.d.ts +6 -1
- package/dist/modules/workflow/session-ontology.service.d.ts.map +1 -1
- package/dist/modules/workflow/session-ontology.service.js +6 -2
- package/dist/modules/workflow/session-ontology.service.js.map +1 -1
- package/dist/modules/workflow/workflow.service.d.ts +8 -3
- package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.service.js +33 -23
- package/dist/modules/workflow/workflow.service.js.map +1 -1
- package/dist/modules/workspace/session-ontology.gate.d.ts +3 -1
- package/dist/modules/workspace/session-ontology.gate.d.ts.map +1 -1
- package/dist/modules/workspace/session-ontology.gate.js +3 -3
- package/dist/modules/workspace/session-ontology.gate.js.map +1 -1
- package/dist/modules/workspace/startup/kb-git.d.ts +4 -3
- package/dist/modules/workspace/startup/kb-git.d.ts.map +1 -1
- package/dist/modules/workspace/startup/kb-git.js +15 -13
- package/dist/modules/workspace/startup/kb-git.js.map +1 -1
- package/dist/modules/workspace/startup/kb-startup-runner.d.ts +5 -8
- package/dist/modules/workspace/startup/kb-startup-runner.d.ts.map +1 -1
- package/dist/modules/workspace/startup/kb-startup-runner.js +35 -38
- package/dist/modules/workspace/startup/kb-startup-runner.js.map +1 -1
- package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts +7 -1
- package/dist/modules/workspace/startup/steps/groups-to-plugins.step.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js +23 -16
- package/dist/modules/workspace/startup/steps/groups-to-plugins.step.js.map +1 -1
- package/dist/modules/workspace/startup/steps/personal-spaces.step.d.ts +7 -1
- package/dist/modules/workspace/startup/steps/personal-spaces.step.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/personal-spaces.step.js +12 -5
- package/dist/modules/workspace/startup/steps/personal-spaces.step.js.map +1 -1
- package/dist/modules/workspace/startup/steps/plugin-display-names.step.d.ts +7 -1
- package/dist/modules/workspace/startup/steps/plugin-display-names.step.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/plugin-display-names.step.js +12 -5
- package/dist/modules/workspace/startup/steps/plugin-display-names.step.js.map +1 -1
- package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts +7 -1
- package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/plugin-manifests.step.js +12 -5
- package/dist/modules/workspace/startup/steps/plugin-manifests.step.js.map +1 -1
- package/dist/modules/workspace/startup/steps/seed-tree.d.ts +4 -1
- package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/seed-tree.js +14 -11
- package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
- package/dist/modules/workspace/startup/steps/template-files.step.d.ts +17 -3
- package/dist/modules/workspace/startup/steps/template-files.step.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/template-files.step.js +36 -30
- package/dist/modules/workspace/startup/steps/template-files.step.js.map +1 -1
- package/dist/modules/workspace/startup/steps/template-source.d.ts +6 -1
- package/dist/modules/workspace/startup/steps/template-source.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/template-source.js +10 -6
- package/dist/modules/workspace/startup/steps/template-source.js.map +1 -1
- package/dist/modules/workspace/workspace.routes.d.ts +2 -1
- package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.routes.js +8 -7
- package/dist/modules/workspace/workspace.routes.js.map +1 -1
- package/dist/modules/workspace/workspace.service.d.ts +18 -12
- package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.service.js +32 -28
- package/dist/modules/workspace/workspace.service.js.map +1 -1
- package/dist/modules/workspace/workspace.tools.d.ts +2 -1
- package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.tools.js +20 -18
- package/dist/modules/workspace/workspace.tools.js.map +1 -1
- package/dist/modules/workspace/write-denial.d.ts +3 -2
- package/dist/modules/workspace/write-denial.d.ts.map +1 -1
- package/dist/modules/workspace/write-denial.js +6 -5
- package/dist/modules/workspace/write-denial.js.map +1 -1
- package/dist/shared/git.contract.d.ts +52 -7
- package/dist/shared/git.contract.d.ts.map +1 -1
- package/dist/shared/git.contract.js +30 -8
- package/dist/shared/git.contract.js.map +1 -1
- package/dist/shared/kb-context.d.ts +76 -0
- package/dist/shared/kb-context.d.ts.map +1 -0
- package/dist/shared/kb-context.js +108 -0
- package/dist/shared/kb-context.js.map +1 -0
- package/dist/shared/kb-layout.d.ts +3 -2
- package/dist/shared/kb-layout.d.ts.map +1 -1
- package/dist/shared/kb-layout.js +4 -3
- package/dist/shared/kb-layout.js.map +1 -1
- package/dist/shared/kb-layout.test.js +35 -27
- package/dist/shared/kb-layout.test.js.map +1 -1
- package/dist/shared/pending-proposals.d.ts +4 -1
- package/dist/shared/pending-proposals.d.ts.map +1 -1
- package/dist/shared/pending-proposals.js +3 -4
- package/dist/shared/pending-proposals.js.map +1 -1
- package/dist/shared/redact-secret.d.ts +5 -5
- package/dist/shared/redact-secret.js +5 -5
- package/dist/tenancy/index.d.ts +13 -0
- package/dist/tenancy/index.d.ts.map +1 -0
- package/dist/tenancy/index.js +13 -0
- package/dist/tenancy/index.js.map +1 -0
- package/dist/tenancy/static-tenant-source.d.ts +98 -0
- package/dist/tenancy/static-tenant-source.d.ts.map +1 -0
- package/dist/tenancy/static-tenant-source.js +220 -0
- package/dist/tenancy/static-tenant-source.js.map +1 -0
- package/dist/tenancy/tenant-host.d.ts +80 -0
- package/dist/tenancy/tenant-host.d.ts.map +1 -0
- package/dist/tenancy/tenant-host.js +217 -0
- package/dist/tenancy/tenant-host.js.map +1 -0
- package/dist/tenancy/tenant-runtime.d.ts +91 -0
- package/dist/tenancy/tenant-runtime.d.ts.map +1 -0
- package/dist/tenancy/tenant-runtime.js +174 -0
- package/dist/tenancy/tenant-runtime.js.map +1 -0
- package/dist/tenancy/tenant-secrets.d.ts +24 -0
- package/dist/tenancy/tenant-secrets.d.ts.map +1 -0
- package/dist/tenancy/tenant-secrets.js +30 -0
- package/dist/tenancy/tenant-secrets.js.map +1 -0
- package/dist/tenancy/tenant-source.contract.d.ts +45 -0
- package/dist/tenancy/tenant-source.contract.d.ts.map +1 -0
- package/dist/tenancy/tenant-source.contract.js +21 -0
- package/dist/tenancy/tenant-source.contract.js.map +1 -0
- package/migrations/0000_core_init.sql +14 -14
- package/migrations/0003_deployment_settings.sql +1 -1
- package/migrations/0005_github_facade.sql +1 -1
- package/migrations/0012_agent_audit.sql +6 -6
- package/package.json +3 -3
- package/src/__tests__/branch-model.test.ts +7 -6
- package/src/__tests__/kb-context.ts +30 -0
- package/src/__tests__/kb-layout-config.test.ts +35 -30
- package/src/core/__tests__/catalog-cache-invalidation.test.ts +2 -1
- package/src/core/__tests__/lifecycle.test.ts +78 -1
- package/src/core/__tests__/public-config.test.ts +5 -1
- package/src/core/catalog-cache-invalidation.ts +16 -11
- package/src/core/core-ports.ts +19 -0
- package/src/core/create-core-server.ts +27 -106
- package/src/core/create-core-services.ts +180 -95
- package/src/core/lifecycle.ts +214 -29
- package/src/core/public-config.ts +8 -5
- package/src/core-config.ts +107 -8
- package/src/index.ts +25 -2
- package/src/modules/access/__tests__/access-control.admin-root-floor.test.ts +2 -1
- package/src/modules/access/__tests__/access.routes.batch.test.ts +2 -1
- package/src/modules/access/__tests__/access.routes.folder-governs.test.ts +2 -1
- package/src/modules/access/__tests__/access.routes.group-grant.test.ts +2 -1
- package/src/modules/access/__tests__/access.routes.overrides.test.ts +2 -1
- package/src/modules/access/__tests__/access.routes.proposal-branch.test.ts +2 -1
- package/src/modules/access/__tests__/access.routes.prospective.test.ts +2 -1
- package/src/modules/access/__tests__/access.routes.revoke.test.ts +2 -1
- package/src/modules/access/__tests__/access.routes.unknown-email.test.ts +2 -1
- package/src/modules/access/__tests__/change-read-gate.test.ts +13 -11
- package/src/modules/access/__tests__/creator-access.test.ts +3 -2
- package/src/modules/access/__tests__/roles.routes.test.ts +2 -1
- package/src/modules/access/access-control.service.ts +20 -6
- package/src/modules/access/access.routes.ts +10 -13
- package/src/modules/access/change-read-gate.ts +10 -4
- package/src/modules/access/creator-access.ts +7 -2
- package/src/modules/agent-instructions/__tests__/read-preamble.test.ts +13 -12
- package/src/modules/agent-instructions/read-preamble.ts +10 -12
- package/src/modules/auth/__tests__/account.routes.test.ts +15 -0
- package/src/modules/auth/__tests__/auth.service.test.ts +60 -0
- package/src/modules/auth/account-admission.ts +44 -0
- package/src/modules/auth/account.routes.ts +4 -1
- package/src/modules/auth/auth.service.ts +33 -1
- package/src/modules/auth/oidc-auth-provider.ts +9 -0
- package/src/modules/connection-probe/connection-probe.service.ts +7 -1
- package/src/modules/database/__tests__/advisory-lock.test.ts +23 -1
- package/src/modules/database/__tests__/connection.test.ts +98 -0
- package/src/modules/database/__tests__/migrations-unqualified.test.ts +32 -0
- package/src/modules/database/advisory-lock.ts +28 -4
- package/src/modules/database/connection.ts +143 -13
- package/src/modules/database/migrate.ts +48 -14
- package/src/modules/diff/__tests__/diff.service.seed-atomicity.test.ts +2 -1
- package/src/modules/diff/__tests__/diff.service.test.ts +2 -1
- package/src/modules/kb-fs/__tests__/branch-name.test.ts +6 -6
- package/src/modules/kb-fs/__tests__/clone-config.test.ts +24 -20
- package/src/modules/kb-fs/branch-name.ts +3 -3
- package/src/modules/kb-fs/clone-config.ts +15 -12
- package/src/modules/marketplace/__tests__/marketplace-repo.head.test.ts +2 -1
- package/src/modules/mcp/mcp.service.ts +11 -4
- package/src/modules/plugins/__tests__/bundle-dialect.e2e.test.ts +12 -11
- package/src/modules/plugins/__tests__/bundle-dialect.test.ts +2 -1
- package/src/modules/plugins/__tests__/compile-marketplace.test.ts +6 -4
- package/src/modules/plugins/__tests__/join-request-jobs.service.test.ts +2 -1
- package/src/modules/plugins/__tests__/join-requests.service.test.ts +2 -1
- package/src/modules/plugins/__tests__/plugin-creation-display-name.test.ts +4 -3
- package/src/modules/plugins/__tests__/plugin-index.service.test.ts +6 -4
- package/src/modules/plugins/__tests__/plugin-links.service.test.ts +220 -5
- package/src/modules/plugins/__tests__/plugin-provision.service.test.ts +3 -1
- package/src/modules/plugins/__tests__/plugin-rename.service.test.ts +7 -6
- package/src/modules/plugins/__tests__/plugins.routes.test.ts +11 -2
- package/src/modules/plugins/__tests__/teams.routes.test.ts +2 -1
- package/src/modules/plugins/compile/marketplace-compiler.service.ts +7 -9
- package/src/modules/plugins/discovery/__tests__/kb-plugin-source.test.ts +20 -16
- package/src/modules/plugins/discovery/bundle-dialect/bundle.source.ts +9 -2
- package/src/modules/plugins/discovery/kb-plugin-source.ts +15 -8
- package/src/modules/plugins/discovery/native.source.ts +4 -1
- package/src/modules/plugins/index.ts +1 -1
- package/src/modules/plugins/join-request-jobs.service.ts +8 -7
- package/src/modules/plugins/join-requests.service.ts +7 -7
- package/src/modules/plugins/plugin-links.service.ts +192 -19
- package/src/modules/plugins/plugin-links.ts +147 -27
- package/src/modules/plugins/plugin-provision.service.ts +54 -27
- package/src/modules/plugins/plugin-rename.service.ts +10 -10
- package/src/modules/plugins/plugins.routes.ts +45 -15
- package/src/modules/plugins/plugins.service.ts +7 -11
- package/src/modules/plugins/teams.routes.ts +16 -8
- package/src/modules/secrets-vault/__tests__/connect-pending.route.test.ts +7 -0
- package/src/modules/secrets-vault/__tests__/secrets-variable-loader.test.ts +40 -0
- package/src/modules/secrets-vault/__tests__/tool-owner-gate.route.test.ts +2 -0
- package/src/modules/secrets-vault/index.ts +2 -0
- package/src/modules/secrets-vault/secrets-variable-loader.ts +51 -21
- package/src/modules/secrets-vault/secrets-vault.routes.ts +7 -6
- package/src/modules/settings/__tests__/deployment-settings.service.test.ts +24 -11
- package/src/modules/settings/__tests__/git-root-folders.test.ts +2 -1
- package/src/modules/settings/__tests__/setup.routes.boot-failure.test.ts +2 -1
- package/src/modules/settings/__tests__/setup.routes.layout-phase.test.ts +7 -4
- package/src/modules/settings/__tests__/setup.routes.oidc.test.ts +2 -0
- package/src/modules/settings/__tests__/setup.routes.sync-status.test.ts +2 -0
- package/src/modules/settings/__tests__/setup.routes.test.ts +33 -52
- package/src/modules/settings/deployment-settings.service.ts +21 -20
- package/src/modules/settings/setup.routes.ts +26 -16
- package/src/modules/skills/__tests__/allowed-tools-check.test.ts +11 -10
- package/src/modules/skills/__tests__/allowed-tools-warn.tools.test.ts +4 -3
- package/src/modules/skills/__tests__/pending-skills.service.test.ts +3 -1
- package/src/modules/skills/__tests__/skill-access-requests.routes.test.ts +2 -1
- package/src/modules/skills/__tests__/skills.routes.warnings.test.ts +2 -1
- package/src/modules/skills/__tests__/skills.service.test.ts +3 -2
- package/src/modules/skills/allowed-tools-check.ts +7 -6
- package/src/modules/skills/pending-skills.service.ts +9 -8
- package/src/modules/skills/skill-access-requests.routes.ts +7 -7
- package/src/modules/skills/skills.routes.ts +4 -3
- package/src/modules/skills/skills.service.ts +13 -9
- package/src/modules/tool-helpers/__tests__/agent-roles-write.test.ts +3 -2
- package/src/modules/tool-helpers/__tests__/phase4-tools.test.ts +3 -2
- package/src/modules/tool-manuals/__tests__/mcp-json-discovery.test.ts +17 -12
- package/src/modules/tool-manuals/__tests__/mcp-server-edit.service.test.ts +2 -1
- package/src/modules/tool-manuals/__tests__/pending-tools.service.test.ts +4 -1
- package/src/modules/tool-manuals/__tests__/tool-delete.service.test.ts +2 -1
- package/src/modules/tool-manuals/__tests__/tool-manuals.archive.route.test.ts +2 -1
- package/src/modules/tool-manuals/__tests__/tool-manuals.cli.test.ts +5 -4
- package/src/modules/tool-manuals/__tests__/tool-manuals.detail.route.test.ts +2 -1
- package/src/modules/tool-manuals/__tests__/tool-manuals.invalid.routes.test.ts +4 -2
- package/src/modules/tool-manuals/__tests__/tool-manuals.invalid.test.ts +13 -5
- package/src/modules/tool-manuals/__tests__/tool-manuals.mcp-oauth.test.ts +2 -1
- package/src/modules/tool-manuals/__tests__/tool-manuals.restart.test.ts +9 -1
- package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +3 -2
- package/src/modules/tool-manuals/__tests__/tool-manuals.tools.test.ts +2 -0
- package/src/modules/tool-manuals/mcp-json-discovery.ts +6 -4
- package/src/modules/tool-manuals/mcp-server-edit.service.ts +13 -11
- package/src/modules/tool-manuals/pending-tools.service.ts +19 -16
- package/src/modules/tool-manuals/tool-delete.service.ts +13 -10
- package/src/modules/tool-manuals/tool-manuals.routes.ts +10 -8
- package/src/modules/tool-manuals/tool-manuals.service.ts +15 -13
- package/src/modules/tool-manuals/tool-manuals.tools.ts +7 -6
- package/src/modules/workflow/__tests__/apply-failure.test.ts +2 -1
- package/src/modules/workflow/__tests__/merge-announces-tree.test.ts +14 -26
- package/src/modules/workflow/__tests__/preserve-roles-yaml.test.ts +2 -1
- package/src/modules/workflow/__tests__/session-ontology.service.test.ts +3 -2
- package/src/modules/workflow/__tests__/workflow.routes.lock-path.test.ts +2 -1
- package/src/modules/workflow/__tests__/workflow.service.branch-in-use.test.ts +3 -10
- package/src/modules/workflow/__tests__/workflow.service.commitFileWhileLocked.test.ts +2 -1
- package/src/modules/workflow/__tests__/workflow.service.deleted-branch-sweep.test.ts +3 -10
- package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +28 -27
- package/src/modules/workflow/__tests__/workflow.service.folder-proposals.test.ts +2 -1
- package/src/modules/workflow/__tests__/workflow.service.platform-restore.test.ts +2 -1
- package/src/modules/workflow/__tests__/workflow.service.read-gate.test.ts +3 -2
- package/src/modules/workflow/__tests__/workflow.service.releaseLock.test.ts +2 -1
- package/src/modules/workflow/__tests__/workflow.service.sync.test.ts +5 -4
- package/src/modules/workflow/__tests__/workflow.service.update-from-target.test.ts +2 -1
- package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +2 -1
- package/src/modules/workflow/agent-tools/workflow.tools.ts +11 -9
- package/src/modules/workflow/git/__tests__/git.service.accessGating.test.ts +5 -4
- package/src/modules/workflow/git/__tests__/git.service.changedFilesForPr.test.ts +12 -11
- package/src/modules/workflow/git/__tests__/git.service.commitChanges.test.ts +9 -8
- package/src/modules/workflow/git/__tests__/git.service.commitFile.strayPath.test.ts +8 -7
- package/src/modules/workflow/git/__tests__/git.service.commitFile.test.ts +6 -5
- package/src/modules/workflow/git/__tests__/git.service.createBranch.test.ts +3 -2
- package/src/modules/workflow/git/__tests__/git.service.deleteBranch.test.ts +3 -2
- package/src/modules/workflow/git/__tests__/git.service.diffFileAtCommit.test.ts +7 -6
- package/src/modules/workflow/git/__tests__/git.service.diffFileBetweenBranches.test.ts +16 -15
- package/src/modules/workflow/git/__tests__/git.service.fileContentsAtCommit.test.ts +3 -2
- package/src/modules/workflow/git/__tests__/git.service.forkPoint.test.ts +2 -1
- package/src/modules/workflow/git/__tests__/git.service.history-guards.test.ts +2 -1
- package/src/modules/workflow/git/__tests__/git.service.list-branches.test.ts +10 -9
- package/src/modules/workflow/git/__tests__/git.service.listTrackedFiles.test.ts +2 -1
- package/src/modules/workflow/git/__tests__/git.service.logForFile.test.ts +6 -5
- package/src/modules/workflow/git/__tests__/git.service.mergeChangeRequest.test.ts +9 -8
- package/src/modules/workflow/git/__tests__/git.service.pendingChanges.test.ts +2 -1
- package/src/modules/workflow/git/__tests__/git.service.postMergePull.test.ts +4 -3
- package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +9 -8
- package/src/modules/workflow/git/__tests__/git.service.readFileAtRef.test.ts +2 -1
- package/src/modules/workflow/git/__tests__/git.service.revertPathsAndPush.test.ts +2 -1
- package/src/modules/workflow/git/__tests__/git.service.status.test.ts +4 -3
- package/src/modules/workflow/git/__tests__/git.service.syncFromRemote.test.ts +8 -7
- package/src/modules/workflow/git/__tests__/node-git-runner.test.ts +41 -3
- package/src/modules/workflow/git/git.service.ts +24 -13
- package/src/modules/workflow/git/node-git-runner.ts +37 -4
- package/src/modules/workflow/review-workflow/review-workflow.service.ts +9 -6
- package/src/modules/workflow/session-ontology.service.ts +7 -2
- package/src/modules/workflow/workflow.service.ts +31 -24
- package/src/modules/workspace/__tests__/file-stat-access.test.ts +3 -2
- package/src/modules/workspace/__tests__/git-internals.security.test.ts +10 -9
- package/src/modules/workspace/__tests__/session-ontology.gate.test.ts +4 -2
- package/src/modules/workspace/__tests__/workspace-paths-inside-the-repo.test.ts +3 -2
- package/src/modules/workspace/__tests__/workspace.routes.branch-errors.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.routes.create-grant.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.routes.delete.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.routes.download-implies-read.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.routes.download.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.routes.file-writes.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.routes.folders.test.ts +5 -4
- package/src/modules/workspace/__tests__/workspace.routes.move-platform-files.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.service.ensure-remotes-fetched.test.ts +2 -2
- package/src/modules/workspace/__tests__/workspace.service.list-cloned.test.ts +6 -5
- package/src/modules/workspace/__tests__/workspace.service.move-never-overwrites.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.service.read-filter.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.service.test.ts +84 -50
- package/src/modules/workspace/__tests__/workspace.service.unknown-branch.test.ts +2 -1
- package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +10 -7
- package/src/modules/workspace/__tests__/workspace.tools.branch-errors.test.ts +4 -3
- package/src/modules/workspace/__tests__/workspace.tools.test.ts +9 -8
- package/src/modules/workspace/__tests__/write-denial.test.ts +5 -4
- package/src/modules/workspace/session-ontology.gate.ts +6 -4
- package/src/modules/workspace/startup/__tests__/kb-startup-runner.degraded.test.ts +6 -2
- package/src/modules/workspace/startup/__tests__/kb-startup-runner.test.ts +29 -21
- package/src/modules/workspace/startup/kb-git.ts +15 -22
- package/src/modules/workspace/startup/kb-startup-runner.ts +40 -46
- package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +104 -98
- package/src/modules/workspace/startup/steps/groups-to-plugins.step.ts +24 -16
- package/src/modules/workspace/startup/steps/personal-spaces.step.ts +14 -5
- package/src/modules/workspace/startup/steps/plugin-display-names.step.ts +13 -5
- package/src/modules/workspace/startup/steps/plugin-manifests.step.ts +14 -5
- package/src/modules/workspace/startup/steps/seed-tree.ts +11 -9
- package/src/modules/workspace/startup/steps/template-files.step.ts +43 -34
- package/src/modules/workspace/startup/steps/template-source.ts +8 -5
- package/src/modules/workspace/workspace.routes.ts +8 -7
- package/src/modules/workspace/workspace.service.ts +30 -27
- package/src/modules/workspace/workspace.tools.ts +21 -20
- package/src/modules/workspace/write-denial.ts +7 -5
- package/src/shared/__tests__/kb-layout.plugin.test.ts +19 -16
- package/src/shared/git.contract.ts +70 -8
- package/src/shared/kb-context.ts +130 -0
- package/src/shared/kb-layout.test.ts +37 -27
- package/src/shared/kb-layout.ts +4 -3
- package/src/shared/pending-proposals.ts +7 -4
- package/src/shared/redact-secret.ts +5 -5
- package/src/tenancy/__tests__/static-tenant-source.test.ts +177 -0
- package/src/tenancy/__tests__/tenant-host.test.ts +372 -0
- package/src/tenancy/__tests__/tenant-runtime.test.ts +221 -0
- package/src/tenancy/__tests__/tenant-secrets.test.ts +35 -0
- package/src/tenancy/index.ts +35 -0
- package/src/tenancy/static-tenant-source.ts +296 -0
- package/src/tenancy/tenant-host.ts +261 -0
- package/src/tenancy/tenant-runtime.ts +208 -0
- package/src/tenancy/tenant-secrets.ts +41 -0
- package/src/tenancy/tenant-source.contract.ts +59 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"static-tenant-source.d.ts","sourceRoot":"","sources":["../../src/tenancy/static-tenant-source.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { resolveDatabaseUrl } from '../core-config.js';
|
|
4
|
+
import { DEFAULT_DB_SCHEMA, assertSchemaName } from '../modules/database/connection.js';
|
|
5
|
+
import { DEFAULT_GIT_TIMEOUT_MS } from '../modules/workflow/git/node-git-runner.js';
|
|
6
|
+
import { defaultKbTemplateDir } from '../assets.js';
|
|
7
|
+
import { DEFAULT_GIT_USERNAME } from '../shared/git.contract.js';
|
|
8
|
+
import { deriveTenantSecrets } from './tenant-secrets.js';
|
|
9
|
+
import { loopbackTenantBaseUrl } from './tenant-host.js';
|
|
10
|
+
import { assertTenantSlug, normalizeHost } from './tenant-source.contract.js';
|
|
11
|
+
const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
12
|
+
const USERNAME_PATTERN = /^[A-Za-z0-9._-]+$/;
|
|
13
|
+
/**
|
|
14
|
+
* A tenant's {@link TenantConfig} from its record and the host's settings:
|
|
15
|
+
* the same values `CoreConfig` reads from the environment, resolved the same
|
|
16
|
+
* way, with the folders and the schema named after the slug so two tenants
|
|
17
|
+
* can never share a path or a table.
|
|
18
|
+
*/
|
|
19
|
+
export function tenantConfigFrom(record, settings) {
|
|
20
|
+
const slug = assertTenantSlug(record.slug);
|
|
21
|
+
if (!Array.isArray(record.hosts) || record.hosts.length === 0) {
|
|
22
|
+
throw new Error(`Tenant "${slug}" names no host`);
|
|
23
|
+
}
|
|
24
|
+
const hosts = record.hosts.map(normalizeHost);
|
|
25
|
+
const adminEmail = (record.adminEmail ?? '').trim().toLowerCase();
|
|
26
|
+
if (!EMAIL_PATTERN.test(adminEmail)) {
|
|
27
|
+
throw new Error(`Tenant "${slug}": adminEmail is not a valid email: "${record.adminEmail ?? ''}"`);
|
|
28
|
+
}
|
|
29
|
+
const gitUsername = (record.gitUsername ?? DEFAULT_GIT_USERNAME).trim();
|
|
30
|
+
if (!USERNAME_PATTERN.test(gitUsername)) {
|
|
31
|
+
throw new Error(`Tenant "${slug}": gitUsername must match [A-Za-z0-9._-]+; got "${gitUsername}"`);
|
|
32
|
+
}
|
|
33
|
+
const tenantId = (record.tenantId ?? slug.replace(/-/g, '')).trim().toLowerCase();
|
|
34
|
+
if (!/^[a-z0-9]+$/.test(tenantId)) {
|
|
35
|
+
throw new Error(`Tenant "${slug}": tenantId must be lowercase alphanumeric; got "${tenantId}"`);
|
|
36
|
+
}
|
|
37
|
+
const dbSchema = assertSchemaName(record.dbSchema ?? `t_${slug.replace(/-/g, '_')}`);
|
|
38
|
+
// The default schema is where a single-tenant deployment on the same
|
|
39
|
+
// database keeps its tables and, in drizzle's own schema beside it, its
|
|
40
|
+
// migration ledger; a tenant there would be no tenant at all.
|
|
41
|
+
if (dbSchema === DEFAULT_DB_SCHEMA) {
|
|
42
|
+
throw new Error(`Tenant "${slug}": dbSchema must not be "${DEFAULT_DB_SCHEMA}"; a tenant needs a schema of its own`);
|
|
43
|
+
}
|
|
44
|
+
const adminPassword = record.adminPassword ?? '';
|
|
45
|
+
const loginPasswordEnabled = record.loginPassword ?? adminPassword !== '';
|
|
46
|
+
if (loginPasswordEnabled && !adminPassword) {
|
|
47
|
+
throw new Error(`Tenant "${slug}": loginPassword is on but no adminPassword is set`);
|
|
48
|
+
}
|
|
49
|
+
const publicBackendUrl = (record.publicBackendUrl ?? `https://${hosts[0]}`).trim().replace(/\/+$/, '');
|
|
50
|
+
const publicFrontendUrl = (record.publicFrontendUrl ?? publicBackendUrl).trim().replace(/\/+$/, '');
|
|
51
|
+
for (const [name, value] of [
|
|
52
|
+
['publicBackendUrl', publicBackendUrl],
|
|
53
|
+
['publicFrontendUrl', publicFrontendUrl],
|
|
54
|
+
]) {
|
|
55
|
+
try {
|
|
56
|
+
new URL(value);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
throw new Error(`Tenant "${slug}": ${name} is not a valid URL: "${value}"`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const secrets = deriveTenantSecrets(settings.masterKey, slug);
|
|
63
|
+
const besideWorkspaces = (name) => path.resolve(settings.workspacesRoot, '..', name, slug);
|
|
64
|
+
const allowedEmailDomains = (record.allowedEmailDomains ?? [])
|
|
65
|
+
.map((d) => d.trim().toLowerCase().replace(/^[@.]+/, ''))
|
|
66
|
+
.filter((d) => d.length > 0);
|
|
67
|
+
const protectedBranches = Array.isArray(record.protectedBranches)
|
|
68
|
+
? record.protectedBranches.join(',')
|
|
69
|
+
: (record.protectedBranches ?? '');
|
|
70
|
+
// The record's settings, as the environment a single-tenant deployment
|
|
71
|
+
// would pin them in: only what the record names, so the setup screen
|
|
72
|
+
// still collects the rest. Nothing of the host process's environment.
|
|
73
|
+
const settingsEnv = {};
|
|
74
|
+
for (const [name, value] of [
|
|
75
|
+
['KB_REPO_URL', record.kbRepoUrl],
|
|
76
|
+
['GIT_TOKEN', record.gitToken],
|
|
77
|
+
['GIT_USERNAME', record.gitUsername],
|
|
78
|
+
['KB_DIR_NAME', record.kbDirName],
|
|
79
|
+
['DEFAULT_BRANCH', record.defaultBranch],
|
|
80
|
+
['PROTECTED_BRANCHES', protectedBranches],
|
|
81
|
+
['KB_SYNC_SECRET', record.kbSyncSecret],
|
|
82
|
+
['OIDC_ISSUER_URL', record.oidc?.issuerUrl],
|
|
83
|
+
['OIDC_CLIENT_ID', record.oidc?.clientId],
|
|
84
|
+
['OIDC_CLIENT_SECRET', record.oidc?.clientSecret],
|
|
85
|
+
['OIDC_SCOPES', record.oidc?.scopes],
|
|
86
|
+
['OIDC_PROVIDER_LABEL', record.oidc?.providerLabel],
|
|
87
|
+
['ALLOWED_EMAIL_DOMAINS', allowedEmailDomains.join(',')],
|
|
88
|
+
]) {
|
|
89
|
+
const trimmed = (value ?? '').trim();
|
|
90
|
+
if (trimmed)
|
|
91
|
+
settingsEnv[name] = trimmed;
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
port: settings.port,
|
|
95
|
+
databaseUrl: settings.databaseUrl,
|
|
96
|
+
dbSchema,
|
|
97
|
+
nodeEnv: settings.nodeEnv,
|
|
98
|
+
tenantId,
|
|
99
|
+
externalApiKeyPrefix: `${tenantId}_`,
|
|
100
|
+
internalTokenPrefix: `${tenantId}-int_`,
|
|
101
|
+
uploadTokenPrefix: `${tenantId}-up_`,
|
|
102
|
+
mcpOAuthTokenPrefix: `${tenantId}-mcp_`,
|
|
103
|
+
workspacesRoot: path.resolve(settings.workspacesRoot, slug),
|
|
104
|
+
backupsRoot: besideWorkspaces('backups'),
|
|
105
|
+
spillRoot: besideWorkspaces('tool-chain-spills'),
|
|
106
|
+
docExtractCacheRoot: besideWorkspaces('doc-extract-cache'),
|
|
107
|
+
jwtSecret: secrets.jwtSecret,
|
|
108
|
+
secretsEncKey: secrets.secretsEncKey,
|
|
109
|
+
internalTokenSecret: secrets.internalTokenSecret,
|
|
110
|
+
adminEmail,
|
|
111
|
+
adminPassword,
|
|
112
|
+
loginPasswordEnabled,
|
|
113
|
+
oidcIssuerUrl: (record.oidc?.issuerUrl ?? '').trim().replace(/\/+$/, ''),
|
|
114
|
+
oidcClientId: (record.oidc?.clientId ?? '').trim(),
|
|
115
|
+
oidcClientSecret: (record.oidc?.clientSecret ?? '').trim(),
|
|
116
|
+
oidcScopes: (record.oidc?.scopes ?? 'openid profile email').trim(),
|
|
117
|
+
oidcProviderLabel: (record.oidc?.providerLabel ?? 'Single sign-on').trim(),
|
|
118
|
+
kbRepoUrl: (record.kbRepoUrl ?? '').trim(),
|
|
119
|
+
kbDirName: (record.kbDirName ?? '').trim(),
|
|
120
|
+
gitUsername,
|
|
121
|
+
gitToken: (record.gitToken ?? '').trim(),
|
|
122
|
+
kbTemplateDir: settings.kbTemplateDir,
|
|
123
|
+
ontologySessionBlock: settings.ontologySessionBlock,
|
|
124
|
+
updateCheckEnabled: settings.updateCheckEnabled,
|
|
125
|
+
allowedEmailDomains,
|
|
126
|
+
trustProxy: settings.trustProxy,
|
|
127
|
+
gitTimeoutMs: settings.gitTimeoutMs,
|
|
128
|
+
publicBackendUrl,
|
|
129
|
+
publicFrontendUrl,
|
|
130
|
+
configuredPublicFrontendUrl: publicFrontendUrl,
|
|
131
|
+
loopbackBaseUrl: loopbackTenantBaseUrl(settings.port, slug),
|
|
132
|
+
settingsEnv,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* The reference {@link TenantSource}: every tenant known up front, from a
|
|
137
|
+
* JSON file (`TENANTS_FILE`). Enough for development, tests and a small
|
|
138
|
+
* self-hosted fleet; the cloud app brings its own source over its registry.
|
|
139
|
+
*/
|
|
140
|
+
export class StaticTenantSource {
|
|
141
|
+
bySlug = new Map();
|
|
142
|
+
byHost = new Map();
|
|
143
|
+
constructor(records, settings) {
|
|
144
|
+
for (const record of records) {
|
|
145
|
+
const config = tenantConfigFrom(record, settings);
|
|
146
|
+
const descriptor = {
|
|
147
|
+
slug: record.slug,
|
|
148
|
+
hosts: record.hosts.map(normalizeHost),
|
|
149
|
+
config,
|
|
150
|
+
};
|
|
151
|
+
if (this.bySlug.has(descriptor.slug))
|
|
152
|
+
throw new Error(`Tenant "${descriptor.slug}" is listed twice`);
|
|
153
|
+
this.bySlug.set(descriptor.slug, descriptor);
|
|
154
|
+
for (const host of descriptor.hosts) {
|
|
155
|
+
const taken = this.byHost.get(host);
|
|
156
|
+
if (taken)
|
|
157
|
+
throw new Error(`Host "${host}" is claimed by both "${taken.slug}" and "${descriptor.slug}"`);
|
|
158
|
+
this.byHost.set(host, descriptor);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
static async fromFile(file, settings) {
|
|
163
|
+
const text = await fs.readFile(file, 'utf8');
|
|
164
|
+
let parsed;
|
|
165
|
+
try {
|
|
166
|
+
parsed = JSON.parse(text);
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
throw new Error(`${file} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
170
|
+
}
|
|
171
|
+
const tenants = parsed?.tenants;
|
|
172
|
+
if (!Array.isArray(tenants))
|
|
173
|
+
throw new Error(`${file} must be an object with a "tenants" array`);
|
|
174
|
+
return new StaticTenantSource(tenants, settings);
|
|
175
|
+
}
|
|
176
|
+
get slugs() {
|
|
177
|
+
return [...this.bySlug.keys()];
|
|
178
|
+
}
|
|
179
|
+
async resolveByHost(host) {
|
|
180
|
+
return this.byHost.get(normalizeHost(host)) ?? null;
|
|
181
|
+
}
|
|
182
|
+
async describe(slug) {
|
|
183
|
+
return this.bySlug.get(slug) ?? null;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const MAX_TIMER_MS = 2_147_483_647;
|
|
187
|
+
/**
|
|
188
|
+
* The host's settings from the environment: the same variables a
|
|
189
|
+
* single-tenant deployment reads for these facts (`DATABASE_URL` and the
|
|
190
|
+
* `POSTGRES_*` parts, `PORT`, `TRUST_PROXY`, `WORKSPACES_ROOT`,
|
|
191
|
+
* `KB_TEMPLATE_DIR`, `GIT_TIMEOUT_MS`, …), plus the three that only a host
|
|
192
|
+
* has: `TENANTS_FILE`, `TENANT_MASTER_KEY` and `TENANT_IDLE_MINUTES`.
|
|
193
|
+
*/
|
|
194
|
+
export function tenantHostEnv(env = process.env) {
|
|
195
|
+
const tenantsFile = (env.TENANTS_FILE || '').trim() || null;
|
|
196
|
+
const masterKey = (env.TENANT_MASTER_KEY || '').trim();
|
|
197
|
+
if (tenantsFile && !masterKey) {
|
|
198
|
+
throw new Error('TENANT_MASTER_KEY is required with TENANTS_FILE — every tenant\'s secrets derive from it. Generate one with: ' +
|
|
199
|
+
`node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"`);
|
|
200
|
+
}
|
|
201
|
+
const gitTimeout = Number(env.GIT_TIMEOUT_MS);
|
|
202
|
+
const idle = Number(env.TENANT_IDLE_MINUTES);
|
|
203
|
+
const domain = (env.DOMAIN || '').trim();
|
|
204
|
+
const workspacesRoot = env.WORKSPACES_ROOT || path.resolve(process.cwd(), 'workspaces');
|
|
205
|
+
return {
|
|
206
|
+
tenantsFile,
|
|
207
|
+
masterKey,
|
|
208
|
+
databaseUrl: resolveDatabaseUrl(env),
|
|
209
|
+
port: parseInt(env.PORT || '3001', 10),
|
|
210
|
+
nodeEnv: env.NODE_ENV || 'development',
|
|
211
|
+
trustProxy: (env.TRUST_PROXY || (domain ? '1' : '')).trim(),
|
|
212
|
+
workspacesRoot,
|
|
213
|
+
kbTemplateDir: env.KB_TEMPLATE_DIR || defaultKbTemplateDir(),
|
|
214
|
+
gitTimeoutMs: Number.isFinite(gitTimeout) && gitTimeout > 0 && gitTimeout <= MAX_TIMER_MS ? gitTimeout : DEFAULT_GIT_TIMEOUT_MS,
|
|
215
|
+
ontologySessionBlock: (env.ONTOLOGY_SESSION_BLOCK ?? 'true').trim().toLowerCase() !== 'false',
|
|
216
|
+
updateCheckEnabled: (env.UPDATE_CHECK ?? 'true').trim().toLowerCase() !== 'false',
|
|
217
|
+
idleMinutes: Number.isFinite(idle) && idle > 0 ? idle : 30,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=static-tenant-source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"static-tenant-source.js","sourceRoot":"","sources":["../../src/tenancy/static-tenant-source.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { type Express } from 'express';
|
|
2
|
+
import type { ProcessConfig } from '../core-config.js';
|
|
3
|
+
import { TenantRuntime, type TenantRuntimeDeps } from './tenant-runtime.js';
|
|
4
|
+
import { type TenantSource } from './tenant-source.contract.js';
|
|
5
|
+
/**
|
|
6
|
+
* The path prefix under which a process reaches ONE of its own tenants over
|
|
7
|
+
* loopback: `http://127.0.0.1:<port>/_tenant/<slug>/api/...`. The MCP proxy
|
|
8
|
+
* and the UTCP manuals it seeds dial the tenant's own REST surface this way,
|
|
9
|
+
* because a loopback request carries no tenant host name and a header cannot
|
|
10
|
+
* ride a UTCP call (the proxy is a passthrough; it reshapes nothing).
|
|
11
|
+
*
|
|
12
|
+
* The host honours the prefix only for a request that came from this
|
|
13
|
+
* machine AND was forwarded by nobody: the socket peer is loopback and there
|
|
14
|
+
* is no `X-Forwarded-For`. A reverse proxy on the same host (nginx, a
|
|
15
|
+
* Coolify proxy) delivers every public request from a loopback socket, and
|
|
16
|
+
* every such proxy adds that header; the process's own dial never does.
|
|
17
|
+
* What the prefix could grant anyone who got past this is only what the
|
|
18
|
+
* tenant's host name grants already — sessions, keys and tokens are per
|
|
19
|
+
* tenant — but the addressing rule is stated so it can be relied on.
|
|
20
|
+
*/
|
|
21
|
+
export declare const LOOPBACK_TENANT_PREFIX = "/_tenant";
|
|
22
|
+
/** The base URL a tenant's graph dials itself at — see {@link LOOPBACK_TENANT_PREFIX}. */
|
|
23
|
+
export declare function loopbackTenantBaseUrl(port: number, slug: string): string;
|
|
24
|
+
/** Whether `address` is this machine talking to itself. */
|
|
25
|
+
export declare function isLoopbackAddress(address: string | undefined): boolean;
|
|
26
|
+
/** How a request names its tenant: by the loopback prefix, or by its host. */
|
|
27
|
+
export type TenantSelector = {
|
|
28
|
+
kind: 'slug';
|
|
29
|
+
slug: string;
|
|
30
|
+
rest: string;
|
|
31
|
+
} | {
|
|
32
|
+
kind: 'host';
|
|
33
|
+
host: string;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Which tenant a request is for. The loopback prefix wins, but only from a
|
|
37
|
+
* loopback peer that no proxy forwarded (no `X-Forwarded-For`); everything
|
|
38
|
+
* else is decided by the host name (the `X-Forwarded-Host` a trusted proxy
|
|
39
|
+
* set, else `Host`), lowercase and without its port.
|
|
40
|
+
*/
|
|
41
|
+
export declare function selectTenant(req: {
|
|
42
|
+
hostname: string;
|
|
43
|
+
url: string;
|
|
44
|
+
peer: string | undefined;
|
|
45
|
+
forwardedFor?: string | string[] | undefined;
|
|
46
|
+
}): TenantSelector;
|
|
47
|
+
export interface TenantHostOptions {
|
|
48
|
+
source: TenantSource;
|
|
49
|
+
process: ProcessConfig;
|
|
50
|
+
/** The built SPA, served once for every resolved tenant. */
|
|
51
|
+
staticDir?: string;
|
|
52
|
+
/** Minutes without a request after which a tenant's graph is stopped. Default 30; 0 never evicts. */
|
|
53
|
+
idleMinutes?: number;
|
|
54
|
+
/** How long a request waits for an activation in flight before answering 503. Default 60s. */
|
|
55
|
+
activationWaitMs?: number;
|
|
56
|
+
/** Seams for the runtimes the host builds — a suite's fake graphs. */
|
|
57
|
+
runtime?: TenantRuntimeDeps;
|
|
58
|
+
/** How often idle tenants are looked for. Default: once a minute. */
|
|
59
|
+
sweepIntervalMs?: number;
|
|
60
|
+
}
|
|
61
|
+
export interface TenantHost {
|
|
62
|
+
/** The app the process listens on. */
|
|
63
|
+
app: Express;
|
|
64
|
+
/** The runtimes built so far, by slug. */
|
|
65
|
+
runtimes(): ReadonlyMap<string, TenantRuntime>;
|
|
66
|
+
/** Stop every active tenant; the host keeps serving and reactivates on demand. */
|
|
67
|
+
evictAll(): Promise<void>;
|
|
68
|
+
/** Stop the idle sweep and every tenant: the process's shutdown. */
|
|
69
|
+
stop(): Promise<void>;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* One Express app in front of many tenants' apps. It answers process health
|
|
73
|
+
* itself, resolves each request to a tenant (see {@link selectTenant}),
|
|
74
|
+
* activates that tenant's graph on first use, hands the request to the
|
|
75
|
+
* tenant's app, and serves the SPA for whatever the tenant's app leaves
|
|
76
|
+
* unhandled. Nothing below it knows there are other tenants: each graph was
|
|
77
|
+
* built by the same composition root a single-tenant deployment uses.
|
|
78
|
+
*/
|
|
79
|
+
export declare function createTenantHost(opts: TenantHostOptions): TenantHost;
|
|
80
|
+
//# sourceMappingURL=tenant-host.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-host.d.ts","sourceRoot":"","sources":["../../src/tenancy/tenant-host.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,KAAK,OAAO,EAAgB,MAAM,SAAS,CAAC;AAG9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGvD,OAAO,EAAE,aAAa,EAAE,KAAK,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC5E,OAAO,EAA6D,KAAK,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAI3H;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,sBAAsB,aAAa,CAAC;AAEjD,0FAA0F;AAC1F,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAExE;AAED,2DAA2D;AAC3D,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAKtE;AAED,8EAA8E;AAC9E,MAAM,MAAM,cAAc,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3G;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC;CAC9C,GAAG,cAAc,CAYjB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,EAAE,aAAa,CAAC;IACvB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qGAAqG;IACrG,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8FAA8F;IAC9F,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sEAAsE;IACtE,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,qEAAqE;IACrE,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,UAAU;IACzB,sCAAsC;IACtC,GAAG,EAAE,OAAO,CAAC;IACb,0CAA0C;IAC1C,QAAQ,IAAI,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC/C,kFAAkF;IAClF,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,oEAAoE;IACpE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAeD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,iBAAiB,GAAG,UAAU,CA+IpE"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { isIP } from 'node:net';
|
|
4
|
+
import { GIT_SHA } from '../version.js';
|
|
5
|
+
import { logger } from '../shared/logging.js';
|
|
6
|
+
import { TenantRuntime } from './tenant-runtime.js';
|
|
7
|
+
import { TENANT_SLUG_PATTERN, normalizeHost } from './tenant-source.contract.js';
|
|
8
|
+
const log = logger('tenancy');
|
|
9
|
+
/**
|
|
10
|
+
* The path prefix under which a process reaches ONE of its own tenants over
|
|
11
|
+
* loopback: `http://127.0.0.1:<port>/_tenant/<slug>/api/...`. The MCP proxy
|
|
12
|
+
* and the UTCP manuals it seeds dial the tenant's own REST surface this way,
|
|
13
|
+
* because a loopback request carries no tenant host name and a header cannot
|
|
14
|
+
* ride a UTCP call (the proxy is a passthrough; it reshapes nothing).
|
|
15
|
+
*
|
|
16
|
+
* The host honours the prefix only for a request that came from this
|
|
17
|
+
* machine AND was forwarded by nobody: the socket peer is loopback and there
|
|
18
|
+
* is no `X-Forwarded-For`. A reverse proxy on the same host (nginx, a
|
|
19
|
+
* Coolify proxy) delivers every public request from a loopback socket, and
|
|
20
|
+
* every such proxy adds that header; the process's own dial never does.
|
|
21
|
+
* What the prefix could grant anyone who got past this is only what the
|
|
22
|
+
* tenant's host name grants already — sessions, keys and tokens are per
|
|
23
|
+
* tenant — but the addressing rule is stated so it can be relied on.
|
|
24
|
+
*/
|
|
25
|
+
export const LOOPBACK_TENANT_PREFIX = '/_tenant';
|
|
26
|
+
/** The base URL a tenant's graph dials itself at — see {@link LOOPBACK_TENANT_PREFIX}. */
|
|
27
|
+
export function loopbackTenantBaseUrl(port, slug) {
|
|
28
|
+
return `http://127.0.0.1:${port}${LOOPBACK_TENANT_PREFIX}/${slug}`;
|
|
29
|
+
}
|
|
30
|
+
/** Whether `address` is this machine talking to itself. */
|
|
31
|
+
export function isLoopbackAddress(address) {
|
|
32
|
+
if (!address)
|
|
33
|
+
return false;
|
|
34
|
+
const bare = address.startsWith('::ffff:') ? address.slice('::ffff:'.length) : address;
|
|
35
|
+
if (bare === '::1')
|
|
36
|
+
return true;
|
|
37
|
+
return isIP(bare) === 4 && bare.startsWith('127.');
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Which tenant a request is for. The loopback prefix wins, but only from a
|
|
41
|
+
* loopback peer that no proxy forwarded (no `X-Forwarded-For`); everything
|
|
42
|
+
* else is decided by the host name (the `X-Forwarded-Host` a trusted proxy
|
|
43
|
+
* set, else `Host`), lowercase and without its port.
|
|
44
|
+
*/
|
|
45
|
+
export function selectTenant(req) {
|
|
46
|
+
const forwarded = Array.isArray(req.forwardedFor) ? req.forwardedFor.length > 0 : Boolean(req.forwardedFor);
|
|
47
|
+
if (isLoopbackAddress(req.peer) && !forwarded && req.url.startsWith(`${LOOPBACK_TENANT_PREFIX}/`)) {
|
|
48
|
+
const afterPrefix = req.url.slice(LOOPBACK_TENANT_PREFIX.length + 1);
|
|
49
|
+
const end = afterPrefix.search(/[/?#]/);
|
|
50
|
+
const slug = end === -1 ? afterPrefix : afterPrefix.slice(0, end);
|
|
51
|
+
if (TENANT_SLUG_PATTERN.test(slug)) {
|
|
52
|
+
const rest = end === -1 ? '/' : afterPrefix.slice(end).replace(/^(?=[?#])/, '/');
|
|
53
|
+
return { kind: 'slug', slug, rest };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return { kind: 'host', host: normalizeHost(req.hostname || '') };
|
|
57
|
+
}
|
|
58
|
+
const DEFAULT_ACTIVATION_WAIT_MS = 60_000;
|
|
59
|
+
const DEFAULT_IDLE_MINUTES = 30;
|
|
60
|
+
const DEFAULT_SWEEP_INTERVAL_MS = 60_000;
|
|
61
|
+
/** `promise`, or `null` once `ms` have passed. */
|
|
62
|
+
function within(promise, ms) {
|
|
63
|
+
let timer;
|
|
64
|
+
const late = new Promise((resolve) => {
|
|
65
|
+
timer = setTimeout(() => resolve(null), ms);
|
|
66
|
+
});
|
|
67
|
+
return Promise.race([promise, late]).finally(() => clearTimeout(timer));
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* One Express app in front of many tenants' apps. It answers process health
|
|
71
|
+
* itself, resolves each request to a tenant (see {@link selectTenant}),
|
|
72
|
+
* activates that tenant's graph on first use, hands the request to the
|
|
73
|
+
* tenant's app, and serves the SPA for whatever the tenant's app leaves
|
|
74
|
+
* unhandled. Nothing below it knows there are other tenants: each graph was
|
|
75
|
+
* built by the same composition root a single-tenant deployment uses.
|
|
76
|
+
*/
|
|
77
|
+
export function createTenantHost(opts) {
|
|
78
|
+
const runtimes = new Map();
|
|
79
|
+
const activationWaitMs = opts.activationWaitMs ?? DEFAULT_ACTIVATION_WAIT_MS;
|
|
80
|
+
const idleMs = (opts.idleMinutes ?? DEFAULT_IDLE_MINUTES) * 60_000;
|
|
81
|
+
const runtimeFor = (descriptor) => {
|
|
82
|
+
let runtime = runtimes.get(descriptor.slug);
|
|
83
|
+
if (!runtime) {
|
|
84
|
+
runtime = new TenantRuntime(descriptor, opts.runtime);
|
|
85
|
+
runtimes.set(descriptor.slug, runtime);
|
|
86
|
+
}
|
|
87
|
+
return runtime;
|
|
88
|
+
};
|
|
89
|
+
const app = express();
|
|
90
|
+
// Forwarded headers are honoured exactly as a single-tenant deployment
|
|
91
|
+
// honours them, and for the same reason: `req.hostname` is what picks the
|
|
92
|
+
// tenant, and behind a proxy it lives in `X-Forwarded-Host`.
|
|
93
|
+
if (opts.process.trustProxy) {
|
|
94
|
+
const raw = opts.process.trustProxy;
|
|
95
|
+
app.set('trust proxy', /^\d+$/.test(raw) ? Number(raw) : raw);
|
|
96
|
+
}
|
|
97
|
+
// The process's own health, before any tenant is involved: a load balancer
|
|
98
|
+
// asks with whatever host name it likes, and a process with no active
|
|
99
|
+
// tenant is still a healthy process.
|
|
100
|
+
app.get('/api/health', (_req, res) => {
|
|
101
|
+
res.json({ status: 'ok', sha: GIT_SHA, timestamp: Date.now(), tenants: runtimes.size });
|
|
102
|
+
});
|
|
103
|
+
app.use(async (req, res, next) => {
|
|
104
|
+
const selector = selectTenant({
|
|
105
|
+
hostname: req.hostname,
|
|
106
|
+
url: req.url,
|
|
107
|
+
peer: req.socket.remoteAddress,
|
|
108
|
+
forwardedFor: req.headers['x-forwarded-for'],
|
|
109
|
+
});
|
|
110
|
+
let descriptor;
|
|
111
|
+
try {
|
|
112
|
+
descriptor =
|
|
113
|
+
selector.kind === 'slug'
|
|
114
|
+
? await opts.source.describe(selector.slug)
|
|
115
|
+
: await opts.source.resolveByHost(selector.host);
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
log.error('tenant lookup failed:', { err });
|
|
119
|
+
res.status(503).set('Retry-After', '5').json({ error: 'Tenant lookup failed' });
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (!descriptor) {
|
|
123
|
+
if (req.path.startsWith('/api/') || req.path.startsWith(LOOPBACK_TENANT_PREFIX)) {
|
|
124
|
+
res.status(404).json({ error: 'No workspace at this address' });
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
res.status(404).type('text/plain').send('No workspace at this address.');
|
|
128
|
+
}
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (selector.kind === 'slug')
|
|
132
|
+
req.url = selector.rest;
|
|
133
|
+
const runtime = runtimeFor(descriptor);
|
|
134
|
+
// Counted from here, before the graph is even asked for, until the
|
|
135
|
+
// response closes however it ends — a 503 below included. An open
|
|
136
|
+
// response is what keeps a tenant from being evicted from under it.
|
|
137
|
+
// Left exactly once: a client that dropped during the tenant lookup
|
|
138
|
+
// above has had its `close` already, and that event will not come
|
|
139
|
+
// again, so the socket is asked directly; a socket destroyed but not
|
|
140
|
+
// yet closed would otherwise be left twice, once here and once by the
|
|
141
|
+
// listener, taking another request's count with it.
|
|
142
|
+
runtime.enter();
|
|
143
|
+
let left = false;
|
|
144
|
+
const leave = () => {
|
|
145
|
+
if (left)
|
|
146
|
+
return;
|
|
147
|
+
left = true;
|
|
148
|
+
runtime.leave();
|
|
149
|
+
};
|
|
150
|
+
res.on('close', leave);
|
|
151
|
+
if (req.socket.destroyed) {
|
|
152
|
+
leave();
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
let graph;
|
|
156
|
+
try {
|
|
157
|
+
graph = await within(runtime.handle(), activationWaitMs);
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
// Logged by the runtime; the caller learns only that it may try again.
|
|
161
|
+
res.status(503).set('Retry-After', '5').json({ error: 'This workspace could not be started; try again shortly' });
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (!graph) {
|
|
165
|
+
res.status(503).set('Retry-After', '5').json({ error: 'This workspace is starting; try again shortly' });
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
graph.app(req, res, next);
|
|
169
|
+
});
|
|
170
|
+
// Whatever a tenant's app left unhandled: the SPA, with the same rule a
|
|
171
|
+
// single-tenant deployment applies to the namespaces that must never
|
|
172
|
+
// resolve to `index.html` (see `createCoreServer`).
|
|
173
|
+
if (opts.staticDir) {
|
|
174
|
+
const frontendDist = opts.staticDir;
|
|
175
|
+
app.use(express.static(frontendDist));
|
|
176
|
+
app.get('{*path}', (req, res) => {
|
|
177
|
+
if (req.path.startsWith('/.well-known/') ||
|
|
178
|
+
req.path.startsWith('/api/') ||
|
|
179
|
+
req.path.startsWith('/login/oauth/')) {
|
|
180
|
+
res.status(404).json({ error: 'Not found' });
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
res.sendFile(path.join(frontendDist, 'index.html'));
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
// Idle tenants are stopped on a timer, never on the request that would
|
|
187
|
+
// have been their last: a graph whose last response closed `idleMs` ago,
|
|
188
|
+
// with none still open and no queued commit, is not worth its pool and
|
|
189
|
+
// its lease. `busy()` is what keeps an event stream's tenant alive.
|
|
190
|
+
const sweep = async () => {
|
|
191
|
+
for (const runtime of runtimes.values()) {
|
|
192
|
+
if (runtime.state !== 'active' || runtime.idleFor() < idleMs)
|
|
193
|
+
continue;
|
|
194
|
+
if (await runtime.busy())
|
|
195
|
+
continue;
|
|
196
|
+
await runtime.evict().catch((err) => log.warn(`evicting tenant "${runtime.slug}" failed:`, { err }));
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
const sweeper = idleMs > 0
|
|
200
|
+
? setInterval(() => void sweep(), Math.min(opts.sweepIntervalMs ?? DEFAULT_SWEEP_INTERVAL_MS, idleMs))
|
|
201
|
+
: null;
|
|
202
|
+
sweeper?.unref();
|
|
203
|
+
const evictAll = async () => {
|
|
204
|
+
await Promise.all([...runtimes.values()].map((runtime) => runtime.evict().catch(() => undefined)));
|
|
205
|
+
};
|
|
206
|
+
return {
|
|
207
|
+
app,
|
|
208
|
+
runtimes: () => runtimes,
|
|
209
|
+
evictAll,
|
|
210
|
+
async stop() {
|
|
211
|
+
if (sweeper)
|
|
212
|
+
clearInterval(sweeper);
|
|
213
|
+
await evictAll();
|
|
214
|
+
},
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=tenant-host.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-host.js","sourceRoot":"","sources":["../../src/tenancy/tenant-host.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { Express } from 'express';
|
|
2
|
+
import { type CoreServices } from '../core/create-core-services.js';
|
|
3
|
+
import type { TenantDescriptor } from './tenant-source.contract.js';
|
|
4
|
+
/** A tenant's running graph: its services and the Express app that serves them. */
|
|
5
|
+
export interface TenantGraph {
|
|
6
|
+
core: CoreServices;
|
|
7
|
+
app: Express;
|
|
8
|
+
}
|
|
9
|
+
export type TenantState = 'idle' | 'activating' | 'active' | 'evicting';
|
|
10
|
+
/** The lifecycle seams, so a suite can drive a runtime without a database. */
|
|
11
|
+
export interface TenantRuntimeDeps {
|
|
12
|
+
/** Build and start the graph. Default: `createCoreServices` + `createCoreServer`. */
|
|
13
|
+
activate?: (descriptor: TenantDescriptor) => Promise<TenantGraph>;
|
|
14
|
+
/** Stop a graph. Default: `stopCore`. */
|
|
15
|
+
stop?: (graph: TenantGraph) => Promise<void>;
|
|
16
|
+
/** Whether the graph has work in flight that an eviction would strand. Default: queued commits. */
|
|
17
|
+
busy?: (graph: TenantGraph) => Promise<boolean>;
|
|
18
|
+
now?: () => number;
|
|
19
|
+
/**
|
|
20
|
+
* How long a failed activation is remembered before the next request tries
|
|
21
|
+
* again: `initialMs` after the first failure, doubling per consecutive
|
|
22
|
+
* failure up to `maxMs`. Defaults: 5 s to 60 s.
|
|
23
|
+
*/
|
|
24
|
+
retry?: {
|
|
25
|
+
initialMs?: number;
|
|
26
|
+
maxMs?: number;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* One tenant's graph, brought up on first use and stopped when the host
|
|
31
|
+
* says so: `idle → activating → active → evicting → idle`.
|
|
32
|
+
*
|
|
33
|
+
* ACTIVATE ONCE. Requests arriving while the graph is being built all await
|
|
34
|
+
* the one activation in flight. A failed activation is remembered for a
|
|
35
|
+
* short, growing window and answered from memory inside it, so a tenant
|
|
36
|
+
* whose remote is down or whose token is wrong costs one clone attempt per
|
|
37
|
+
* window rather than one per request; past the window the next request
|
|
38
|
+
* tries again, since the remote may be back or the token fixed. An eviction
|
|
39
|
+
* that lands mid-activation waits for the activation and then stops what it
|
|
40
|
+
* produced, so a graph is never left running behind an evicted runtime.
|
|
41
|
+
*
|
|
42
|
+
* IN USE MEANS A RESPONSE IS OPEN. The host tells the runtime when it hands
|
|
43
|
+
* a request over and when that response closes, so idleness counts from the
|
|
44
|
+
* last response that ENDED, and a tenant with any response still open — an
|
|
45
|
+
* event stream a browser holds, a clone in flight — is busy and never
|
|
46
|
+
* evicted from under it.
|
|
47
|
+
*/
|
|
48
|
+
export declare class TenantRuntime {
|
|
49
|
+
readonly descriptor: TenantDescriptor;
|
|
50
|
+
private graph;
|
|
51
|
+
private activation;
|
|
52
|
+
private eviction;
|
|
53
|
+
private failure;
|
|
54
|
+
private openResponses;
|
|
55
|
+
private lastUsedAt;
|
|
56
|
+
private readonly activate;
|
|
57
|
+
private readonly stopGraph;
|
|
58
|
+
private readonly isBusy;
|
|
59
|
+
private readonly now;
|
|
60
|
+
private readonly retryInitialMs;
|
|
61
|
+
private readonly retryMaxMs;
|
|
62
|
+
constructor(descriptor: TenantDescriptor, deps?: TenantRuntimeDeps);
|
|
63
|
+
/** A request was handed to this tenant; paired with {@link leave} when its response closes. */
|
|
64
|
+
enter(): void;
|
|
65
|
+
/** A response to this tenant closed, however it ended. */
|
|
66
|
+
leave(): void;
|
|
67
|
+
/** Responses handed over and not yet closed. */
|
|
68
|
+
get open(): number;
|
|
69
|
+
get slug(): string;
|
|
70
|
+
get state(): TenantState;
|
|
71
|
+
/** Milliseconds since a request last touched this tenant. */
|
|
72
|
+
idleFor(): number;
|
|
73
|
+
/**
|
|
74
|
+
* The graph, built if it is not running. Every caller of a concurrent
|
|
75
|
+
* first burst gets the same promise; a caller that arrives during an
|
|
76
|
+
* eviction waits for it, then starts the graph afresh.
|
|
77
|
+
*/
|
|
78
|
+
handle(): Promise<TenantGraph>;
|
|
79
|
+
/**
|
|
80
|
+
* Whether an eviction would strand something: a response still open, or
|
|
81
|
+
* work the graph reports (queued commits). False when nothing runs.
|
|
82
|
+
*/
|
|
83
|
+
busy(): Promise<boolean>;
|
|
84
|
+
/**
|
|
85
|
+
* Stop the graph, if one runs or is being built. Idempotent: a second call
|
|
86
|
+
* joins the eviction under way. Afterwards the runtime is idle and the
|
|
87
|
+
* next {@link handle} builds a fresh graph.
|
|
88
|
+
*/
|
|
89
|
+
evict(): Promise<void>;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=tenant-runtime.d.ts.map
|