@zackbart/connecta 0.24.3 → 0.25.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/AGENTS.md +58 -30
- package/CHANGELOG.md +317 -1
- package/README.md +5 -6
- package/dist/advertised-schema.js +53 -0
- package/dist/auth/downstream-oauth.js +490 -106
- package/dist/branding.d.ts +31 -2
- package/dist/branding.js +116 -8
- package/dist/call-admission.js +139 -113
- package/dist/catalog-service.js +141 -105
- package/dist/catalog.js +258 -33
- package/dist/concurrency.js +24 -23
- package/dist/connector-scope.js +6 -37
- package/dist/connectors/api.d.ts +3 -3
- package/dist/connectors/api.js +10 -2
- package/dist/connectors/guarded-fetch.js +34 -4
- package/dist/connectors/remote-mcp.d.ts +6 -5
- package/dist/connectors/remote-mcp.js +325 -245
- package/dist/credential-contract.d.ts +9 -0
- package/dist/credential-rules.js +2 -2
- package/dist/credentials.d.ts +6 -1
- package/dist/credentials.js +45 -3
- package/dist/errors.d.ts +11 -3
- package/dist/errors.js +2 -1
- package/dist/execute.js +363 -257
- package/dist/executor-admission.js +67 -50
- package/dist/executors/quickjs-runtime.js +4 -2
- package/dist/executors/quickjs.js +251 -210
- package/dist/fix-prompt.js +37 -0
- package/dist/index.d.ts +11 -2
- package/dist/index.js +63 -21
- package/dist/invocation.js +324 -285
- package/dist/meta-tools.js +304 -114
- package/dist/oauth-callback-outcome.js +94 -0
- package/dist/oauth-sealing.js +29 -0
- package/dist/operator-ui/fix-prompts.js +87 -0
- package/dist/operator-ui/generated.js +2 -2
- package/dist/operator-ui/model.d.ts +46 -1
- package/dist/operator-ui/setup-commands.js +53 -0
- package/dist/operator-ui/view.js +109 -2
- package/dist/program-source.js +205 -0
- package/dist/providers/cloudflare.d.ts +14 -2
- package/dist/providers/cloudflare.js +107 -16
- package/dist/providers/linear.d.ts +26 -4
- package/dist/providers/linear.js +19 -4
- package/dist/providers/mixpanel.d.ts +16 -3
- package/dist/providers/mixpanel.js +13 -2
- package/dist/providers/notion.d.ts +8 -1
- package/dist/providers/notion.js +85 -12
- package/dist/providers/revenuecat.d.ts +30 -4
- package/dist/providers/revenuecat.js +70 -5
- package/dist/providers/stripe.d.ts +10 -3
- package/dist/providers/stripe.js +30 -4
- package/dist/providers/vercel.js +12 -2
- package/dist/registry.d.ts +13 -5
- package/dist/registry.js +317 -258
- package/dist/routes/activity.js +120 -137
- package/dist/routes/credentials.js +174 -163
- package/dist/routes/mcp.js +129 -70
- package/dist/routes/oauth-management.js +60 -71
- package/dist/routes/oauth.js +44 -16
- package/dist/routes/operator.js +82 -0
- package/dist/routes/shared.d.ts +2 -0
- package/dist/routes/ui-data.js +210 -0
- package/dist/routes/ui.js +64 -32
- package/dist/runtime/admission.js +43 -0
- package/dist/runtime/call-admission.js +67 -0
- package/dist/runtime/connector-scope.js +72 -0
- package/dist/runtime/run.js +174 -0
- package/dist/runtime/services.js +130 -0
- package/dist/runtime/storage.js +66 -0
- package/dist/server.js +111 -89
- package/dist/skills.js +1 -1
- package/dist/storage/file.js +39 -2
- package/dist/storage/memory.js +28 -16
- package/dist/timeout.js +17 -47
- package/dist/types.d.ts +64 -7
- package/dist/ui.d.ts +16 -2
- package/dist/ui.js +68 -176
- package/dist/url-safety.js +157 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/architecture.md +368 -182
- package/documentation/auth.md +277 -176
- package/documentation/code-mode.md +479 -329
- package/documentation/meta-tools.md +494 -417
- package/ethos.md +44 -47
- package/examples/worker/AGENTS.md +2 -1
- package/examples/worker/README.md +62 -14
- package/examples/worker/src/d1-storage.ts +116 -0
- package/examples/worker/src/index.ts +18 -21
- package/examples/worker/wrangler.jsonc +16 -4
- package/package.json +11 -4
- package/templates/node/.env.example +3 -3
- package/templates/node/AGENTS.md +5 -4
- package/templates/node/README.md +2 -1
- package/templates/node/package.json +1 -1
- package/templates/node/src/index.ts +23 -22
- package/dist/auth/downstream-oauth.d.ts +0 -200
- package/dist/catalog-fingerprint.d.ts +0 -19
- package/dist/catalog-limits.d.ts +0 -9
- package/dist/catalog-service.d.ts +0 -263
- package/dist/catalog.d.ts +0 -87
- package/dist/concurrency.d.ts +0 -3
- package/dist/connectors/guarded-fetch.d.ts +0 -92
- package/dist/credential-rules.d.ts +0 -85
- package/dist/execute.d.ts +0 -154
- package/dist/executor-result.d.ts +0 -10
- package/dist/executors/quickjs-child.d.ts +0 -1
- package/dist/executors/quickjs-protocol.d.ts +0 -67
- package/dist/favicon.d.ts +0 -1
- package/dist/identity.d.ts +0 -4
- package/dist/invocation.d.ts +0 -74
- package/dist/mcp-result.d.ts +0 -8
- package/dist/meta-tools.d.ts +0 -129
- package/dist/operator-ui/generated.d.ts +0 -2
- package/dist/operator-ui/view.d.ts +0 -151
- package/dist/result-shapes.d.ts +0 -13
- package/dist/retired-toolkits.d.ts +0 -2
- package/dist/routes/activity.d.ts +0 -2
- package/dist/routes/credentials.d.ts +0 -2
- package/dist/routes/mcp.d.ts +0 -10
- package/dist/routes/oauth-management.d.ts +0 -2
- package/dist/routes/oauth.d.ts +0 -2
- package/dist/routes/ui.d.ts +0 -2
- package/dist/server.d.ts +0 -9
- package/dist/skills.d.ts +0 -44
- package/dist/timeout.d.ts +0 -21
- package/dist/tool-safety.d.ts +0 -9
- package/documentation/call-admission.md +0 -158
- package/documentation/cloudflare.md +0 -471
- package/documentation/connector-guides.md +0 -176
- package/documentation/connectors.md +0 -431
- package/documentation/linear.md +0 -193
- package/documentation/mixpanel.md +0 -160
- package/documentation/notion.md +0 -308
- package/documentation/operations.md +0 -359
- package/documentation/operator-ui.md +0 -135
- package/documentation/optional-modules-upgrade.md +0 -243
- package/documentation/provider-conventions.md +0 -729
- package/documentation/request-admission.md +0 -204
- package/documentation/revenuecat.md +0 -305
- package/documentation/storage-and-credentials.md +0 -254
- package/documentation/stripe.md +0 -262
- package/documentation/upgrading.md +0 -768
- package/documentation/vercel.md +0 -241
package/ethos.md
CHANGED
|
@@ -4,92 +4,89 @@ Connecta’s scope, refusals, and invariants. Contradictions require a design de
|
|
|
4
4
|
|
|
5
5
|
## What this is
|
|
6
6
|
|
|
7
|
-
- **One MCP endpoint.** Agents reach configured connectors through JavaScript
|
|
8
|
-
and seven meta-tools
|
|
9
|
-
- **Config-as-code.** One tenant and connector set; identities
|
|
10
|
-
config-derived views. Maintained providers, `remoteMcp()`, and `api()`
|
|
11
|
-
|
|
12
|
-
- **Safe by default.**
|
|
13
|
-
|
|
14
|
-
- **One fetch-native core.** Web APIs
|
|
15
|
-
|
|
7
|
+
- **One MCP endpoint.** Agents reach configured connectors through JavaScript,
|
|
8
|
+
a required executor, and seven meta-tools, eight planned.
|
|
9
|
+
- **Config-as-code.** One tenant and connector set; identities get
|
|
10
|
+
config-derived views. Maintained providers, `remoteMcp()`, and `api()` share
|
|
11
|
+
one connector contract.
|
|
12
|
+
- **Safe by default.** Programs run only explicitly read-only tools unasked;
|
|
13
|
+
others cross `call_destructive_tool` or, planned, pause. The host owns approval.
|
|
14
|
+
- **One fetch-native core.** Web APIs on Node and Workers; platform code and
|
|
15
|
+
optional features behind explicit subpaths; Effect inside, Promises outside.
|
|
16
16
|
- **Human auth management.** Optional pages show status and payload-free history.
|
|
17
17
|
Explicit permissions allow credential changes and OAuth. Capabilities stay in code.
|
|
18
18
|
|
|
19
19
|
## What this isn't
|
|
20
20
|
|
|
21
|
-
No runtime registration, admin-editable capabilities, policy engine
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
acceptable; version numbers signal change, not stability.
|
|
21
|
+
No runtime registration, admin-editable capabilities, or policy engine; config
|
|
22
|
+
may only exempt a tool from asking. No schema ingestion, accounts, or groups;
|
|
23
|
+
personal state remains within one tenant. Breaking changes remain acceptable;
|
|
24
|
+
version numbers signal change, not stability.
|
|
26
25
|
|
|
27
26
|
## Decisions
|
|
28
27
|
|
|
29
28
|
Revisiting a verdict requires a new argument. Accepted designs live in
|
|
30
|
-
subsystem guides and the CHANGELOG
|
|
29
|
+
subsystem guides and the CHANGELOG; `planned` ones bind review before either
|
|
30
|
+
carries them.
|
|
31
31
|
|
|
32
32
|
| Decision | Verdict | Why |
|
|
33
33
|
| --- | --- | --- |
|
|
34
|
-
| OpenAPI / GraphQL ingestion | refused | the disease is a tool nobody chose
|
|
34
|
+
| OpenAPI / GraphQL ingestion | refused | the disease is a document-authored tool nobody chose; hand-written literals, even through a shared factory, are still authorship |
|
|
35
35
|
| Multi-tenancy / account model | refused | one deployment per tenant; inbound auth owns identity |
|
|
36
|
-
|
|
|
36
|
+
| Approvals and pauses | planned | a program pauses host-side at its first unapproved write until the destructive-annotated `resume_execution` repeats that exact call, the host's prompt approving it or its tool; an expiring journal replays the run, never re-sending unknown outcomes |
|
|
37
37
|
| Runtime connector registration | refused | config-as-code is the security model |
|
|
38
|
-
| Optional deployment modules | accepted |
|
|
39
|
-
|
|
|
40
|
-
|
|
|
41
|
-
|
|
|
38
|
+
| Optional deployment modules | accepted | typed slots select UI, activity, vault, and inbound auth; core keeps discovery, execution, invocation, and enforcement |
|
|
39
|
+
| Artifacts module | planned | a built-in connector, not a meta-tool: team-only sandboxed pages over stored JSON, never calling tools. Immutable versions let writes skip approval. Supersedes [#287](https://github.com/zackbart/connecta/issues/287) on executor's artifacts |
|
|
40
|
+
| Plugin lifecycle, provider registry, or marketplace | refused | modules are deployment code, not runtime installs; prebuilt connections are imports, discovered in docs ([#297](https://github.com/zackbart/connecta/issues/297)) |
|
|
41
|
+
| Connecta-issued access tokens | removed | inbound providers authenticate clients; bearer auth stays an optional adapter |
|
|
42
42
|
| Expanded Notion page create/update options | refused | different workflows, not missing fields; use `api()` ([#408](https://github.com/zackbart/connecta/issues/408)) |
|
|
43
|
-
|
|
|
44
|
-
|
|
|
45
|
-
| Elicitation passthrough | refused | no route through a stateless aggregator |
|
|
43
|
+
| Resources, prompts, and downstream MCP Apps templates | refused | tools only; clients own presentation ([#266](https://github.com/zackbart/connecta/issues/266)) |
|
|
44
|
+
| Protocol sessions, server push, elicitation passthrough | refused | stateless per request; elicitation has no route |
|
|
46
45
|
| Repository formatter | refused | style is authored, not enforced |
|
|
47
|
-
| Host-side projection of program results | refused | a program projects
|
|
48
|
-
| `get_result` paging for program results | refused | paging rewards the unprojected return code mode exists to remove ([#223](https://github.com/zackbart/connecta/issues/223)) |
|
|
46
|
+
| Host-side projection or paging of program results | refused | a program projects: heuristics drop fields invisibly, paging rewards unprojected returns ([#223](https://github.com/zackbart/connecta/issues/223)) |
|
|
49
47
|
| Native Tasks for oversized results | refused | tasks solve duration, `get_result` solves size ([#176](https://github.com/zackbart/connecta/issues/176)) |
|
|
50
48
|
| Widening the `Executor` result contract | refused | `{ result, error?, logs? }` is the `@cloudflare/codemode` parity guarantee ([#267](https://github.com/zackbart/connecta/issues/267)) |
|
|
51
|
-
| Erasable TypeScript in `execute_code` | refused | 24 MB dependency
|
|
52
|
-
| Guest-minted `resource` / `resource_link` blocks | refused |
|
|
49
|
+
| Erasable TypeScript in `execute_code` | refused | a 24 MB core dependency, no measured agent benefit ([#419](https://github.com/zackbart/connecta/issues/419)) |
|
|
50
|
+
| Guest-minted `resource` / `resource_link` blocks | refused | no program mints a URI a client may dereference ([#266](https://github.com/zackbart/connecta/issues/266)) |
|
|
53
51
|
| Provenance tracking for emitted content | refused | everything a program emits is program output ([#267](https://github.com/zackbart/connecta/issues/267)) |
|
|
54
|
-
| Result sampling on the catalog surface | refused | sampling is execution
|
|
52
|
+
| Result sampling on the catalog surface | refused | sampling is execution, not a catalog read ([#282](https://github.com/zackbart/connecta/issues/282)) |
|
|
55
53
|
| Legacy embedded `UIResource` delivery | refused | superseded upstream, rendered by no client we face ([#266](https://github.com/zackbart/connecta/issues/266)) |
|
|
56
|
-
| Effect as the core effect system |
|
|
57
|
-
| Shared bounded queue under both admission controllers | refused | built
|
|
54
|
+
| Effect as the core effect system | accepted | lifetimes held the bugs: the rewrite closed leaked permits, hung waits, and cross-request races. Overturns [#470](https://github.com/zackbart/connecta/issues/470) |
|
|
55
|
+
| Shared bounded queue under both admission controllers | refused | built: −17 lines bought a hook-parameterised abstraction ([#453](https://github.com/zackbart/connecta/issues/453)) |
|
|
58
56
|
| MCP Apps rendering and `connecta.ui` | removed | clients render returned data |
|
|
59
57
|
| Connector shortcut globals | removed | canonical addresses need no sanitization |
|
|
60
58
|
| `connecta.batch` | removed | JavaScript promises suffice |
|
|
61
59
|
| Automatic direct-call retries | removed | callers own retry timing |
|
|
62
|
-
| Connector HTTP routes | removed | deployments own custom routes |
|
|
63
|
-
| Caller-selected toolkits | removed | config derives every view
|
|
60
|
+
| Connector HTTP routes | removed | deployments own custom routes; artifact viewing is the planned exception |
|
|
61
|
+
| Caller-selected toolkits | removed | config derives every view, grant-gated `/mcp/<pool>` pools included ([#178](https://github.com/zackbart/connecta/issues/178), [#531](https://github.com/zackbart/connecta/issues/531)) |
|
|
64
62
|
| Proactive credential liveness | removed | fail-at-use is enough ([#179](https://github.com/zackbart/connecta/issues/179)) |
|
|
65
63
|
| Classic (executor-free) surface | removed | an executor is mandatory ([#273](https://github.com/zackbart/connecta/issues/273)) |
|
|
66
|
-
| Per-result lexical query coverage | removed |
|
|
67
|
-
| Stabilized workflows |
|
|
64
|
+
| Per-result lexical query coverage | removed | failed a precommitted response-bytes gate ([#323](https://github.com/zackbart/connecta/issues/323)) |
|
|
65
|
+
| Stabilized workflows | planned | only as artifact refresh: scheduled read-only programs, shared credentials ([#225](https://github.com/zackbart/connecta/issues/225)) |
|
|
68
66
|
| Semantic tool search | gated | keyword search has not been shown to fail ([#27](https://github.com/zackbart/connecta/issues/27)) |
|
|
69
67
|
| MRTR / `input_required` passthrough | gated | relayable statelessly; no host or downstream emits it yet ([#176](https://github.com/zackbart/connecta/issues/176)) |
|
|
70
68
|
| Downstream `ttlMs` cache hints | gated | needs refresh-churn evidence ([#206](https://github.com/zackbart/connecta/issues/206)) |
|
|
71
|
-
| Downstream MCP Apps template passthrough | refused | clients own presentation; Connecta serves tools and data |
|
|
72
69
|
| Worker Access inbound auth | provisional | Managed OAuth and Clerk migration need production evidence ([#506](https://github.com/zackbart/connecta/issues/506)) |
|
|
73
70
|
| Program UI tool calls | removed | duplicated calls without improving retrieval ([#287](https://github.com/zackbart/connecta/issues/287), [#484](https://github.com/zackbart/connecta/issues/484)) |
|
|
74
71
|
|
|
75
72
|
## Invariants
|
|
76
73
|
|
|
77
|
-
Tests beside subsystem documentation enforce these
|
|
78
|
-
|
|
74
|
+
Tests beside subsystem documentation enforce these; breaking one requires a
|
|
75
|
+
design decision.
|
|
79
76
|
|
|
80
77
|
- **Fail-closed read-only.** A missing, false, or contradictory annotation never gets the benefit of the doubt.
|
|
81
78
|
- **Generated code cannot mint capabilities.** Admission, credentials, and classification are enforced below the sandbox.
|
|
82
|
-
- **Only explicitly read-only work runs
|
|
83
|
-
- **Nothing request-bound survives a request.** No transport, stream, signal, or awaited promise outlives it.
|
|
84
|
-
- **A downstream catalog is complete or it is a failure.** A partial
|
|
79
|
+
- **Only explicitly read-only work runs unasked in the sandbox.** Everything else crosses `call_destructive_tool` or, planned, `resume_execution` or a config exemption.
|
|
80
|
+
- **Nothing request-bound survives a request.** No transport, stream, signal, or awaited promise outlives it; a paused journal is data, not a request.
|
|
81
|
+
- **A downstream catalog is complete or it is a failure.** A partial one is never cached, persisted, or served.
|
|
85
82
|
- **Activity is payload-free by construction.** The event type has nowhere to put arguments, results, code, or raw errors.
|
|
86
83
|
- **An observed shape is never a declaration.** Names and broad types only, labeled, and gone behind any declared schema.
|
|
87
|
-
- **Credentials never leave the host.** Encrypted at rest, readable only by the owning connector and, for personal auth,
|
|
88
|
-
- **Import-graph purity.** Nothing reachable from the root entry imports a `node:` builtin
|
|
89
|
-
- **The published surface is a boundary.** Heavyweight or platform-bound code goes behind an optional-peer subpath.
|
|
90
|
-
- **Human routes manage auth, never capability.** Visibility grants use, not
|
|
91
|
-
- **Omitted modules do no work.** Core imports no UI bundle,
|
|
92
|
-
- **Status reads do not start authorization.**
|
|
84
|
+
- **Credentials never leave the host.** Encrypted at rest, readable only by the owning connector (and principal, for personal auth), rendered by nothing.
|
|
85
|
+
- **Import-graph purity.** Nothing reachable from the root entry imports a `node:` builtin or `effect/testing`.
|
|
86
|
+
- **The published surface is a boundary.** Heavyweight or platform-bound code goes behind an optional-peer subpath; Effect is the one hard dependency, named by no published type.
|
|
87
|
+
- **Human routes manage auth, never capability.** Visibility grants use, not administration. Shared and personal auth mutations need separate config-derived permissions, both denied by default, and activity reads their own. Planned: viewing artifacts.
|
|
88
|
+
- **Omitted modules do no work.** Core imports no UI bundle, vault, activity, or bearer implementation. OAuth callbacks work without UI.
|
|
89
|
+
- **Status reads do not start authorization.** Only an explicit authorized action starts OAuth, never a page load.
|
|
93
90
|
- **Structural mistakes throw at construction.** Booting into the wrong shape is worse than not booting.
|
|
94
91
|
|
|
95
92
|
Connecta simplifies [executor](https://github.com/UsefulSoftwareCo/executor).
|
|
@@ -37,7 +37,8 @@ This repository is deployment configuration, not a copy of Connecta itself.
|
|
|
37
37
|
A configured Connecta bearer cannot cross the Access edge alone. Connecta-issued
|
|
38
38
|
tokens are removed. UI, vault, and activity use explicit optional imports;
|
|
39
39
|
auth changes require code-derived shared or personal management permissions.
|
|
40
|
-
-
|
|
40
|
+
- After configuration changes, typecheck: inside the connecta repository that
|
|
41
|
+
is `npm run check:examples`; a copied deployment runs its own `tsc --noEmit`.
|
|
41
42
|
After deployment, connect both Claude and ChatGPT to `<PUBLIC_URL>/mcp` and
|
|
42
43
|
complete their browser authorization flows before calling setup complete.
|
|
43
44
|
|
|
@@ -8,7 +8,7 @@ Workers Paid plan.
|
|
|
8
8
|
This is also the **starting template for a deployment**: a real deployment
|
|
9
9
|
should be its own repository that pins an exact `@zackbart/connecta` version and
|
|
10
10
|
owns only its connector configuration, auth policy, domain, bindings,
|
|
11
|
-
migrations, and secrets.
|
|
11
|
+
migrations, and secrets.
|
|
12
12
|
|
|
13
13
|
## Files
|
|
14
14
|
|
|
@@ -16,15 +16,18 @@ migrations, and secrets. See [the Cloudflare guide](../../documentation/cloudfla
|
|
|
16
16
|
| --- | --- |
|
|
17
17
|
| `src/index.ts` | the Worker entrypoint — connector and auth configuration |
|
|
18
18
|
| `src/cloudflare-kv.ts` | `KVStorage` over Workers KV (deployment-owned, not a package export) |
|
|
19
|
+
| `src/d1-storage.ts` | `KVStorage` over D1 with atomic compare-and-set (optional; see below) |
|
|
19
20
|
| `src/d1-activity.ts` | `ActivityStore` over D1 (deployment-owned; see below) |
|
|
21
|
+
| `src/d1-activity-row.ts` | the row ↔ event mapping `d1-activity.ts` uses, including friction derived from `error_code` for rows written before that column existed |
|
|
20
22
|
| `wrangler.jsonc` | Worker name, vars, bindings, `compatibility_flags` |
|
|
21
23
|
|
|
22
|
-
`cloudflare-kv.ts` and `d1-activity.ts` deliberately live
|
|
23
|
-
the package: storage backends are deployment-owned, so the
|
|
24
|
-
the generic `KVStorage` and `ActivityStore` contracts.
|
|
25
|
-
consistent across locations; use a strongly consistent
|
|
26
|
-
|
|
27
|
-
become globally visible
|
|
24
|
+
`cloudflare-kv.ts`, `d1-storage.ts`, and `d1-activity.ts` deliberately live
|
|
25
|
+
here rather than in the package: storage backends are deployment-owned, so the
|
|
26
|
+
package ships only the generic `KVStorage` and `ActivityStore` contracts.
|
|
27
|
+
Workers KV is eventually consistent across locations; use a strongly consistent
|
|
28
|
+
`KVStorage` adapter, such as [`d1-storage.ts`](#strongly-consistent-storage-optional),
|
|
29
|
+
when OAuth disconnect or credential rotation must become globally visible
|
|
30
|
+
immediately.
|
|
28
31
|
|
|
29
32
|
## Deploy
|
|
30
33
|
|
|
@@ -144,9 +147,12 @@ See [inbound identity](../../documentation/auth.md#principals-visibility-and-ope
|
|
|
144
147
|
### UI and encrypted credentials
|
|
145
148
|
|
|
146
149
|
Import `operatorUi` from `@zackbart/connecta/ui` and set `ui: operatorUi()`.
|
|
147
|
-
Branding belongs in `operatorUi({ branding })
|
|
148
|
-
|
|
149
|
-
|
|
150
|
+
Branding belongs in `operatorUi({ branding })`, including `branding.theme`:
|
|
151
|
+
`accent`, `radius`, `fontFamily`, `monoFamily`, and `colorScheme`. Every other
|
|
152
|
+
color is mixed from those, so one accent themes the whole page. A value that
|
|
153
|
+
fails its gate falls back to the default, and the startup warning names it. Omit
|
|
154
|
+
the import and option to serve no UI routes; OAuth callbacks still work in core
|
|
155
|
+
for authorized interactive MCP callers.
|
|
150
156
|
|
|
151
157
|
Import `encryptedCredentialVault` from `@zackbart/connecta/credentials` and set
|
|
152
158
|
`vault: encryptedCredentialVault(storage, env.CREDENTIAL_ENCRYPTION_KEY)` when
|
|
@@ -172,8 +178,7 @@ does not create credentials or permissions by itself.
|
|
|
172
178
|
Interactive MCP clients use Access Managed OAuth. Unattended clients use Access
|
|
173
179
|
service tokens when needed. Connecta-issued `cta_` tokens and their management
|
|
174
180
|
routes are removed; a configured Connecta bearer cannot cross the Access edge
|
|
175
|
-
alone.
|
|
176
|
-
if an older deployment still issues tokens.
|
|
181
|
+
alone.
|
|
177
182
|
|
|
178
183
|
Activity uses `activityHistory({ store: d1ActivityStore(env.ACTIVITY_DB) })`
|
|
179
184
|
from `@zackbart/connecta/activity`. Enable the database and bindings described
|
|
@@ -211,6 +216,50 @@ serves the seven-tool surface. Do not add `bindings`, `modules`, or
|
|
|
211
216
|
owns the package install — see
|
|
212
217
|
[copied into its own repository](#copied-into-its-own-repository).
|
|
213
218
|
|
|
219
|
+
## Strongly consistent storage (optional)
|
|
220
|
+
|
|
221
|
+
`KVStorage` has an optional atomic `compareAndSet`, for a subsystem that must
|
|
222
|
+
claim a key exactly once. **Workers KV cannot provide it.** It is eventually
|
|
223
|
+
consistent: two locations can each read a key as absent and both write, so
|
|
224
|
+
`cloudflare-kv.ts` declares no `compareAndSet` rather than fake one with a read
|
|
225
|
+
followed by a write. `src/d1-storage.ts` is a complete `KVStorage` over D1
|
|
226
|
+
that does provide it, one SQL statement per claim, with TTLs honored at read
|
|
227
|
+
time. It replaces the KV namespace rather than sitting beside it: every piece
|
|
228
|
+
of connecta state lives in one store.
|
|
229
|
+
|
|
230
|
+
1. Create the database and uncomment the `STORAGE_DB` entry of the
|
|
231
|
+
`d1_databases` binding in `wrangler.jsonc`, pasting in the id it prints:
|
|
232
|
+
|
|
233
|
+
```sh
|
|
234
|
+
wrangler d1 create connecta-storage
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
2. Apply the schema (keep it in a deployment-owned `migrations/` directory):
|
|
238
|
+
|
|
239
|
+
```sql
|
|
240
|
+
CREATE TABLE IF NOT EXISTS connecta_kv (
|
|
241
|
+
key TEXT PRIMARY KEY,
|
|
242
|
+
value TEXT NOT NULL,
|
|
243
|
+
expires_at_ms INTEGER
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
CREATE INDEX IF NOT EXISTS connecta_kv_expiry
|
|
247
|
+
ON connecta_kv (expires_at_ms);
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
3. In `src/index.ts`, add `STORAGE_DB: D1Database` to `Env` and build the store
|
|
251
|
+
from it:
|
|
252
|
+
|
|
253
|
+
```ts
|
|
254
|
+
import { d1Storage } from "./d1-storage.js";
|
|
255
|
+
|
|
256
|
+
const storage = d1Storage(env.STORAGE_DB);
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Switching an existing deployment starts from empty state: downstream OAuth
|
|
260
|
+
connections must be re-authorized and vault credentials re-entered, because
|
|
261
|
+
nothing copies them out of the KV namespace.
|
|
262
|
+
|
|
214
263
|
## Activity history (optional)
|
|
215
264
|
|
|
216
265
|
`src/d1-activity.ts` is a complete `ActivityStore` over D1 — keyset paging on
|
|
@@ -295,8 +344,7 @@ To enable it:
|
|
|
295
344
|
});
|
|
296
345
|
```
|
|
297
346
|
|
|
298
|
-
Events carry no arguments, results, generated code, or raw error messages
|
|
299
|
-
[activity history](../../documentation/operator-ui.md).
|
|
347
|
+
Events carry no arguments, results, generated code, or raw error messages.
|
|
300
348
|
The Worker entrypoint already forwards `ctx` to `connecta.fetch`, which lets
|
|
301
349
|
async activity writes settle on `waitUntil`.
|
|
302
350
|
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import type { KVStorage } from "@zackbart/connecta";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* KVStorage over a D1 database, with the atomic `compareAndSet` that Workers
|
|
5
|
+
* KV cannot offer. Apply the `connecta_kv` schema in README.md § "Strongly
|
|
6
|
+
* consistent storage" before first use.
|
|
7
|
+
*
|
|
8
|
+
* Every compare-and-set is a single SQL statement. D1 runs one database's
|
|
9
|
+
* statements one at a time against its primary, so the comparison and the
|
|
10
|
+
* write cannot interleave with another request's: of fifty concurrent claims
|
|
11
|
+
* on an absent key, exactly one changes a row.
|
|
12
|
+
*
|
|
13
|
+
* Expiry is judged by the Worker's clock at each call, the same `Date.now()`
|
|
14
|
+
* the core uses. An expired row reads as absent immediately and is removed
|
|
15
|
+
* physically by later writes, a bounded batch at a time, so no request starts
|
|
16
|
+
* a background job and no cron is required.
|
|
17
|
+
*/
|
|
18
|
+
export function d1Storage(db: D1Database): KVStorage {
|
|
19
|
+
// Every statement that tests liveness binds the current time as ?2.
|
|
20
|
+
const live = "(expires_at_ms IS NULL OR expires_at_ms > ?2)";
|
|
21
|
+
const expiresAt = (now: number, ttlSeconds?: number) =>
|
|
22
|
+
ttlSeconds ? now + ttlSeconds * 1000 : null;
|
|
23
|
+
const changed = (result: D1Result) => result.meta.changes > 0;
|
|
24
|
+
return {
|
|
25
|
+
async get(key) {
|
|
26
|
+
const row = await db
|
|
27
|
+
.prepare(`SELECT value FROM connecta_kv WHERE key = ?1 AND ${live}`)
|
|
28
|
+
.bind(key, Date.now())
|
|
29
|
+
.first<{ value: string }>();
|
|
30
|
+
return row?.value ?? null;
|
|
31
|
+
},
|
|
32
|
+
async set(key, value, opts) {
|
|
33
|
+
const now = Date.now();
|
|
34
|
+
await db.batch([
|
|
35
|
+
db
|
|
36
|
+
.prepare(
|
|
37
|
+
`DELETE FROM connecta_kv WHERE key IN (
|
|
38
|
+
SELECT key FROM connecta_kv WHERE expires_at_ms <= ?1 LIMIT 16
|
|
39
|
+
)`,
|
|
40
|
+
)
|
|
41
|
+
.bind(now),
|
|
42
|
+
db
|
|
43
|
+
.prepare(
|
|
44
|
+
`INSERT INTO connecta_kv (key, value, expires_at_ms)
|
|
45
|
+
VALUES (?1, ?2, ?3)
|
|
46
|
+
ON CONFLICT (key) DO UPDATE SET
|
|
47
|
+
value = excluded.value, expires_at_ms = excluded.expires_at_ms`,
|
|
48
|
+
)
|
|
49
|
+
.bind(key, value, expiresAt(now, opts?.ttlSeconds)),
|
|
50
|
+
]);
|
|
51
|
+
},
|
|
52
|
+
async delete(key) {
|
|
53
|
+
await db.prepare("DELETE FROM connecta_kv WHERE key = ?1").bind(key).run();
|
|
54
|
+
},
|
|
55
|
+
async list(prefix) {
|
|
56
|
+
const { results } = await db
|
|
57
|
+
.prepare(
|
|
58
|
+
`SELECT key FROM connecta_kv
|
|
59
|
+
WHERE key >= ?1 AND substr(key, 1, length(?1)) = ?1 AND ${live}`,
|
|
60
|
+
)
|
|
61
|
+
.bind(prefix, Date.now())
|
|
62
|
+
.all<{ key: string }>();
|
|
63
|
+
// Sort in JavaScript: SQLite orders UTF-8 bytes, the contract orders
|
|
64
|
+
// the UTF-16 code units every other adapter sorts by.
|
|
65
|
+
return results.map((row) => row.key).sort();
|
|
66
|
+
},
|
|
67
|
+
async compareAndSet(key, expected, next, opts) {
|
|
68
|
+
const now = Date.now();
|
|
69
|
+
if (next === null && expected === null) {
|
|
70
|
+
// Nothing to write; the claim holds exactly when no live row exists.
|
|
71
|
+
const row = await db
|
|
72
|
+
.prepare(`SELECT 1 AS present FROM connecta_kv WHERE key = ?1 AND ${live}`)
|
|
73
|
+
.bind(key, now)
|
|
74
|
+
.first();
|
|
75
|
+
return row === null;
|
|
76
|
+
}
|
|
77
|
+
if (next === null) {
|
|
78
|
+
return changed(
|
|
79
|
+
await db
|
|
80
|
+
.prepare(
|
|
81
|
+
`DELETE FROM connecta_kv WHERE key = ?1 AND ${live} AND value = ?3`,
|
|
82
|
+
)
|
|
83
|
+
.bind(key, now, expected)
|
|
84
|
+
.run(),
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
const expiry = expiresAt(now, opts?.ttlSeconds);
|
|
88
|
+
if (expected === null) {
|
|
89
|
+
// Insert, or take over a row that has expired. A live row makes the
|
|
90
|
+
// upsert's WHERE false, so nothing changes and the claim is refused.
|
|
91
|
+
return changed(
|
|
92
|
+
await db
|
|
93
|
+
.prepare(
|
|
94
|
+
`INSERT INTO connecta_kv (key, value, expires_at_ms)
|
|
95
|
+
VALUES (?1, ?3, ?4)
|
|
96
|
+
ON CONFLICT (key) DO UPDATE SET
|
|
97
|
+
value = excluded.value, expires_at_ms = excluded.expires_at_ms
|
|
98
|
+
WHERE connecta_kv.expires_at_ms IS NOT NULL
|
|
99
|
+
AND connecta_kv.expires_at_ms <= ?2`,
|
|
100
|
+
)
|
|
101
|
+
.bind(key, now, next, expiry)
|
|
102
|
+
.run(),
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
return changed(
|
|
106
|
+
await db
|
|
107
|
+
.prepare(
|
|
108
|
+
`UPDATE connecta_kv SET value = ?4, expires_at_ms = ?5
|
|
109
|
+
WHERE key = ?1 AND ${live} AND value = ?3`,
|
|
110
|
+
)
|
|
111
|
+
.bind(key, now, expected, next, expiry)
|
|
112
|
+
.run(),
|
|
113
|
+
);
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
}
|
|
@@ -11,9 +11,9 @@ import { encryptedCredentialVault } from "@zackbart/connecta/credentials";
|
|
|
11
11
|
* wrangler.jsonc backs the seven-tool surface.
|
|
12
12
|
*
|
|
13
13
|
* The operator surface is wired here except for activity history, which needs
|
|
14
|
-
* a database this example does not create for you: sign-in
|
|
15
|
-
* vault
|
|
16
|
-
*
|
|
14
|
+
* a database this example does not create for you: sign-in and the credential
|
|
15
|
+
* vault are on, and activity is three commented lines below. README.md
|
|
16
|
+
* § "Select optional modules" walks through all three.
|
|
17
17
|
*
|
|
18
18
|
* Setup (this example has no package.json of its own — it self-references the
|
|
19
19
|
* installed `@zackbart/connecta` package):
|
|
@@ -50,8 +50,8 @@ interface Env {
|
|
|
50
50
|
CONNECTA_KV: KVNamespace;
|
|
51
51
|
/**
|
|
52
52
|
* Base64 32-byte AES key encrypting operator-managed credentials in KV.
|
|
53
|
-
* Unset means no vault:
|
|
54
|
-
*
|
|
53
|
+
* Unset means no vault: credential management is unavailable in the operator
|
|
54
|
+
* UI. Never put it in KV, since it is what protects KV.
|
|
55
55
|
*/
|
|
56
56
|
CREDENTIAL_ENCRYPTION_KEY: string;
|
|
57
57
|
DOWNSTREAM_TOKEN: string;
|
|
@@ -90,22 +90,19 @@ function build(env: Env) {
|
|
|
90
90
|
// activityAccess: ({ id }) => id === "ACCESS_USER_UUID",
|
|
91
91
|
// },
|
|
92
92
|
// Connectors that declare a `credential` slot become editable by every
|
|
93
|
-
// signed-in human who can see that connector
|
|
94
|
-
// with this key before anything reaches KV. A saved replacement takes
|
|
93
|
+
// signed-in human who can see that connector, inside its connection on /,
|
|
94
|
+
// encrypted with this key before anything reaches KV. A saved replacement takes
|
|
95
95
|
// effect on the next call — no redeploy, and no liveness probe:
|
|
96
96
|
// credentials fail at use.
|
|
97
97
|
//
|
|
98
|
-
// The key is the vault, not the
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
//
|
|
98
|
+
// The key is the vault, not the form: a credential form appears only on a
|
|
99
|
+
// connector that declares a slot. Neither connector below does — Notion
|
|
100
|
+
// here carries a deployment-owned static header and echo has no secret at
|
|
101
|
+
// all — so this example ships the vault ready and nothing to fill in.
|
|
102
|
+
// Declare a slot (see the commented shape on `echo`, or use a provider
|
|
103
|
+
// connector like `notion()`, which declares its own) and the form appears
|
|
104
|
+
// on the next load.
|
|
105
105
|
vault: encryptedCredentialVault(storage, env.CREDENTIAL_ENCRYPTION_KEY),
|
|
106
|
-
// Eligible human operators can create named, revocable MCP Bearer tokens
|
|
107
|
-
// at /tokens. Under Worker-level Access those tokens are a rollback tool,
|
|
108
|
-
// not standalone edge credentials: Access still runs before connecta.
|
|
109
106
|
ui: operatorUi(),
|
|
110
107
|
identity: { credentialAdministration: () => "all", personalConnection: () => "all" },
|
|
111
108
|
// Payload-free activity at /activity, off until a database exists to hold
|
|
@@ -124,8 +121,8 @@ function build(env: Env) {
|
|
|
124
121
|
type: "headers",
|
|
125
122
|
headers: { Authorization: `Bearer ${env.DOWNSTREAM_TOKEN}` },
|
|
126
123
|
// The vault-backed alternative for a downstream that authenticates
|
|
127
|
-
// with a static key: the operator
|
|
128
|
-
//
|
|
124
|
+
// with a static key: the operator manages it inside its connection
|
|
125
|
+
// in the operator UI, so no Worker secret holds it.
|
|
129
126
|
// type: "credential",
|
|
130
127
|
// credential: { label: "Notion internal integration token" },
|
|
131
128
|
},
|
|
@@ -135,8 +132,8 @@ function build(env: Env) {
|
|
|
135
132
|
}),
|
|
136
133
|
api("echo", {
|
|
137
134
|
description: "Echo — text transforms",
|
|
138
|
-
// What a vault-backed connector adds — an operator edits this slot
|
|
139
|
-
//
|
|
135
|
+
// What a vault-backed connector adds — an operator edits this slot
|
|
136
|
+
// inside its connection in the operator UI and the handler reads it with
|
|
140
137
|
// `await ctx.credential?.get()`, so the secret never lives in source
|
|
141
138
|
// or in a Worker variable:
|
|
142
139
|
// credential: { label: "API token" },
|
|
@@ -27,15 +27,27 @@
|
|
|
27
27
|
{ "binding": "CONNECTA_KV", "id": "replace-with-kv-namespace-id" }
|
|
28
28
|
],
|
|
29
29
|
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
30
|
+
// D1 databases, off by default so this example deploys without one.
|
|
31
|
+
// Uncomment the array and keep the entries you use.
|
|
32
|
+
//
|
|
33
|
+
// ACTIVITY_DB: activity history. Create with `wrangler d1 create
|
|
34
|
+
// connecta-activity`, paste the id, apply the schema in README.md, then
|
|
35
|
+
// uncomment the matching lines in src/index.ts.
|
|
36
|
+
//
|
|
37
|
+
// STORAGE_DB: strongly consistent storage with compare-and-set, in place of
|
|
38
|
+
// CONNECTA_KV. Create with `wrangler d1 create connecta-storage`, paste the
|
|
39
|
+
// id, apply the schema in README.md § "Strongly consistent storage", then
|
|
40
|
+
// pass `d1Storage(env.STORAGE_DB)` as `storage` in src/index.ts.
|
|
34
41
|
// "d1_databases": [
|
|
35
42
|
// {
|
|
36
43
|
// "binding": "ACTIVITY_DB",
|
|
37
44
|
// "database_name": "connecta-activity",
|
|
38
45
|
// "database_id": "replace-with-d1-database-id"
|
|
46
|
+
// },
|
|
47
|
+
// {
|
|
48
|
+
// "binding": "STORAGE_DB",
|
|
49
|
+
// "database_name": "connecta-storage",
|
|
50
|
+
// "database_id": "replace-with-d1-database-id"
|
|
39
51
|
// }
|
|
40
52
|
// ],
|
|
41
53
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zackbart/connecta",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"description": "One MCP to rule them all — a single MCP endpoint aggregating many downstream connectors behind a code-first surface of seven meta-tools.",
|
|
@@ -116,7 +116,7 @@
|
|
|
116
116
|
"clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
117
117
|
"build:operator-ui": "node scripts/build-operator-ui.mjs",
|
|
118
118
|
"check:operator-ui": "node scripts/build-operator-ui.mjs --check",
|
|
119
|
-
"build": "npm run build:operator-ui && npm run clean && tsc -p tsconfig.build.json",
|
|
119
|
+
"build": "npm run build:operator-ui && npm run clean && tsc -p tsconfig.build.json && node scripts/prune-declarations.mjs",
|
|
120
120
|
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.operator-ui.json --noEmit",
|
|
121
121
|
"test": "vitest run",
|
|
122
122
|
"test:browser:install": "playwright install chromium",
|
|
@@ -129,10 +129,16 @@
|
|
|
129
129
|
"check:examples": "tsc -p tsconfig.template.node.json && tsc -p tsconfig.examples.worker.json",
|
|
130
130
|
"check:security": "npm audit --omit=dev --audit-level=moderate",
|
|
131
131
|
"check:package": "node scripts/check-package.mjs",
|
|
132
|
-
"
|
|
132
|
+
"check:declarations": "node scripts/check-declarations.mjs --dist .",
|
|
133
|
+
"check:bundle": "node scripts/bundle-size.mjs",
|
|
133
134
|
"providers:check": "tsx scripts/drift-check.mjs --specs --docs",
|
|
134
135
|
"drift:check": "tsx scripts/drift-check.mjs",
|
|
135
|
-
"
|
|
136
|
+
"eval:selftest": "tsx eval/selftest.ts",
|
|
137
|
+
"eval:agent": "tsx eval/run-agent.ts",
|
|
138
|
+
"eval:smoke": "tsx eval/run-smoke.ts",
|
|
139
|
+
"eval:perf": "tsx eval/run-perf.ts",
|
|
140
|
+
"eval:report": "tsx eval/report.ts",
|
|
141
|
+
"check": "npm run check:docs && npm run check:operator-ui && npm run check:lint && npm run check:unused && npm run typecheck && npm run test && npm run test:browser && npm run build && npm run check:declarations && npm run check:bundle && npm run check:examples",
|
|
136
142
|
"release:check": "npm run check && npm run check:security && npm run check:package",
|
|
137
143
|
"prepack": "npm run check"
|
|
138
144
|
},
|
|
@@ -140,6 +146,7 @@
|
|
|
140
146
|
"@cfworker/json-schema": "^4.1.1",
|
|
141
147
|
"@modelcontextprotocol/client": "2.0.0",
|
|
142
148
|
"@modelcontextprotocol/server": "2.0.0",
|
|
149
|
+
"effect": "4.0.0-rc.117",
|
|
143
150
|
"zod": "^4.4.3"
|
|
144
151
|
},
|
|
145
152
|
"peerDependencies": {
|
|
@@ -23,14 +23,14 @@ CONNECTA_STATE_FILE=
|
|
|
23
23
|
# Each of these is read only once the matching block in src/index.ts is
|
|
24
24
|
# uncommented. Setting one on its own changes nothing.
|
|
25
25
|
|
|
26
|
-
# Operator sign-in with Clerk. Only a signed-in operator may write a
|
|
27
|
-
#
|
|
26
|
+
# Operator sign-in with Clerk. Only a signed-in operator may write a
|
|
27
|
+
# credential; the bearer above is a client key, not an admin key.
|
|
28
28
|
# Also run `npm install @clerk/backend` — it is an optional peer.
|
|
29
29
|
CLERK_PUBLISHABLE_KEY=
|
|
30
30
|
CLERK_SECRET_KEY=
|
|
31
31
|
|
|
32
32
|
# Credential vault. Base64-encoded 32-byte AES key encrypting the values an
|
|
33
|
-
# operator saves
|
|
33
|
+
# operator saves inside a connection on /. Generate one with:
|
|
34
34
|
# node -e "console.log(crypto.randomBytes(32).toString('base64'))"
|
|
35
35
|
# Keep it here or in a secret store — never in the state file it protects, and
|
|
36
36
|
# never in source control. Losing it makes stored credentials unreadable.
|
package/templates/node/AGENTS.md
CHANGED
|
@@ -19,7 +19,6 @@ This repository is deployment configuration, not a copy of Connecta itself.
|
|
|
19
19
|
alone never grants it. Configured bearer auth is a client option, not a human
|
|
20
20
|
management identity. Connecta-issued access tokens are removed.
|
|
21
21
|
`src/file-activity.ts` remains the deployment-owned history store.
|
|
22
|
-
|
|
23
22
|
- Run `npm run typecheck` after configuration changes. With the server running,
|
|
24
23
|
run `CONNECTA_TOKEN=... npm run doctor` before calling setup complete.
|
|
25
24
|
- `Dockerfile` and `docker-compose.yml` containerize *this* source; they are
|
|
@@ -27,9 +26,11 @@ This repository is deployment configuration, not a copy of Connecta itself.
|
|
|
27
26
|
`src/index.ts`, never in a divergent container entrypoint.
|
|
28
27
|
- Moving this deployment to a newer Connecta is its own procedure, and it is
|
|
29
28
|
not a re-`init` — `connecta init` refuses to merge into an existing path on
|
|
30
|
-
purpose.
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
purpose. Bump the exact `@zackbart/connecta` pin in `package.json`, then read
|
|
30
|
+
the [changelog](https://github.com/zackbart/connecta/blob/main/CHANGELOG.md)
|
|
31
|
+
for every release in between — it also ships at
|
|
32
|
+
`node_modules/@zackbart/connecta/CHANGELOG.md`. Each release opens with what
|
|
33
|
+
breaks and what a deployment can ignore.
|
|
33
34
|
|
|
34
35
|
Do not add alternate entrypoints, policy layers, generated connector catalogs,
|
|
35
36
|
or runtime connector registration. Keep the deployment small enough to review
|
package/templates/node/README.md
CHANGED
|
@@ -74,7 +74,8 @@ connector where each person should connect their own downstream account.
|
|
|
74
74
|
### Credential vault
|
|
75
75
|
|
|
76
76
|
Import `encryptedCredentialVault` from `@zackbart/connecta/credentials`, then
|
|
77
|
-
set `vault: encryptedCredentialVault(storage, credentialKey)
|
|
77
|
+
set `vault: encryptedCredentialVault(storage, credentialKey)` — `storage` is
|
|
78
|
+
the `fileStorage` binding `src/index.ts` already passes to `createConnecta`. Set
|
|
78
79
|
`CONNECTA_CREDENTIAL_KEY` to a base64 32-byte AES key:
|
|
79
80
|
|
|
80
81
|
```sh
|