@objectstack/service-datasource 9.11.0 → 10.2.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/.turbo/turbo-build.log +19 -15
- package/CHANGELOG.md +88 -0
- package/README.md +34 -0
- package/dist/chunk-BI2SYWLC.cjs +9 -0
- package/dist/chunk-BI2SYWLC.cjs.map +1 -0
- package/dist/chunk-XLS4RP7B.js +9 -0
- package/dist/chunk-XLS4RP7B.js.map +1 -0
- package/dist/contracts/index.cjs +7 -1
- package/dist/contracts/index.cjs.map +1 -1
- package/dist/contracts/index.d.cts +59 -1
- package/dist/contracts/index.d.ts +59 -1
- package/dist/contracts/index.js +6 -0
- package/dist/index.cjs +555 -89
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +204 -4
- package/dist/index.d.ts +204 -4
- package/dist/index.js +497 -31
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
- package/src/__tests__/admin-routes.test.ts +58 -0
- package/src/__tests__/datasource-admin-plugin.test.ts +59 -0
- package/src/__tests__/datasource-admin-service.test.ts +30 -0
- package/src/__tests__/datasource-connection-service.test.ts +294 -0
- package/src/admin-routes.ts +75 -0
- package/src/contracts/connect-policy.ts +69 -0
- package/src/contracts/index.ts +11 -0
- package/src/datasource-admin-plugin.ts +189 -43
- package/src/datasource-admin-service.ts +32 -0
- package/src/datasource-connection-service.ts +364 -0
- package/src/driver-catalog.ts +113 -0
- package/src/external-datasource-service.ts +25 -0
- package/src/index.ts +18 -0
- package/src/logger.ts +2 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @objectstack/service-datasource@
|
|
2
|
+
> @objectstack/service-datasource@10.2.0 build /home/runner/work/framework/framework/packages/services/service-datasource
|
|
3
3
|
> tsup
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts, src/contracts/index.ts
|
|
@@ -10,19 +10,23 @@
|
|
|
10
10
|
[34mCLI[39m Cleaning output folder
|
|
11
11
|
[34mESM[39m Build start
|
|
12
12
|
[34mCJS[39m Build start
|
|
13
|
-
[32mESM[39m [1mdist/index.js [22m[
|
|
14
|
-
[32mESM[39m [1mdist/contracts/index.js [22m[
|
|
15
|
-
[32mESM[39m [1mdist/
|
|
13
|
+
[32mESM[39m [1mdist/index.js [22m[32m52.34 KB[39m
|
|
14
|
+
[32mESM[39m [1mdist/contracts/index.js [22m[32m133.00 B[39m
|
|
15
|
+
[32mESM[39m [1mdist/chunk-XLS4RP7B.js [22m[32m185.00 B[39m
|
|
16
|
+
[32mESM[39m [1mdist/index.js.map [22m[32m129.15 KB[39m
|
|
16
17
|
[32mESM[39m [1mdist/contracts/index.js.map [22m[32m71.00 B[39m
|
|
17
|
-
[32mESM[39m
|
|
18
|
-
[
|
|
19
|
-
[32mCJS[39m [1mdist/
|
|
20
|
-
[32mCJS[39m [1mdist/contracts/index.cjs
|
|
21
|
-
[32mCJS[39m [1mdist/
|
|
22
|
-
[32mCJS[39m
|
|
18
|
+
[32mESM[39m [1mdist/chunk-XLS4RP7B.js.map [22m[32m3.10 KB[39m
|
|
19
|
+
[32mESM[39m ⚡️ Build success in 934ms
|
|
20
|
+
[32mCJS[39m [1mdist/index.cjs [22m[32m63.66 KB[39m
|
|
21
|
+
[32mCJS[39m [1mdist/contracts/index.cjs [22m[32m242.00 B[39m
|
|
22
|
+
[32mCJS[39m [1mdist/chunk-BI2SYWLC.cjs [22m[32m280.00 B[39m
|
|
23
|
+
[32mCJS[39m [1mdist/index.cjs.map [22m[32m110.35 KB[39m
|
|
24
|
+
[32mCJS[39m [1mdist/contracts/index.cjs.map [22m[32m304.00 B[39m
|
|
25
|
+
[32mCJS[39m [1mdist/chunk-BI2SYWLC.cjs.map [22m[32m3.38 KB[39m
|
|
26
|
+
[32mCJS[39m ⚡️ Build success in 1084ms
|
|
23
27
|
[34mDTS[39m Build start
|
|
24
|
-
[32mDTS[39m ⚡️ Build success in
|
|
25
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[
|
|
26
|
-
[32mDTS[39m [1mdist/contracts/index.d.ts [22m[
|
|
27
|
-
[32mDTS[39m [1mdist/index.d.cts [22m[
|
|
28
|
-
[32mDTS[39m [1mdist/contracts/index.d.cts [22m[
|
|
28
|
+
[32mDTS[39m ⚡️ Build success in 18283ms
|
|
29
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m28.97 KB[39m
|
|
30
|
+
[32mDTS[39m [1mdist/contracts/index.d.ts [22m[32m11.03 KB[39m
|
|
31
|
+
[32mDTS[39m [1mdist/index.d.cts [22m[32m28.97 KB[39m
|
|
32
|
+
[32mDTS[39m [1mdist/contracts/index.d.cts [22m[32m11.03 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,93 @@
|
|
|
1
1
|
# @objectstack/service-external-datasource
|
|
2
2
|
|
|
3
|
+
## 10.2.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [b496498]
|
|
8
|
+
- @objectstack/spec@10.2.0
|
|
9
|
+
- @objectstack/core@10.2.0
|
|
10
|
+
|
|
11
|
+
## 10.1.0
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- 49da36e: feat(datasource): fail-closed credential resolution at connect (ADR-0062 Phase 2, D3)
|
|
16
|
+
|
|
17
|
+
`DatasourceConnectionService` now treats a declared `external.credentialsRef` as
|
|
18
|
+
**fail-closed**: the credential must resolve to a cleartext secret (via the
|
|
19
|
+
host's `SecretBinder` over `ICryptoProvider`) _before_ the driver is built. An
|
|
20
|
+
absent secret store, or a ref that cannot be resolved/decrypted (missing
|
|
21
|
+
`sys_secret` row, rotated key, or a throwing resolver), leaves the datasource
|
|
22
|
+
**unconnected with a clear message** — never a silent build-without-secret that
|
|
23
|
+
would connect with no/wrong auth or fail later with a confusing driver error.
|
|
24
|
+
|
|
25
|
+
The same policy as connect failures applies: a code-defined `external` datasource
|
|
26
|
+
with `validation.onMismatch: 'fail'` auto-connected at boot fails fast (bricks
|
|
27
|
+
boot); runtime-admin create/update + boot rehydration degrade-with-warning. Code-
|
|
28
|
+
and runtime-origin secrets converge on the one connection path (the same
|
|
29
|
+
`SecretBinder` is threaded through the shared service). New `failed-credentials`
|
|
30
|
+
connect status.
|
|
31
|
+
|
|
32
|
+
- ac79f16: feat(datasource): auto-connect declared external datasources (ADR-0062 Phase 1, D1/D2/D5)
|
|
33
|
+
|
|
34
|
+
A declared external datasource is now connected to a live ObjectQL driver and its
|
|
35
|
+
federated objects are queryable **with zero app code** — no `onEnable` driver
|
|
36
|
+
wiring. Implements ADR-0062 Phase 1.
|
|
37
|
+
|
|
38
|
+
- **D1 — one connect path.** New `DatasourceConnectionService` in
|
|
39
|
+
`@objectstack/service-datasource` owns the single "definition → live driver"
|
|
40
|
+
path: build via the injected driver factory → resolve `external.credentialsRef`
|
|
41
|
+
via the `SecretBinder` → connect → `engine.registerDriver` under the datasource
|
|
42
|
+
name → register the datasource def → sync each bound federated object's read
|
|
43
|
+
metadata (DDL-free). Both origins converge on it: the runtime-admin
|
|
44
|
+
`registerPool` now delegates here, and `AppPlugin` auto-connects code-defined
|
|
45
|
+
datasources. Exposed as the `'datasource-connection'` kernel service.
|
|
46
|
+
- **D2 — opt-in-safe gate.** A declared datasource auto-connects only when it is
|
|
47
|
+
`external`, an object **explicitly** binds to it via `object.datasource`, or it
|
|
48
|
+
sets the new `autoConnect: true` flag. A managed datasource that nothing
|
|
49
|
+
explicitly binds (incl. ones referenced only by a `datasourceMapping` rule, e.g.
|
|
50
|
+
`examples/app-crm`'s `:memory:` datasources) stays metadata-only — existing apps
|
|
51
|
+
are byte-for-byte unchanged. See the ADR-0062 D2 implementation note.
|
|
52
|
+
- **D5 — lifecycle, ordering & policy.** Connect happens in `AppPlugin.start()`
|
|
53
|
+
(before the `kernel:ready` validation gate, relying on the kernel's
|
|
54
|
+
init-all-then-start-all ordering). Fail-fast for a declared `external` datasource
|
|
55
|
+
with `validation.onMismatch: 'fail'`; degrade-with-warning otherwise (and always
|
|
56
|
+
for runtime-admin/rehydrate, so a UI action or replica blip never bricks the
|
|
57
|
+
server). Adds a host-injectable `DatasourceConnectPolicy` (open-core default
|
|
58
|
+
allows; a multi-tenant host binds a stricter fail-closed policy for egress
|
|
59
|
+
isolation) consulted before every connect — one connect path, no cloud fork.
|
|
60
|
+
|
|
61
|
+
Adds `datasource.autoConnect` to the spec. The legacy `onEnable` +
|
|
62
|
+
`ctx.drivers.register` bridge remains supported as an escape hatch (idempotent vs.
|
|
63
|
+
auto-connect). No behavior change for managed apps.
|
|
64
|
+
|
|
65
|
+
### Patch Changes
|
|
66
|
+
|
|
67
|
+
- Updated dependencies [49da36e]
|
|
68
|
+
- Updated dependencies [ac79f16]
|
|
69
|
+
- @objectstack/spec@10.1.0
|
|
70
|
+
- @objectstack/core@10.1.0
|
|
71
|
+
|
|
72
|
+
## 10.0.0
|
|
73
|
+
|
|
74
|
+
### Patch Changes
|
|
75
|
+
|
|
76
|
+
- Updated dependencies [d7ff626]
|
|
77
|
+
- Updated dependencies [2a1b16b]
|
|
78
|
+
- Updated dependencies [e16f2a8]
|
|
79
|
+
- Updated dependencies [e411a82]
|
|
80
|
+
- Updated dependencies [a581385]
|
|
81
|
+
- Updated dependencies [d5f6d29]
|
|
82
|
+
- Updated dependencies [220ce5b]
|
|
83
|
+
- Updated dependencies [3efe334]
|
|
84
|
+
- Updated dependencies [feead7e]
|
|
85
|
+
- Updated dependencies [6ca20b3]
|
|
86
|
+
- Updated dependencies [5f875fe]
|
|
87
|
+
- Updated dependencies [b469950]
|
|
88
|
+
- @objectstack/spec@10.0.0
|
|
89
|
+
- @objectstack/core@10.0.0
|
|
90
|
+
|
|
3
91
|
## 9.11.0
|
|
4
92
|
|
|
5
93
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -33,6 +33,40 @@ layered on by a private host **without forking**.
|
|
|
33
33
|
|
|
34
34
|
The runtime admin owns only the `origin: 'runtime'` lifecycle.
|
|
35
35
|
|
|
36
|
+
## REST routes
|
|
37
|
+
|
|
38
|
+
Mounted under `/api/v1/datasources` by `registerDatasourceAdminRoutes` (lifecycle
|
|
39
|
+
+ introspection) and the federation routes by the external service. Every route
|
|
40
|
+
degrades gracefully (`503` / "unavailable") when its service isn't wired.
|
|
41
|
+
|
|
42
|
+
**Lifecycle & connection**
|
|
43
|
+
- `GET /datasources` — list (code + runtime, with provenance/health)
|
|
44
|
+
- `GET /datasources/drivers` — driver catalog + per-driver JSON-Schema `configSchema` (drives the Studio connection form)
|
|
45
|
+
- `GET /datasources/:name` — one datasource's detail, **credential-stripped** (`config` + `hasSecret`)
|
|
46
|
+
- `POST /datasources` — create a runtime datasource
|
|
47
|
+
- `PATCH /datasources/:name` — update a runtime datasource
|
|
48
|
+
- `DELETE /datasources/:name` — remove a runtime datasource (blocked while objects are bound)
|
|
49
|
+
- `POST /datasources/test` — probe an unsaved draft (inline body)
|
|
50
|
+
- `POST /datasources/:name/test` — probe a **saved** datasource by name (backs the `test_connection` action)
|
|
51
|
+
|
|
52
|
+
**Introspection / sync (read-only)**
|
|
53
|
+
- `GET /datasources/:name/remote-tables` — list remote tables
|
|
54
|
+
- `POST /datasources/:name/object-draft` — generate an object definition draft for one table (no persistence)
|
|
55
|
+
- federation import/validate/refresh routes under `/datasources/:name/external/*` (ADR-0015)
|
|
56
|
+
|
|
57
|
+
Credentials never travel in `config`: a `format:'password'` field is split into
|
|
58
|
+
the top-level `secret` on write (encrypted to `sys_secret`), and reads never
|
|
59
|
+
echo it (`hasSecret` only).
|
|
60
|
+
|
|
61
|
+
## Studio integration
|
|
62
|
+
|
|
63
|
+
`datasource` is a metadata type, so it is administered through the generic
|
|
64
|
+
metadata-admin engine (not a bespoke page): the `DatasourceAdminServicePlugin`
|
|
65
|
+
contributes a **Datasources** entry to the Setup app's `group_integrations` nav
|
|
66
|
+
slot (→ the engine route `…/component/metadata/resource?type=datasource`), where
|
|
67
|
+
the list/create/edit/test/**sync objects**/delete UI lives. Runtime records are
|
|
68
|
+
persisted to `sys_metadata` and restored (pools rehydrated) on restart.
|
|
69
|
+
|
|
36
70
|
## Exports
|
|
37
71
|
|
|
38
72
|
- Federation: `ExternalDatasourceService`, `ExternalDatasourceServicePlugin`.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});// src/contracts/connect-policy.ts
|
|
2
|
+
var allowAllConnectPolicy = {
|
|
3
|
+
canConnect: () => ({ allow: true })
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
exports.allowAllConnectPolicy = allowAllConnectPolicy;
|
|
9
|
+
//# sourceMappingURL=chunk-BI2SYWLC.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/framework/framework/packages/services/service-datasource/dist/chunk-BI2SYWLC.cjs","../src/contracts/connect-policy.ts"],"names":[],"mappings":"AAAA;ACkEO,IAAM,sBAAA,EAAiD;AAAA,EAC5D,UAAA,EAAY,CAAA,EAAA,GAAA,CAAO,EAAE,KAAA,EAAO,KAAK,CAAA;AACnC,CAAA;ADhEA;AACA;AACE;AACF,sDAAC","file":"/home/runner/work/framework/framework/packages/services/service-datasource/dist/chunk-BI2SYWLC.cjs","sourcesContent":[null,"// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * DatasourceConnectPolicy — host-injectable gate consulted *before*\n * {@link DatasourceConnectionService} builds and registers a live driver\n * (ADR-0062 D1/D5, and the epic #2163 \"connect-policy seam\" note).\n *\n * The framework ships a permissive default ({@link allowAllConnectPolicy}) so a\n * self-hosted single-environment runtime connects external datasources out of\n * the box (subject to the D2 auto-connect gate, which is applied separately by\n * {@link DatasourceConnectionService.connectDeclared}). A multi-tenant host\n * (shared-container cloud) binds a stricter policy that can *fail-close* on the\n * shared runtime — e.g. checking `sys_environment.plan`, an egress allow-list,\n * and per-tenant quota — to enforce SSRF / egress isolation.\n *\n * This keeps a single connect path for code- and runtime-origin datasources\n * (D1): the host injects a policy rather than forking a second connect path.\n * No plan coupling lives in the open framework.\n */\n\n/** Why a connect is being attempted — lets a policy treat origins differently. */\nexport interface DatasourceConnectContext {\n /** Provenance of the datasource being connected. */\n origin?: 'code' | 'runtime';\n /**\n * What triggered this connect:\n * - `declared-auto` — code-defined datasource auto-connected at boot (D2 gate passed).\n * - `runtime-admin` — UI \"Add/Update Datasource\" hot pool registration.\n * - `rehydrate` — boot rehydration of a persisted runtime datasource.\n */\n trigger?: 'declared-auto' | 'runtime-admin' | 'rehydrate';\n}\n\n/** A policy verdict. `allow:false` leaves the datasource unconnected (metadata-only). */\nexport interface DatasourceConnectDecision {\n allow: boolean;\n /** Human-readable reason, surfaced in logs when a connect is denied. */\n reason?: string;\n}\n\n/** The minimal datasource shape a policy inspects (never a secret). */\nexport interface DatasourceConnectSubject {\n name: string;\n driver: string;\n schemaMode?: 'managed' | 'external' | 'validate-only';\n external?: Record<string, unknown>;\n}\n\n/** Host-provided policy gate consulted before opening a connection. */\nexport interface DatasourceConnectPolicy {\n /**\n * Decide whether `ds` may be connected. Sync or async. Throwing is treated\n * as a denial (fail-closed) by {@link DatasourceConnectionService}.\n */\n canConnect(\n ds: DatasourceConnectSubject,\n ctx?: DatasourceConnectContext,\n ): DatasourceConnectDecision | Promise<DatasourceConnectDecision>;\n}\n\n/**\n * Open-core default: allow every connect. The D2 auto-connect gate (external /\n * explicitly-routed / `autoConnect:true`) still applies on top of this for\n * code-defined datasources, so a managed, unrouted datasource is never\n * connected even under the permissive policy.\n */\nexport const allowAllConnectPolicy: DatasourceConnectPolicy = {\n canConnect: () => ({ allow: true }),\n};\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/contracts/connect-policy.ts"],"sourcesContent":["// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * DatasourceConnectPolicy — host-injectable gate consulted *before*\n * {@link DatasourceConnectionService} builds and registers a live driver\n * (ADR-0062 D1/D5, and the epic #2163 \"connect-policy seam\" note).\n *\n * The framework ships a permissive default ({@link allowAllConnectPolicy}) so a\n * self-hosted single-environment runtime connects external datasources out of\n * the box (subject to the D2 auto-connect gate, which is applied separately by\n * {@link DatasourceConnectionService.connectDeclared}). A multi-tenant host\n * (shared-container cloud) binds a stricter policy that can *fail-close* on the\n * shared runtime — e.g. checking `sys_environment.plan`, an egress allow-list,\n * and per-tenant quota — to enforce SSRF / egress isolation.\n *\n * This keeps a single connect path for code- and runtime-origin datasources\n * (D1): the host injects a policy rather than forking a second connect path.\n * No plan coupling lives in the open framework.\n */\n\n/** Why a connect is being attempted — lets a policy treat origins differently. */\nexport interface DatasourceConnectContext {\n /** Provenance of the datasource being connected. */\n origin?: 'code' | 'runtime';\n /**\n * What triggered this connect:\n * - `declared-auto` — code-defined datasource auto-connected at boot (D2 gate passed).\n * - `runtime-admin` — UI \"Add/Update Datasource\" hot pool registration.\n * - `rehydrate` — boot rehydration of a persisted runtime datasource.\n */\n trigger?: 'declared-auto' | 'runtime-admin' | 'rehydrate';\n}\n\n/** A policy verdict. `allow:false` leaves the datasource unconnected (metadata-only). */\nexport interface DatasourceConnectDecision {\n allow: boolean;\n /** Human-readable reason, surfaced in logs when a connect is denied. */\n reason?: string;\n}\n\n/** The minimal datasource shape a policy inspects (never a secret). */\nexport interface DatasourceConnectSubject {\n name: string;\n driver: string;\n schemaMode?: 'managed' | 'external' | 'validate-only';\n external?: Record<string, unknown>;\n}\n\n/** Host-provided policy gate consulted before opening a connection. */\nexport interface DatasourceConnectPolicy {\n /**\n * Decide whether `ds` may be connected. Sync or async. Throwing is treated\n * as a denial (fail-closed) by {@link DatasourceConnectionService}.\n */\n canConnect(\n ds: DatasourceConnectSubject,\n ctx?: DatasourceConnectContext,\n ): DatasourceConnectDecision | Promise<DatasourceConnectDecision>;\n}\n\n/**\n * Open-core default: allow every connect. The D2 auto-connect gate (external /\n * explicitly-routed / `autoConnect:true`) still applies on top of this for\n * code-defined datasources, so a managed, unrouted datasource is never\n * connected even under the permissive policy.\n */\nexport const allowAllConnectPolicy: DatasourceConnectPolicy = {\n canConnect: () => ({ allow: true }),\n};\n"],"mappings":";AAkEO,IAAM,wBAAiD;AAAA,EAC5D,YAAY,OAAO,EAAE,OAAO,KAAK;AACnC;","names":[]}
|
package/dist/contracts/index.cjs
CHANGED
|
@@ -1 +1,7 @@
|
|
|
1
|
-
"use strict"
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
2
|
+
|
|
3
|
+
var _chunkBI2SYWLCcjs = require('../chunk-BI2SYWLC.cjs');
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
exports.allowAllConnectPolicy = _chunkBI2SYWLCcjs.allowAllConnectPolicy;
|
|
7
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/framework/framework/packages/services/service-datasource/dist/contracts/index.cjs"],"names":[],"mappings":"AAAA","file":"/home/runner/work/framework/framework/packages/services/service-datasource/dist/contracts/index.cjs"}
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/framework/framework/packages/services/service-datasource/dist/contracts/index.cjs"],"names":[],"mappings":"AAAA;AACE;AACF,yDAA8B;AAC9B;AACE;AACF,wEAAC","file":"/home/runner/work/framework/framework/packages/services/service-datasource/dist/contracts/index.cjs"}
|
|
@@ -175,4 +175,62 @@ interface IDatasourceDriverFactory {
|
|
|
175
175
|
create(spec: DatasourceConnectionSpec): Promise<DatasourceDriverHandle> | DatasourceDriverHandle;
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
-
|
|
178
|
+
/**
|
|
179
|
+
* DatasourceConnectPolicy — host-injectable gate consulted *before*
|
|
180
|
+
* {@link DatasourceConnectionService} builds and registers a live driver
|
|
181
|
+
* (ADR-0062 D1/D5, and the epic #2163 "connect-policy seam" note).
|
|
182
|
+
*
|
|
183
|
+
* The framework ships a permissive default ({@link allowAllConnectPolicy}) so a
|
|
184
|
+
* self-hosted single-environment runtime connects external datasources out of
|
|
185
|
+
* the box (subject to the D2 auto-connect gate, which is applied separately by
|
|
186
|
+
* {@link DatasourceConnectionService.connectDeclared}). A multi-tenant host
|
|
187
|
+
* (shared-container cloud) binds a stricter policy that can *fail-close* on the
|
|
188
|
+
* shared runtime — e.g. checking `sys_environment.plan`, an egress allow-list,
|
|
189
|
+
* and per-tenant quota — to enforce SSRF / egress isolation.
|
|
190
|
+
*
|
|
191
|
+
* This keeps a single connect path for code- and runtime-origin datasources
|
|
192
|
+
* (D1): the host injects a policy rather than forking a second connect path.
|
|
193
|
+
* No plan coupling lives in the open framework.
|
|
194
|
+
*/
|
|
195
|
+
/** Why a connect is being attempted — lets a policy treat origins differently. */
|
|
196
|
+
interface DatasourceConnectContext {
|
|
197
|
+
/** Provenance of the datasource being connected. */
|
|
198
|
+
origin?: 'code' | 'runtime';
|
|
199
|
+
/**
|
|
200
|
+
* What triggered this connect:
|
|
201
|
+
* - `declared-auto` — code-defined datasource auto-connected at boot (D2 gate passed).
|
|
202
|
+
* - `runtime-admin` — UI "Add/Update Datasource" hot pool registration.
|
|
203
|
+
* - `rehydrate` — boot rehydration of a persisted runtime datasource.
|
|
204
|
+
*/
|
|
205
|
+
trigger?: 'declared-auto' | 'runtime-admin' | 'rehydrate';
|
|
206
|
+
}
|
|
207
|
+
/** A policy verdict. `allow:false` leaves the datasource unconnected (metadata-only). */
|
|
208
|
+
interface DatasourceConnectDecision {
|
|
209
|
+
allow: boolean;
|
|
210
|
+
/** Human-readable reason, surfaced in logs when a connect is denied. */
|
|
211
|
+
reason?: string;
|
|
212
|
+
}
|
|
213
|
+
/** The minimal datasource shape a policy inspects (never a secret). */
|
|
214
|
+
interface DatasourceConnectSubject {
|
|
215
|
+
name: string;
|
|
216
|
+
driver: string;
|
|
217
|
+
schemaMode?: 'managed' | 'external' | 'validate-only';
|
|
218
|
+
external?: Record<string, unknown>;
|
|
219
|
+
}
|
|
220
|
+
/** Host-provided policy gate consulted before opening a connection. */
|
|
221
|
+
interface DatasourceConnectPolicy {
|
|
222
|
+
/**
|
|
223
|
+
* Decide whether `ds` may be connected. Sync or async. Throwing is treated
|
|
224
|
+
* as a denial (fail-closed) by {@link DatasourceConnectionService}.
|
|
225
|
+
*/
|
|
226
|
+
canConnect(ds: DatasourceConnectSubject, ctx?: DatasourceConnectContext): DatasourceConnectDecision | Promise<DatasourceConnectDecision>;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Open-core default: allow every connect. The D2 auto-connect gate (external /
|
|
230
|
+
* explicitly-routed / `autoConnect:true`) still applies on top of this for
|
|
231
|
+
* code-defined datasources, so a managed, unrouted datasource is never
|
|
232
|
+
* connected even under the permissive policy.
|
|
233
|
+
*/
|
|
234
|
+
declare const allowAllConnectPolicy: DatasourceConnectPolicy;
|
|
235
|
+
|
|
236
|
+
export { type DatasourceConnectContext, type DatasourceConnectDecision, type DatasourceConnectPolicy, type DatasourceConnectSubject, type DatasourceConnectionSpec, type DatasourceDraft, type DatasourceDriverHandle, type DatasourceOrigin, type DatasourceSummary, type IDatasourceAdminService, type IDatasourceDriverFactory, type SecretInput, type TestConnectionResult, allowAllConnectPolicy };
|
|
@@ -175,4 +175,62 @@ interface IDatasourceDriverFactory {
|
|
|
175
175
|
create(spec: DatasourceConnectionSpec): Promise<DatasourceDriverHandle> | DatasourceDriverHandle;
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
-
|
|
178
|
+
/**
|
|
179
|
+
* DatasourceConnectPolicy — host-injectable gate consulted *before*
|
|
180
|
+
* {@link DatasourceConnectionService} builds and registers a live driver
|
|
181
|
+
* (ADR-0062 D1/D5, and the epic #2163 "connect-policy seam" note).
|
|
182
|
+
*
|
|
183
|
+
* The framework ships a permissive default ({@link allowAllConnectPolicy}) so a
|
|
184
|
+
* self-hosted single-environment runtime connects external datasources out of
|
|
185
|
+
* the box (subject to the D2 auto-connect gate, which is applied separately by
|
|
186
|
+
* {@link DatasourceConnectionService.connectDeclared}). A multi-tenant host
|
|
187
|
+
* (shared-container cloud) binds a stricter policy that can *fail-close* on the
|
|
188
|
+
* shared runtime — e.g. checking `sys_environment.plan`, an egress allow-list,
|
|
189
|
+
* and per-tenant quota — to enforce SSRF / egress isolation.
|
|
190
|
+
*
|
|
191
|
+
* This keeps a single connect path for code- and runtime-origin datasources
|
|
192
|
+
* (D1): the host injects a policy rather than forking a second connect path.
|
|
193
|
+
* No plan coupling lives in the open framework.
|
|
194
|
+
*/
|
|
195
|
+
/** Why a connect is being attempted — lets a policy treat origins differently. */
|
|
196
|
+
interface DatasourceConnectContext {
|
|
197
|
+
/** Provenance of the datasource being connected. */
|
|
198
|
+
origin?: 'code' | 'runtime';
|
|
199
|
+
/**
|
|
200
|
+
* What triggered this connect:
|
|
201
|
+
* - `declared-auto` — code-defined datasource auto-connected at boot (D2 gate passed).
|
|
202
|
+
* - `runtime-admin` — UI "Add/Update Datasource" hot pool registration.
|
|
203
|
+
* - `rehydrate` — boot rehydration of a persisted runtime datasource.
|
|
204
|
+
*/
|
|
205
|
+
trigger?: 'declared-auto' | 'runtime-admin' | 'rehydrate';
|
|
206
|
+
}
|
|
207
|
+
/** A policy verdict. `allow:false` leaves the datasource unconnected (metadata-only). */
|
|
208
|
+
interface DatasourceConnectDecision {
|
|
209
|
+
allow: boolean;
|
|
210
|
+
/** Human-readable reason, surfaced in logs when a connect is denied. */
|
|
211
|
+
reason?: string;
|
|
212
|
+
}
|
|
213
|
+
/** The minimal datasource shape a policy inspects (never a secret). */
|
|
214
|
+
interface DatasourceConnectSubject {
|
|
215
|
+
name: string;
|
|
216
|
+
driver: string;
|
|
217
|
+
schemaMode?: 'managed' | 'external' | 'validate-only';
|
|
218
|
+
external?: Record<string, unknown>;
|
|
219
|
+
}
|
|
220
|
+
/** Host-provided policy gate consulted before opening a connection. */
|
|
221
|
+
interface DatasourceConnectPolicy {
|
|
222
|
+
/**
|
|
223
|
+
* Decide whether `ds` may be connected. Sync or async. Throwing is treated
|
|
224
|
+
* as a denial (fail-closed) by {@link DatasourceConnectionService}.
|
|
225
|
+
*/
|
|
226
|
+
canConnect(ds: DatasourceConnectSubject, ctx?: DatasourceConnectContext): DatasourceConnectDecision | Promise<DatasourceConnectDecision>;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Open-core default: allow every connect. The D2 auto-connect gate (external /
|
|
230
|
+
* explicitly-routed / `autoConnect:true`) still applies on top of this for
|
|
231
|
+
* code-defined datasources, so a managed, unrouted datasource is never
|
|
232
|
+
* connected even under the permissive policy.
|
|
233
|
+
*/
|
|
234
|
+
declare const allowAllConnectPolicy: DatasourceConnectPolicy;
|
|
235
|
+
|
|
236
|
+
export { type DatasourceConnectContext, type DatasourceConnectDecision, type DatasourceConnectPolicy, type DatasourceConnectSubject, type DatasourceConnectionSpec, type DatasourceDraft, type DatasourceDriverHandle, type DatasourceOrigin, type DatasourceSummary, type IDatasourceAdminService, type IDatasourceDriverFactory, type SecretInput, type TestConnectionResult, allowAllConnectPolicy };
|