@zackbart/connecta 0.23.0 → 0.24.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.
Files changed (78) hide show
  1. package/AGENTS.md +5 -0
  2. package/CHANGELOG.md +40 -0
  3. package/README.md +18 -10
  4. package/dist/activity-friction.d.ts +3 -0
  5. package/dist/activity-friction.js +19 -0
  6. package/dist/activity.d.ts +11 -2
  7. package/dist/activity.js +15 -19
  8. package/dist/auth/downstream-oauth.d.ts +2 -1
  9. package/dist/auth/downstream-oauth.js +10 -1
  10. package/dist/branding.d.ts +67 -0
  11. package/dist/branding.js +176 -0
  12. package/dist/connectors/remote-mcp.js +3 -5
  13. package/dist/credential-contract.d.ts +24 -0
  14. package/dist/credential-contract.js +1 -0
  15. package/dist/credential-rules.d.ts +85 -0
  16. package/dist/credential-rules.js +107 -0
  17. package/dist/credentials.d.ts +4 -100
  18. package/dist/credentials.js +3 -107
  19. package/dist/index.d.ts +22 -55
  20. package/dist/index.js +30 -58
  21. package/dist/invocation.js +2 -3
  22. package/dist/meta-tools.d.ts +4 -0
  23. package/dist/meta-tools.js +8 -4
  24. package/dist/module-contracts.d.ts +19 -0
  25. package/dist/module-contracts.js +1 -0
  26. package/dist/operator-ui/generated.js +2 -2
  27. package/dist/operator-ui/model.d.ts +6 -3
  28. package/dist/operator-ui/view.d.ts +2 -18
  29. package/dist/operator-ui/view.js +3 -20
  30. package/dist/registry.d.ts +4 -1
  31. package/dist/registry.js +4 -6
  32. package/dist/routes/activity.js +1 -1
  33. package/dist/routes/credentials.js +5 -2
  34. package/dist/routes/mcp.js +7 -3
  35. package/dist/routes/oauth-management.d.ts +2 -0
  36. package/dist/routes/oauth-management.js +108 -0
  37. package/dist/routes/oauth.d.ts +0 -1
  38. package/dist/routes/oauth.js +21 -121
  39. package/dist/routes/shared.d.ts +19 -17
  40. package/dist/routes/shared.js +48 -44
  41. package/dist/routes/ui.js +36 -33
  42. package/dist/server.js +6 -26
  43. package/dist/types.d.ts +2 -0
  44. package/dist/ui.d.ts +15 -70
  45. package/dist/ui.js +176 -317
  46. package/dist/version.d.ts +1 -1
  47. package/dist/version.js +1 -1
  48. package/documentation/architecture.md +26 -17
  49. package/documentation/auth.md +61 -106
  50. package/documentation/cloudflare.md +1 -1
  51. package/documentation/connectors.md +1 -1
  52. package/documentation/linear.md +1 -1
  53. package/documentation/meta-tools.md +6 -4
  54. package/documentation/mixpanel.md +1 -1
  55. package/documentation/notion.md +2 -2
  56. package/documentation/operations.md +12 -14
  57. package/documentation/operator-ui.md +82 -104
  58. package/documentation/optional-modules-upgrade.md +243 -0
  59. package/documentation/provider-conventions.md +5 -3
  60. package/documentation/revenuecat.md +1 -1
  61. package/documentation/storage-and-credentials.md +59 -40
  62. package/documentation/stripe.md +1 -1
  63. package/documentation/upgrading.md +29 -4
  64. package/ethos.md +22 -30
  65. package/examples/worker/AGENTS.md +3 -1
  66. package/examples/worker/README.md +68 -84
  67. package/examples/worker/src/d1-activity.ts +1 -1
  68. package/examples/worker/src/index.ts +11 -6
  69. package/package.json +18 -2
  70. package/templates/node/AGENTS.md +8 -6
  71. package/templates/node/README.md +56 -67
  72. package/templates/node/package.json +1 -1
  73. package/templates/node/src/file-activity.ts +1 -1
  74. package/templates/node/src/index.ts +11 -12
  75. package/dist/access-tokens.d.ts +0 -31
  76. package/dist/access-tokens.js +0 -236
  77. package/dist/routes/access-tokens.d.ts +0 -6
  78. package/dist/routes/access-tokens.js +0 -83
@@ -120,96 +120,80 @@ discover the skew in production ([#376](https://github.com/zackbart/connecta/iss
120
120
  no Clerk import, secret, package, or fallback provider. Docker deployments keep
121
121
  the Clerk path in the Node template.
122
122
 
123
- Then point an MCP client at `<PUBLIC_URL>/mcp`, and open `<PUBLIC_URL>/` for
124
- Connections. Credentials is at `/credentials`, named MCP access tokens are at
125
- `/tokens`, Activity is at `/activity`, and legacy `/ui` redirects to `/`. Each
126
- of those three appears in the nav only when this deployment can serve it — see
127
- the next section for what turns each one on.
128
-
129
- ## The operator surface
130
-
131
- This example ships the whole operator feature set. Three quarters of it is on
132
- as deployed; the fourth needs a database, so it is commented in place.
133
-
134
- **Operator sign-in** is the `cloudflareAccessAuth()` entry in `src/index.ts`.
135
- Access authenticates before the Worker runs. A human Access identity can use
136
- MCP and human-management pages; a service-token identity can use MCP but cannot
137
- write a credential, run downstream OAuth, or issue a connecta token. Cloudflare
138
- still owns the outer application admission policy, but Connecta's user roster,
139
- connector access, and deployment roles stay in `src/index.ts`.
140
-
141
- **Several users** need no second auth system or Connecta account dashboard.
142
- Uncomment the `identity` block in `src/index.ts` to derive connector ids and
143
- deployment-operator membership from the Access principal. Connectors remain
144
- visible to everyone and every human remains an operator when that block is
145
- absent. A signed-in human may edit auth for every connector their view includes.
146
- Add `authScope: "personal"` when each user should connect a different downstream
147
- account; leave it shared only when any user with connector access may rotate the
148
- deployment-wide grant. Static headers stay shared because their value lives in
149
- deployment configuration. See [inbound identity](../../documentation/auth.md#principals-visibility-and-operators)
150
- for the resolver contract.
151
-
152
- **The credential vault** is `credentials: { encryptionKey: … }`, backed by the
153
- same KV namespace as everything else and encrypted with the
154
- `CREDENTIAL_ENCRYPTION_KEY` secret before a value reaches it. Generate one with:
123
+ Then point an MCP client at `<PUBLIC_URL>/mcp`. The example explicitly enables
124
+ `ui: operatorUi()`; open `<PUBLIC_URL>/` for Connections and each connection's
125
+ authentication controls. There is no separate Credentials or Tokens tab.
126
+
127
+ ## Select optional modules
128
+
129
+ `cloudflareAccessAuth()` reads trusted identity after Access admits the Worker
130
+ request. Humans may use their code-derived connector view; service identities
131
+ can use MCP but cannot manage personal or shared auth as an interactive human.
132
+ Keep users, groups, and admission in Access. Connector visibility and management
133
+ permissions belong in `src/index.ts`.
134
+
135
+ `identity.connectorAccess` selects discoverable and callable connectors.
136
+ `credentialAdministration` separately allows shared credential and OAuth
137
+ management, while `personalConnection` allows a human to connect their own
138
+ account. Both management permissions default to none. Grant the intended
139
+ owner's shared permissions explicitly, and grant users personal permissions
140
+ only for connectors configured with `authScope: "personal"`. Static headers
141
+ remain deployment configuration. `activityAccess` governs global history reads.
142
+ See [inbound identity](../../documentation/auth.md#principals-visibility-and-operators).
143
+
144
+ ### UI and encrypted credentials
145
+
146
+ Import `operatorUi` from `@zackbart/connecta/ui` and set `ui: operatorUi()`.
147
+ Branding belongs in `operatorUi({ branding })`. Omit the import and option to
148
+ serve no UI routes; OAuth callbacks still work in core for authorized
149
+ interactive MCP callers.
150
+
151
+ Import `encryptedCredentialVault` from `@zackbart/connecta/credentials` and set
152
+ `vault: encryptedCredentialVault(storage, env.CREDENTIAL_ENCRYPTION_KEY)` when
153
+ the secret is configured. Keep this base64 32-byte key in Worker secrets:
155
154
 
156
155
  ```sh
157
156
  node -e "console.log(crypto.randomBytes(32).toString('base64'))"
158
157
  ```
159
158
 
160
- Leave the secret unset and the deployment still runs `/credentials` stays
161
- read-only and connecta says so at startup. Keep the key in Worker secrets and
162
- nowhere near KV: it is the only thing that makes a copied namespace useless.
163
- Rotation takes effect on the next call, with no redeploy and no liveness probe,
164
- because credentials fail at use.
165
-
166
- The vault is ready here, and the Credentials page is still hidden, because that
167
- page lists connector credential slots rather than deployments. Neither
168
- connector in `src/index.ts` declares one Notion carries a deployment-owned
169
- static header and echo has no secret — so nothing would be on the page. Add
170
- `credential: { label: "API token" }` to an `api()` connector (the commented
171
- shape on `echo` is exactly it) or use a provider connector such as `notion()`,
172
- which declares its own, and Credentials appears for a signed-in operator on the
173
- next load.
174
-
175
- **Access tokens** are `accessTokens: {}`. A signed-in human operator mints named,
176
- revocable Bearer tokens at `/tokens` for header-capable clients that will not do
177
- OAuth. Secrets are shown once and only their hashes enter KV; a lost token is
178
- reissued, never recovered. Note the KV caveat above — revocation is visible
179
- everywhere only as fast as the namespace converges.
180
-
181
- Worker-level Access still runs before these tokens. A `cta_…` token therefore
182
- does not reach connecta by itself; retain the feature as a rollback path or for
183
- a caller that already supplies separate Access service-token headers. Normal
184
- interactive MCP clients should use Managed OAuth, and unattended clients should
185
- use Access service tokens.
186
-
187
- **Activity** is the commented block in `src/index.ts` and the commented
188
- `d1_databases` binding in `wrangler.jsonc`; the section below creates the
189
- database and applies the schema.
190
-
191
- None of these change what agents can reach. Operator routes manage the
192
- authentication material behind capabilities `src/index.ts` already declares —
193
- never the connector set, the tool catalog, or its annotations.
194
-
195
- `connecta doctor` reports the same line here as for a deployment with none of
196
- this on: connector count, executor, seven tools. The executor it names is this
197
- one — `DynamicWorkerExecutor executed`, not the Node template's QuickJS, which
198
- is what doctor used to claim everywhere
199
- ([#368](https://github.com/zackbart/connecta/issues/368)). Against Access it
200
- carries `CF_ACCESS_CLIENT_ID` and `CF_ACCESS_CLIENT_SECRET`, and the service
201
- identity learns the model-facing surface rather than deployment topology:
159
+ Reuse the same key and KV namespace during upgrades. Without the secret, omit
160
+ the vault; declared credential slots remain unmanageable. Keep the key outside
161
+ KV because it protects a copied namespace. Credential replacement takes effect
162
+ on the next call without redeploying; no liveness probe runs in the background.
163
+
164
+ The shipped Notion connector uses a deployment-owned static header and echo
165
+ needs no secret. To exercise vault controls, declare a `credential` slot on an
166
+ `api()` connector or use a provider such as `notion()` that declares its own.
167
+ Authorized users manage that slot inside the connection. Configuring a vault
168
+ does not create credentials or permissions by itself.
169
+
170
+ ### Client authentication and activity
171
+
172
+ Interactive MCP clients use Access Managed OAuth. Unattended clients use Access
173
+ service tokens when needed. Connecta-issued `cta_` tokens and their management
174
+ routes are removed; a configured Connecta bearer cannot cross the Access edge
175
+ alone. See the [migration guide](../../documentation/upgrading.md#0240-optional-modules)
176
+ if an older deployment still issues tokens.
177
+
178
+ Activity uses `activityHistory({ store: d1ActivityStore(env.ACTIVITY_DB) })`
179
+ from `@zackbart/connecta/activity`. Enable the database and bindings described
180
+ below. Omit the module and store wiring to record no history and show no
181
+ Activity tab. Diagnostics remain independent; `logger: "silent"` suppresses
182
+ them explicitly.
183
+
184
+ Verify MCP health and the exact seven tools with:
202
185
 
203
186
  ```sh
204
187
  CF_ACCESS_CLIENT_ID=… CF_ACCESS_CLIENT_SECRET=… \
205
188
  npx connecta doctor --url "$PUBLIC_URL"
206
189
  ```
207
190
 
208
- Confirm the operator surface the way an operator will: sign in at
209
- `<PUBLIC_URL>/` and check that Tokens is live. Credentials joins it
210
- once a connector declares a `credential` slot, and Activity once the D1 wiring
211
- below is on the nav shows a page when the deployment can actually serve it,
212
- so a missing page is the honest report that its half is still off.
191
+ Doctor reports the configured `DynamicWorkerExecutor`, not a presumed Node
192
+ executor. Verify the UI separately as a human: check visible connections,
193
+ explicit shared and personal auth permissions, and Activity only when enabled.
194
+ The configured list loads before live connector checks; a slow provider must
195
+ not prevent other connections from appearing. All capability and access changes
196
+ still require a deployment-code change.
213
197
 
214
198
  ## Code mode
215
199
 
@@ -295,19 +279,19 @@ To enable it:
295
279
  the failure and returns the tool result unharmed — so the symptom is not an
296
280
  error your agent sees, it is an activity log that quietly stops recording.
297
281
 
298
- 3. In `src/index.ts`, uncomment the `d1ActivityStore` import, the `ACTIVITY_DB`
299
- field on `Env`, and the `activity` block — the three commented fragments
300
- that together read:
282
+ 3. In `src/index.ts`, enable the `activityHistory` and `d1ActivityStore` imports,
283
+ the `ACTIVITY_DB` field on `Env`, and the `activity` option:
301
284
 
302
285
  ```ts
286
+ import { activityHistory } from "@zackbart/connecta/activity";
303
287
  import { d1ActivityStore } from "./d1-activity.js";
304
288
 
305
289
  createConnecta({
306
290
  // …
307
- activity: {
291
+ activity: activityHistory({
308
292
  store: d1ActivityStore(env.ACTIVITY_DB),
309
293
  deploymentId: "production",
310
- },
294
+ }),
311
295
  });
312
296
  ```
313
297
 
@@ -2,7 +2,7 @@ import type {
2
2
  ActivityPage,
3
3
  ActivityStore,
4
4
  } from "@zackbart/connecta";
5
- import { InvalidActivityCursorError } from "@zackbart/connecta";
5
+ import { InvalidActivityCursorError } from "@zackbart/connecta/activity";
6
6
  import {
7
7
  activityEventToRow,
8
8
  activityRowToEvent,
@@ -1,3 +1,6 @@
1
+ import { operatorUi } from "@zackbart/connecta/ui";
2
+ // import { activityHistory } from "@zackbart/connecta/activity";
3
+ import { encryptedCredentialVault } from "@zackbart/connecta/credentials";
1
4
  /**
2
5
  * connecta on Cloudflare Workers.
3
6
  *
@@ -63,9 +66,10 @@ interface Env {
63
66
  }
64
67
 
65
68
  function build(env: Env) {
69
+ const storage = cloudflareKvStorage(env.CONNECTA_KV);
66
70
  return createConnecta({
67
71
  publicUrl: env.PUBLIC_URL,
68
- storage: cloudflareKvStorage(env.CONNECTA_KV),
72
+ storage,
69
73
  executor: new DynamicWorkerExecutor({ loader: env.LOADER }),
70
74
  auth: [
71
75
  // Access owns admission policy. A human identity may use MCP and the
@@ -83,7 +87,7 @@ function build(env: Env) {
83
87
  // principal?.id === "ACCESS_USER_UUID"
84
88
  // ? ["notion", "echo"]
85
89
  // : ["echo"],
86
- // operatorAccess: ({ id }) => id === "ACCESS_USER_UUID",
90
+ // activityAccess: ({ id }) => id === "ACCESS_USER_UUID",
87
91
  // },
88
92
  // Connectors that declare a `credential` slot become editable by every
89
93
  // signed-in human who can see that connector at /credentials, encrypted
@@ -98,19 +102,20 @@ function build(env: Env) {
98
102
  // ready and the page empty. Declare a slot (see the commented shape on
99
103
  // `echo`, or use a provider connector like `notion()`, which declares its
100
104
  // own) and the page appears on the next load.
101
- credentials: { encryptionKey: env.CREDENTIAL_ENCRYPTION_KEY },
105
+ vault: encryptedCredentialVault(storage, env.CREDENTIAL_ENCRYPTION_KEY),
102
106
  // Eligible human operators can create named, revocable MCP Bearer tokens
103
107
  // at /tokens. Under Worker-level Access those tokens are a rollback tool,
104
108
  // not standalone edge credentials: Access still runs before connecta.
105
- accessTokens: {},
109
+ ui: operatorUi(),
110
+ identity: { credentialAdministration: () => "all", personalConnection: () => "all" },
106
111
  // Payload-free activity at /activity, off until a database exists to hold
107
112
  // it. Uncomment the `d1_databases` binding in wrangler.jsonc, apply the
108
113
  // schema in README.md § "Activity history", then these three lines and the
109
114
  // import above.
110
- // activity: {
115
+ // activity: activityHistory({
111
116
  // store: d1ActivityStore(env.ACTIVITY_DB),
112
117
  // deploymentId: "production",
113
- // },
118
+ // }),
114
119
  connectors: [
115
120
  remoteMcp("notion", {
116
121
  url: "https://mcp.notion.com/mcp",
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@zackbart/connecta",
3
- "version": "0.23.0",
3
+ "version": "0.24.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
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.",
6
+ "description": "One MCP to rule them all \u2014 a single MCP endpoint aggregating many downstream connectors behind a code-first surface of seven meta-tools.",
7
7
  "license": "MIT",
8
8
  "engines": {
9
9
  "node": ">=22.0.0"
@@ -94,6 +94,22 @@
94
94
  "./providers/vercel": {
95
95
  "types": "./dist/providers/vercel.d.ts",
96
96
  "import": "./dist/providers/vercel.js"
97
+ },
98
+ "./ui": {
99
+ "types": "./dist/ui.d.ts",
100
+ "import": "./dist/ui.js"
101
+ },
102
+ "./credentials": {
103
+ "types": "./dist/credentials.d.ts",
104
+ "import": "./dist/credentials.js"
105
+ },
106
+ "./activity": {
107
+ "types": "./dist/activity.d.ts",
108
+ "import": "./dist/activity.js"
109
+ },
110
+ "./auth/bearer": {
111
+ "types": "./dist/auth/bearer.d.ts",
112
+ "import": "./dist/auth/bearer.js"
97
113
  }
98
114
  },
99
115
  "scripts": {
@@ -12,12 +12,14 @@ This repository is deployment configuration, not a copy of Connecta itself.
12
12
  Do not copy or modify Connecta package internals here.
13
13
  - Prefer `api()` when the agent must see an exact reviewed capability surface;
14
14
  `remoteMcp()` follows the downstream server's evolving tool catalog.
15
- - The operator surface — Clerk sign-in, credential vault, access tokens,
16
- activity ships as commented blocks in `src/index.ts`. Enable one by
17
- uncommenting it and setting the variables it names in `.env`, never by
18
- inventing a parallel configuration path. `README.md` § "Turn on the operator
19
- surface" is the walkthrough; `src/file-activity.ts` is the deployment-owned
20
- activity store the activity block wires.
15
+ - The UI, encrypted credential vault, and activity history use explicit module
16
+ imports and typed `ui`, `vault`, and `activity` options in `src/index.ts`.
17
+ Follow README "Select optional modules". Auth management requires explicit
18
+ `credentialAdministration` or `personalConnection` permissions; visibility
19
+ alone never grants it. Configured bearer auth is a client option, not a human
20
+ management identity. Connecta-issued access tokens are removed.
21
+ `src/file-activity.ts` remains the deployment-owned history store.
22
+
21
23
  - Run `npm run typecheck` after configuration changes. With the server running,
22
24
  run `CONNECTA_TOKEN=... npm run doctor` before calling setup complete.
23
25
  - `Dockerfile` and `docker-compose.yml` containerize *this* source; they are
@@ -44,79 +44,72 @@ So commit the `package-lock.json` that the `npm install` above wrote on this
44
44
  machine: from then on the build context carries it and every build takes the
45
45
  reproducible `npm ci` path.
46
46
 
47
- ## Turn on the operator surface
47
+ ## Select optional modules
48
48
 
49
- Out of the box this deployment serves the seven-tool MCP surface and a
50
- read-only operator UI: open `http://localhost:8787/`, paste the bearer, and you
51
- get Connections. The other three pages are configuration away, and each one is
52
- a commented block in `src/index.ts` uncomment it, set the variables it names
53
- in `.env`, restart. Do them in this order; the last two lean on the first, and
54
- Credentials wants one thing more than a block, called out in step 2.
49
+ The template explicitly enables `ui: operatorUi()` from
50
+ `@zackbart/connecta/ui`. Open `http://localhost:8787/` and supply the configured
51
+ bearer to inspect Connections. Omit that option and import for an API-only
52
+ server. OAuth callbacks remain in core even with no UI.
55
53
 
56
- **1. Operator sign-in (Clerk).** A bearer token is a client key. It may call
57
- tools and read connector status, but it may not write a credential or issue an
58
- access token — that would make one shared secret a deployment-admin key. An
59
- interactive identity is what unlocks the actionable half:
54
+ Connection management needs an interactive identity. A configured bearer is a
55
+ client key and never authorizes browser credential mutations. To enable Clerk:
60
56
 
61
57
  ```sh
62
- npm install @clerk/backend # optional peer; it does not install with Connecta
58
+ npm install @clerk/backend
63
59
  ```
64
60
 
65
- Set `CLERK_PUBLISHABLE_KEY` and `CLERK_SECRET_KEY`, uncomment the `clerkAuth`
66
- import, the two `process.env.CLERK_*` reads, and the `clerkAuth({ … })` entry in
67
- `auth`. Enable Dynamic Client Registration on the Clerk instance (OAuth
68
- Applications DCR) if MCP clients should sign in through it too, and set
69
- `PUBLIC_URL` first Clerk redirects back to it.
61
+ Set `CLERK_PUBLISHABLE_KEY` and `CLERK_SECRET_KEY`, enable the corresponding
62
+ `clerkAuth` import and auth entry in `src/index.ts`, and set `PUBLIC_URL`.
63
+ Enable Dynamic Client Registration on the Clerk instance if MCP clients should
64
+ sign in with OAuth. Connecta no longer issues named client access tokens; keep
65
+ the configured bearer only for clients that need it.
70
66
 
71
- Clerk remains the identity provider when several people share this Docker
72
- deployment. Uncomment the `identity` block in `src/index.ts` to give each Clerk
73
- principal a config-derived connector view and to choose operators. Add
74
- `authScope: "personal"` to a connector when each person should supply their own
75
- credential or finish their own downstream OAuth flow. Without those options,
76
- all connectors and auth stay shared exactly as before.
67
+ Set the code-owned identity resolvers deliberately. `connectorAccess` governs
68
+ use; `credentialAdministration` permits shared-auth changes, and
69
+ `personalConnection` permits the signed-in principal's personal-auth changes.
70
+ Both management permissions default to none. Use `authScope: "personal"` for a
71
+ connector where each person should connect their own downstream account.
72
+ `activityAccess` separately selects readers of global activity.
77
73
 
78
- **2. Credential vault.** Uncomment `credentials` and set
74
+ ### Credential vault
75
+
76
+ Import `encryptedCredentialVault` from `@zackbart/connecta/credentials`, then
77
+ set `vault: encryptedCredentialVault(storage, credentialKey)`. Set
79
78
  `CONNECTA_CREDENTIAL_KEY` to a base64 32-byte AES key:
80
79
 
81
80
  ```sh
82
81
  node -e "console.log(crypto.randomBytes(32).toString('base64'))"
83
82
  ```
84
83
 
85
- Every connector that declares a `credential` slot then becomes editable at
86
- `/credentials`, with values encrypted in the state file. The shipped `time`
87
- connector declares none telling the time needs no secret so the key alone
88
- leaves the page hidden, which is the honest state for a page with nothing on
89
- it. Add `credential: { label: "API token" }` to an `api()` connector (the
90
- commented shape is in `src/index.ts`) and read it in a handler with
91
- `await ctx.credential?.get()`, or use a provider connector such as `notion()`,
92
- which declares its own; Credentials appears for a signed-in operator on the
93
- next restart. Keep the key anywhere
94
- except that file it is the only thing standing between a copied state file
95
- and the secrets in it — and note that losing it makes stored values
96
- unreadable. A saved replacement takes effect on the next call; nothing
97
- restarts, and the deployment never probes a credential to see whether it still
98
- works. It fails at use, loudly, and the agent is routed to `/credentials`.
99
-
100
- **3. Access tokens.** Uncomment `accessTokens: {}`. A signed-in operator can
101
- then mint named, revocable Bearer tokens at `/tokens` for header-capable
102
- clients that will not do OAuth. Secrets are shown once and only their hashes
103
- are stored, so a lost token is reissued, never recovered.
104
-
105
- **4. Activity.** Uncomment the `activity` block and the `fileActivityStore`
106
- import. `/activity` then answers with who called what, when, how long it took,
107
- and whether it worked — never arguments, results, generated code, or raw error
108
- messages, because the store is never handed one. `src/file-activity.ts` is
109
- yours: it appends a line per call and rewrites the log down to the newest 5,000
110
- events once it runs a slack window past that, so the file holds a few hundred
111
- more than the ceiling between rewrites rather than being rewritten on every
112
- call. That is a retention policy chosen for a single container and worth
113
- revisiting for anything busier. In Docker the log lands on the same volume as
114
- the state file.
115
-
116
- None of this changes what agents can reach. Operator pages manage the
117
- authentication material behind capabilities this file already declares; the
118
- connector set, the tool catalog, and its annotations are `src/index.ts`'s
119
- business and stay that way.
84
+ Keep this key outside the state file. Losing it makes saved values unreadable;
85
+ upgrades must reuse it. The shipped `time` connector declares no credential
86
+ slot. Add `credential: { label: "API token" }` to an `api()` connector and read
87
+ it through `await ctx.credential?.get()`, or use a provider such as `notion()`
88
+ that declares its own slot. Authorized humans manage the slot inside that
89
+ connection on `/`; there is no separate Credentials tab.
90
+
91
+ A saved replacement takes effect on the next call. Connecta tests credentials
92
+ only on an explicit action and otherwise fails at use. Without a vault or UI,
93
+ static credential recovery reports unavailable instead of offering a dead link.
94
+
95
+ ### Activity history and diagnostics
96
+
97
+ Import `activityHistory` from `@zackbart/connecta/activity` and wire the template's
98
+ `fileActivityStore` through `activity: activityHistory({ store })`. The Activity
99
+ tab appears for authorized readers when the store supports listing. Omit this
100
+ option and its store wiring to record no activity.
101
+
102
+ `src/file-activity.ts` belongs to the deployment. It appends payload-free events
103
+ and periodically retains the newest 5,000, allowing a small slack window between
104
+ rewrites. Docker stores the log on the state volume. It records no arguments,
105
+ results, generated code, or raw errors. Adjust retention in that file if needed.
106
+
107
+ Diagnostics are independent. Keep the default logger or provide your own;
108
+ `logger: "silent"` suppresses diagnostic output explicitly.
109
+
110
+ The UI displays connections and current permissions. Configuration still owns
111
+ the connector set, tool definitions, and access rules. There is no token tab,
112
+ team roster, or policy editor.
120
113
 
121
114
  ## Deployment contract
122
115
 
@@ -153,11 +146,7 @@ Connecta doctor passed: 1 connector(s), QuickJS executed, prescribed seven-tool
153
146
  `QuickJS` is this deployment's sandbox, reported by the deployment itself —
154
147
  swap the executor and doctor names the one that actually ran the program.
155
148
 
156
- That is deliberate. Doctor holds a bearer, and a bearer learns the model-facing
157
- surface, not the deployment's configuration topology: whether this deployment
158
- issues access tokens or keeps a credential vault is operator data, and a client
159
- key is not an operator. Confirm the operator surface the way an operator will —
160
- sign in at `/` and check that the pages you turned on are there: Tokens and
161
- Activity once their blocks are uncommented, and Credentials once the vault has
162
- a connector credential slot to show. The nav lists a page only when this
163
- deployment can serve it, so an absent page is a report, not a fault.
149
+ Doctor verifies the MCP contract. Verify UI behavior separately: sign in at `/`,
150
+ confirm the visible connections and their permitted auth controls, and check
151
+ Activity only when you enabled a readable history store. A missing optional
152
+ feature should not leave a tab behind.
@@ -15,7 +15,7 @@
15
15
  "typecheck": "tsc --noEmit"
16
16
  },
17
17
  "dependencies": {
18
- "@zackbart/connecta": "0.23.0",
18
+ "@zackbart/connecta": "0.24.0",
19
19
  "quickjs-emscripten": "0.32.0"
20
20
  },
21
21
  "devDependencies": {
@@ -27,7 +27,7 @@ import type {
27
27
  ActivityStore,
28
28
  ToolCallActivityEvent,
29
29
  } from "@zackbart/connecta";
30
- import { InvalidActivityCursorError } from "@zackbart/connecta";
30
+ import { InvalidActivityCursorError } from "@zackbart/connecta/activity";
31
31
 
32
32
  /**
33
33
  * Events allowed past `maxEvents` before the log is rewritten. Trimming on the
@@ -1,3 +1,7 @@
1
+ import { bearerToken } from "@zackbart/connecta/auth/bearer";
2
+ import { operatorUi } from "@zackbart/connecta/ui";
3
+ // import { encryptedCredentialVault } from "@zackbart/connecta/credentials";
4
+ // import { activityHistory } from "@zackbart/connecta/activity";
1
5
  /**
2
6
  * Prescribed Connecta deployment.
3
7
  *
@@ -20,7 +24,7 @@
20
24
  * CONNECTA_CREDENTIAL_KEY vault key, once the credentials block is on
21
25
  * CONNECTA_ACTIVITY_FILE activity log, once the activity block is on
22
26
  */
23
- import { api, bearerToken, createConnecta } from "@zackbart/connecta";
27
+ import { api, createConnecta } from "@zackbart/connecta";
24
28
  import { fileStorage, listen } from "@zackbart/connecta/node";
25
29
  import { quickJsExecutor } from "@zackbart/connecta/quickjs";
26
30
  // Operator sign-in. Needs `npm install @clerk/backend` — it is an optional
@@ -74,7 +78,7 @@ const connecta = createConnecta({
74
78
  // identity: {
75
79
  // connectorAccess: ({ principal }) =>
76
80
  // principal?.id === "user_admin" ? "all" : ["time"],
77
- // operatorAccess: ({ id }) => id === "user_admin",
81
+ // activityAccess: ({ id }) => id === "user_admin",
78
82
  // },
79
83
  publicUrl,
80
84
  // Required: model-written programs run in a bounded QuickJS child.
@@ -84,24 +88,19 @@ const connecta = createConnecta({
84
88
  // with this key — so keep the key out of that file and out of source:
85
89
  // node -e "console.log(crypto.randomBytes(32).toString('base64'))"
86
90
  // Rotating a credential takes effect on the next call; no restart.
87
- // credentials: { encryptionKey: process.env.CONNECTA_CREDENTIAL_KEY },
88
- //
89
- // Named, revocable Bearer tokens for MCP clients, issued at /tokens by a
90
- // signed-in operator. Secrets are shown once; only their hashes are stored.
91
- // Requires the Clerk block above — there is nobody to authorize issuance
92
- // otherwise.
93
- // accessTokens: {},
94
- //
91
+ // vault: encryptedCredentialVault(storage, process.env.CONNECTA_CREDENTIAL_KEY!),
95
92
  // Payload-free activity history at /activity: who called what, when, how
96
93
  // long it took, and whether it worked. Never arguments, results, generated
97
94
  // code, or raw error messages. Commented because retention is yours to
98
95
  // choose — see src/file-activity.ts.
99
- // activity: {
96
+ // activity: activityHistory({
100
97
  // store: fileActivityStore(
101
98
  // process.env.CONNECTA_ACTIVITY_FILE || "./.connecta-activity.jsonl",
102
99
  // ),
103
100
  // deploymentId: "production",
104
- // },
101
+ // }),
102
+ ui: operatorUi(),
103
+ identity: { credentialAdministration: () => "all", personalConnection: () => "all" },
105
104
  connectors: [
106
105
  api("time", {
107
106
  description: "Time — current timestamp",
@@ -1,31 +0,0 @@
1
- import type { IdentityReference, InboundAuth, KVStorage } from "./types.js";
2
- export interface AccessTokenMetadata {
3
- id: string;
4
- name: string;
5
- tokenPrefix: string;
6
- createdAt: string;
7
- revokedAt?: string;
8
- }
9
- export interface CreatedAccessToken {
10
- token: string;
11
- accessToken: AccessTokenMetadata;
12
- }
13
- /**
14
- * Deployment-scoped personal access tokens. Secret material is never
15
- * recoverable: authentication indexes a SHA-256 digest of a random 256-bit
16
- * token, while separately enumerable metadata powers operator management.
17
- */
18
- export declare class AccessTokenManager {
19
- private readonly storage;
20
- readonly auth: InboundAuth;
21
- private readonly maxActive;
22
- constructor(storage: KVStorage, options?: {
23
- maxActive?: number;
24
- });
25
- private read;
26
- list(): Promise<AccessTokenMetadata[]>;
27
- create(name: unknown, createdBy: string | IdentityReference): Promise<CreatedAccessToken>;
28
- rename(id: string, name: unknown): Promise<AccessTokenMetadata | null>;
29
- revoke(id: string, revokedBy: string): Promise<AccessTokenMetadata | null>;
30
- private authorize;
31
- }