@zackbart/connecta 0.32.0 → 0.33.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 (137) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/README.md +15 -7
  3. package/bin/connecta.mjs +24 -6
  4. package/bin/version.mjs +1 -1
  5. package/dist/access-tokens.d.ts +167 -23
  6. package/dist/access-tokens.js +676 -140
  7. package/dist/agent-identity.js +47 -0
  8. package/dist/auth/cloudflare-access.js +13 -5
  9. package/dist/call-admission-context.js +15 -0
  10. package/dist/call-admission.js +30 -43
  11. package/dist/call-recovery.js +6 -2
  12. package/dist/config-value-sources.js +11 -9
  13. package/dist/config.d.ts +37 -75
  14. package/dist/config.js +110 -36
  15. package/dist/connector-access.d.ts +4 -34
  16. package/dist/connector-access.js +15 -99
  17. package/dist/connector-caller.d.ts +7 -1
  18. package/dist/connector-caller.js +3 -2
  19. package/dist/connectors/api-connector.d.ts +27 -4
  20. package/dist/connectors/api-connector.js +2 -0
  21. package/dist/connectors/remote-mcp.js +2 -0
  22. package/dist/describe-config.d.ts +29 -7
  23. package/dist/describe-config.js +7 -17
  24. package/dist/index.d.ts +4 -3
  25. package/dist/index.js +29 -10
  26. package/dist/invocation.js +5 -3
  27. package/dist/membership.d.ts +51 -0
  28. package/dist/membership.js +121 -0
  29. package/dist/module-contracts.d.ts +23 -6
  30. package/dist/operator-record.d.ts +2 -2
  31. package/dist/operator-record.js +2 -4
  32. package/dist/operator-ui/agents.d.ts +74 -0
  33. package/dist/operator-ui/agents.js +104 -0
  34. package/dist/operator-ui/contract.d.ts +149 -28
  35. package/dist/operator-ui/endpoints.js +192 -0
  36. package/dist/operator-ui/fix-prompts.js +4 -4
  37. package/dist/operator-ui/generated.js +5 -4
  38. package/dist/operator-ui/member-page.js +112 -0
  39. package/dist/operator-ui/model.d.ts +14 -6
  40. package/dist/operator-ui/view.d.ts +84 -19
  41. package/dist/operator-ui/view.js +82 -21
  42. package/dist/page-styles.js +1 -1
  43. package/dist/provider.d.ts +61 -8
  44. package/dist/provider.js +46 -7
  45. package/dist/providers/_shared/google/workspace.d.ts +53 -16
  46. package/dist/providers/_shared/google/workspace.js +30 -8
  47. package/dist/providers/_shared/rest/tools.js +2 -2
  48. package/dist/providers/_shared/rest/value-safety.d.ts +50 -0
  49. package/dist/providers/_shared/rest/value-safety.js +699 -7
  50. package/dist/providers/ccb/index.js +1 -1
  51. package/dist/providers/cloudflare/index.js +3 -3
  52. package/dist/providers/cloudflare/rest.js +25 -3
  53. package/dist/providers/cloudflare/skill.generated.js +1 -1
  54. package/dist/providers/cloudflare/value-safety.js +32 -0
  55. package/dist/providers/docs/index.js +3 -3
  56. package/dist/providers/docs/skill.generated.js +2 -2
  57. package/dist/providers/drive/index.js +3 -3
  58. package/dist/providers/drive/skill.generated.js +2 -2
  59. package/dist/providers/forms/index.js +3 -3
  60. package/dist/providers/forms/skill.generated.js +2 -2
  61. package/dist/providers/gmail/index.js +10 -8
  62. package/dist/providers/gmail/skill.generated.js +2 -2
  63. package/dist/providers/mixpanel/index.js +1 -1
  64. package/dist/providers/notion/index.d.ts +7 -0
  65. package/dist/providers/notion/index.js +100 -11
  66. package/dist/providers/notion/rest.js +45 -5
  67. package/dist/providers/notion/skill.generated.js +2 -2
  68. package/dist/providers/notion/value-safety.js +30 -5
  69. package/dist/providers/overflow/index.js +1 -1
  70. package/dist/providers/planning-center/index.js +1 -1
  71. package/dist/providers/sheets/index.d.ts +3 -0
  72. package/dist/providers/sheets/index.js +6 -3
  73. package/dist/providers/sheets/skill.generated.js +2 -2
  74. package/dist/providers/slides/index.js +3 -3
  75. package/dist/providers/slides/skill.generated.js +2 -2
  76. package/dist/providers/stripe/rest.js +9 -2
  77. package/dist/providers/stripe/skill.generated.js +1 -1
  78. package/dist/providers/stripe/value-safety.js +22 -0
  79. package/dist/providers/vercel/rest.js +20 -2
  80. package/dist/providers/vercel/skill.generated.js +1 -1
  81. package/dist/providers/vercel/value-safety.js +22 -0
  82. package/dist/registry.d.ts +2 -3
  83. package/dist/registry.js +8 -9
  84. package/dist/routes/access-tokens.js +82 -75
  85. package/dist/routes/activity-disclosure.js +17 -18
  86. package/dist/routes/activity.js +34 -54
  87. package/dist/routes/actor-labels.js +33 -4
  88. package/dist/routes/agent-path.js +43 -0
  89. package/dist/routes/connect.js +16 -12
  90. package/dist/routes/credentials.js +16 -40
  91. package/dist/routes/mcp.js +145 -95
  92. package/dist/routes/oauth-management.js +11 -5
  93. package/dist/routes/oauth.js +3 -9
  94. package/dist/routes/operator.js +24 -23
  95. package/dist/routes/shared.d.ts +60 -20
  96. package/dist/routes/shared.js +187 -96
  97. package/dist/routes/ui-config.js +437 -97
  98. package/dist/routes/ui-data.js +2 -2
  99. package/dist/routes/ui.js +9 -16
  100. package/dist/runtime/call-admission.js +9 -0
  101. package/dist/server.js +5 -0
  102. package/dist/storage/keys.js +9 -1
  103. package/dist/types.d.ts +11 -0
  104. package/dist/ui.d.ts +1 -1
  105. package/dist/ui.js +16 -14
  106. package/dist/version.d.ts +1 -1
  107. package/dist/version.js +1 -1
  108. package/dist/workspace-address.js +9 -0
  109. package/dist/workspace-subject.js +23 -0
  110. package/documentation/README.md +46 -22
  111. package/documentation/architecture.md +355 -50
  112. package/documentation/auth.md +775 -243
  113. package/documentation/code-mode.md +14 -11
  114. package/documentation/deploying.md +121 -24
  115. package/documentation/integrating.md +92 -21
  116. package/documentation/meta-tools.md +14 -9
  117. package/documentation/migration-0.33.md +344 -0
  118. package/documentation/operating.md +3 -1
  119. package/documentation/operator-tests.md +115 -28
  120. package/documentation/operator-ui.md +326 -61
  121. package/documentation/provider-auth-migration.md +37 -23
  122. package/documentation/provider-migration-0.29.md +2 -2
  123. package/examples/worker/AGENTS.md +30 -8
  124. package/examples/worker/README.md +92 -53
  125. package/examples/worker/src/connecta.config.ts +36 -20
  126. package/examples/worker/wrangler.jsonc +3 -0
  127. package/package.json +3 -2
  128. package/templates/node/.env.example +3 -0
  129. package/templates/node/AGENTS.md +27 -6
  130. package/templates/node/Dockerfile +2 -2
  131. package/templates/node/README.md +67 -42
  132. package/templates/node/docker-compose.yml +3 -2
  133. package/templates/node/package.json +4 -3
  134. package/templates/node/src/adopt-token.ts +23 -0
  135. package/templates/node/src/connecta.config.ts +28 -12
  136. package/templates/node/src/rotate-token.ts +17 -0
  137. package/templates/node/src/provision-token.ts +0 -17
@@ -2,6 +2,12 @@
2
2
 
3
3
  This repository is deployment configuration, not a copy of Connecta itself.
4
4
 
5
+ Start every operator task at
6
+ `node_modules/@zackbart/connecta/documentation/README.md#operator-tasks`.
7
+ That installed index routes adding/removing connectors, organizing agent pools,
8
+ adding people, connecting shared credentials, issuing/rotating/revoking agent
9
+ tokens, upgrading, and diagnosis with `connecta doctor` to their procedures.
10
+
5
11
  - Edit `src/connecta.config.ts` for connectors, authentication, storage, public
6
12
  URL, and optional modules. `src/index.ts` only starts it. Set plain vars
7
13
  `CONNECTA_ACTIVITY="on"` for preserved activity.
@@ -9,8 +15,8 @@ This repository is deployment configuration, not a copy of Connecta itself.
9
15
  always installed over the same `CONNECTA_DB` for machines. Cloudflare Access
10
16
  admits the request before the Worker runs; a service identity alone is
11
17
  refused inside connecta. Do not add JWT parsing or another identity gate.
12
- Grant `identity.accessTokenManagement` explicitly to interactive operators;
13
- this example permits every signed-in human to manage connectors and tokens.
18
+ Signed-in maintainers alone manage shared credentials and agent tokens; pool
19
+ members connect only their own accounts on personal connectors in their pools.
14
20
  - Attach Access to the Worker itself, not only its hostname. Enable Managed
15
21
  OAuth on that Access application. CIMD is the spec's preferred client
16
22
  registration; enable DCR as the fallback Access currently requires.
@@ -57,14 +63,30 @@ This repository is deployment configuration, not a copy of Connecta itself.
57
63
  Do not copy or modify Connecta package internals here.
58
64
  - Prefer `api()` when the agent must see an exact reviewed capability set;
59
65
  `remoteMcp()` follows the downstream server's evolving tool catalog.
60
- - Unattended clients and `connecta doctor` need Access service credentials
61
- plus a stored `cta_` bearer. Set `CF_ACCESS_CLIENT_ID`,
62
- `CF_ACCESS_CLIENT_SECRET`, and `CONNECTA_TOKEN` together for doctor.
63
- Bootstrap tokens through the Access-authenticated operator page or trusted
64
- `AccessTokenManager.create` tooling against this same D1 storage, as README
66
+ - Agents (unattended clients) use `<PUBLIC_URL>/agent/<pool>` with a stored
67
+ `cta_` bearer as their only credential. That needs one self-hosted Access
68
+ application per hostname for `<host>/agent/*` with a single Bypass policy,
69
+ beside the Worker-level application (README.md § "Let agents through
70
+ Access"). Never bypass any other path. `/mcp` and `/mcp/<pool>` refuse `cta_`.
71
+ `connecta doctor` also reads `/health` behind Access: set
72
+ `CF_ACCESS_CLIENT_ID`, `CF_ACCESS_CLIENT_SECRET`, and `CONNECTA_TOKEN`
73
+ together, and pass `--pool ci`, a pool that lists the agent.
74
+ Machine clients are the `agents` declared in `src/connecta.config.ts`. Issue
75
+ or rotate a token through the Access-authenticated operator page or trusted
76
+ `AccessTokenManager.rotate` tooling against this same D1 storage, as README
65
77
  "Machine tokens" describes. Never configure a static bearer or open startup.
66
78
  UI, vault, and activity use explicit optional imports;
67
- auth changes require code-derived shared or personal management permissions.
79
+ auth changes require a maintainer, or a pool member for their own personal account.
80
+ - Organize agent pools by connecta's agent pool layout:
81
+ `node_modules/@zackbart/connecta/documentation/auth.md#organizing-an-agents-pools`
82
+ ([online](https://github.com/zackbart/connecta/blob/main/documentation/auth.md#organizing-an-agents-pools)).
83
+ Each agent keeps one Connecta connection whose client picks the pool per
84
+ turn: base pool `<agent>` (shared connectors, no `owner`) outside a
85
+ one-on-one, and `<agent>-<user>` (the base plus that user's own connector
86
+ ids, `owner` set to their Workspace address) only with that user. Never list
87
+ another person in a `<agent>-<user>` pool: its per-user connectors act as
88
+ that user for every member. Never add a second Connecta connection or mount
89
+ downstream tools directly.
68
90
  - Brand the operator UI in `operatorUi({ branding })`: product and owner names,
69
91
  description, favicon, and `theme` (`accent`, `radius`, `fontFamily`,
70
92
  `monoFamily`, `colorScheme`). Ask the deployment's owner for their brand
@@ -81,10 +81,10 @@ attempt and add the narrowest matching entry rather than allowing its entire
81
81
  origin.
82
82
 
83
83
  Access then serves OAuth discovery and turns the client's opaque token into the
84
- trusted human `ctx.access` identity connecta reads. A cron job or CI client
85
- needs Access service headers to cross the edge and a stored `cta_` bearer to
86
- authenticate inside connecta. An Access service identity alone is refused.
87
- See [Machine tokens](#machine-tokens).
84
+ trusted human `ctx.access` identity connecta reads. A cron job or CI client is
85
+ an agent: it uses `/agent/<pool>` with a stored `cta_` bearer as its only
86
+ credential, once [the agent bypass](#let-agents-through-access) exists. An
87
+ Access service identity alone is refused. See [Machine tokens](#machine-tokens).
88
88
 
89
89
  Through the API, the relevant part of the application is:
90
90
 
@@ -112,6 +112,33 @@ owns client registration, redirect allowlists, and token lifetimes.
112
112
  working in a copied deployment. Do not remove the entries there when changing
113
113
  the Access policy or application.
114
114
 
115
+ ### Let agents through Access
116
+
117
+ Add one more self-hosted Access application for each hostname agents use (the
118
+ `workers.dev` hostname and each custom domain): domain set to that hostname,
119
+ path `agent/*`, and a single policy whose action is **Bypass** and whose include
120
+ rule is **Everyone**. Keep the Worker-level application; it still protects every
121
+ other path. Through the API:
122
+
123
+ ```json
124
+ {
125
+ "type": "self_hosted",
126
+ "name": "connecta agents",
127
+ "destinations": [{ "type": "public", "uri": "connecta.example.com/agent/*" }],
128
+ "policies": [{ "name": "agents bypass", "decision": "bypass", "include": [{ "everyone": {} }] }]
129
+ }
130
+ ```
131
+
132
+ It must be a hostname-and-path (`public`) destination: a second Worker
133
+ destination is refused because the Worker-level application owns it. Changes
134
+ took 15–19 seconds to settle when probed, occasionally longer, with transient
135
+ edge errors meanwhile. Do not rely on a fixed wait: verify that `/agent/ci`
136
+ without credentials returns connecta's 401 (`WWW-Authenticate: Bearer`) while
137
+ `/mcp` still returns Access's refusal, several times in a row. Connecta ignores Access
138
+ context on `/agent/*` and refuses every other spelling of that namespace before
139
+ auth. [Deploying Connecta](https://github.com/zackbart/connecta/blob/main/documentation/deploying.md#agent-endpoints-behind-cloudflare-access)
140
+ records the untested cases and the propagation details.
141
+
115
142
  The checked-in `access.dev` block gives `wrangler dev` a local operator
116
143
  identity. Remove the block to test the missing-Access refusal. It has no effect
117
144
  on a deployed Worker's production identity.
@@ -219,57 +246,63 @@ discover the skew in production ([#376](https://github.com/zackbart/connecta/iss
219
246
  no Clerk import, secret, package, or fallback provider. Docker deployments keep
220
247
  the Clerk path in the Node template.
221
248
 
222
- Then point an MCP client at `<PUBLIC_URL>/mcp`. The example explicitly enables
249
+ Then list the maintainers' Access user ids (`user_uuid`, else email) in the
250
+ `CONNECTA_MAINTAINERS` var and point a maintainer's MCP client at
251
+ `<PUBLIC_URL>/mcp`. Other people use `<PUBLIC_URL>/mcp/<pool>`, and agents
252
+ `<PUBLIC_URL>/agent/<pool>`, for a pool that lists them in `members`. The example explicitly enables
223
253
  `ui: operatorUi()`; open `<PUBLIC_URL>/` for Connections and each connection's
224
254
  authentication controls. The page labels tool safety, offers fixed repair
225
255
  prompts for classified failures, and shows client setup commands for endpoints
226
256
  the signed-in person may use. Those commands contain no token. There is no
227
- separate Credentials tab. The Access tokens page is available to permitted
228
- interactive operators.
257
+ separate Credentials tab. The Access tokens page, listing one token slot per
258
+ declared agent, is available to permitted interactive operators.
229
259
 
230
260
  ## Select optional modules
231
261
 
232
262
  `cloudflareAccessAuth()` reads trusted identity after Access admits the Worker
233
- request. Humans may use their code-derived connector view and operator pages.
234
- Machines authenticate with `accessTokens(storage)` over the same D1 database;
235
- Access service headers alone never grant MCP access or interactive authority.
263
+ request. Maintainers use plain `/mcp` and the operator pages; people in a pool
264
+ use `/mcp/<pool>`, and agents in it `/agent/<pool>`, which serve exactly that
265
+ pool's tools. Agents authenticate with `accessTokens(storage)` over the same D1
266
+ database; human endpoints refuse their tokens, and Access service headers alone
267
+ never grant MCP access or interactive authority.
236
268
  Keep users, groups, and admission in Access. Connector visibility and management
237
269
  permissions belong in `src/connecta.config.ts`.
238
270
 
239
- `identity.connectorAccess` selects discoverable and callable connectors.
240
- `credentialAdministration` separately allows shared credential and OAuth
241
- management, while `personalConnection` allows a human to connect their own
242
- account. Both management permissions default to none. Grant the intended
243
- owner's shared permissions explicitly, and grant users personal permissions
244
- only for connectors configured with `authScope: "personal"`. Static headers
245
- remain deployment configuration. `activityAccess` governs global history reads.
246
- `accessTokenManagement` separately allows interactive operators to create, rename,
247
- and revoke machine tokens. This example grants that permission to every signed-in
248
- human, matching its connector-management permissions; narrow all three for a team.
271
+ `maintainers` and each pool's `members` select who reaches which endpoint and
272
+ who manages what. Signed-in maintainers alone administer shared credentials
273
+ and OAuth grants, issue, rotate, and revoke agent tokens, and read activity.
274
+ A pool member connects their own account only on connectors configured with
275
+ `authScope: "personal"` in their pools. Static headers remain deployment
276
+ configuration.
249
277
  See [inbound identity](../../documentation/auth.md#principals-visibility-and-operators).
250
278
 
251
279
  ### Machine tokens
252
280
 
253
- `accessTokens(storage)` is always installed over `CONNECTA_DB`, with no new
254
- binding. Empty token storage admits no machine client. A human authenticated
255
- through Access can still sign in and bootstrap the first token. Open
256
- `<PUBLIC_URL>/`, select Access tokens, and create a named token. Save the
257
- returned `cta_` secret privately; it is returned once. UI-issued tokens retain
258
- the issuing human's principal, while connector and pool rules are evaluated
259
- on every request. They never grant interactive operator authority.
260
-
261
- A machine sends all three headers:
281
+ Machine clients are agents declared by name in `agents` in
282
+ `src/connecta.config.ts`; this example declares `ci`. Each agent has one
283
+ `cta_` token held by `accessTokens(storage)` over `CONNECTA_DB`, with no new
284
+ binding. An agent with no issued token admits nothing. A human authenticated
285
+ through Access can still sign in and issue the first token. Open
286
+ `<PUBLIC_URL>/`, select Access tokens, and issue the agent's token. Save the
287
+ returned `cta_` secret privately; it is returned once. Rotating replaces it
288
+ immediately: the old secret stops on its next request. An agent has no human
289
+ principal, so personal connectors are absent for it; connector and pool rules
290
+ recognize it by `identity.agent` on every request. It never has interactive
291
+ operator authority. Removing a name from `agents` stops its token on the next
292
+ request.
293
+
294
+ An agent connects to `<PUBLIC_URL>/agent/<pool>` for a pool that lists it and
295
+ sends one header:
262
296
 
263
297
  ```text
264
- CF-Access-Client-Id: <access-service-client-id>
265
- CF-Access-Client-Secret: <access-service-client-secret>
266
298
  Authorization: Bearer <stored-cta-token>
267
299
  ```
268
300
 
269
- Permit the service credentials in the Worker-level Access application's policy.
270
- The service headers admit the request at the edge; connecta verifies the bearer
271
- against D1. Neither credential works alone for machine access. Doctor uses
272
- the same three credentials; see [Client authentication and activity](#client-authentication-and-activity).
301
+ [The agent bypass](#let-agents-through-access) lets the request past the edge;
302
+ connecta verifies the bearer against D1 and ignores any Access context or
303
+ headers there. `/mcp` and `/mcp/<pool>` refuse the token. Doctor also reads
304
+ `/health`, which stays behind Access, so it needs Access service credentials
305
+ too; see [Client authentication and activity](#client-authentication-and-activity).
273
306
 
274
307
  For programmatic bootstrap, trusted provisioning code that already holds this
275
308
  deployment's D1 binding can use the published API against that same storage:
@@ -278,16 +311,18 @@ deployment's D1 binding can use the published API against that same storage:
278
311
  import { AccessTokenManager } from "@zackbart/connecta/auth/access-tokens";
279
312
  import { d1Storage } from "@zackbart/connecta/d1";
280
313
 
281
- const manager = new AccessTokenManager(d1Storage(env.CONNECTA_DB));
282
- const { token } = await manager.create("ci-client", "trusted-provisioning");
314
+ const manager = new AccessTokenManager(d1Storage(env.CONNECTA_DB), { agents: ["ci"] });
315
+ const { token } = await manager.rotate("ci", "trusted-provisioning");
283
316
  // Deliver token once to the client's secret store, outside logs and source control.
284
317
  ```
285
318
 
286
- This creates a token without a human principal. The name is a label, never a
287
- grant. Provision only through trusted operator tooling; do not expose this
288
- code as a public route or mint a token at startup. Existing unrevoked `cta_`
289
- tokens survive a state migration into this same D1 namespace. Keep
290
- `accessTokens(storage)` configured and preserve the identity and pool rules.
319
+ Provision only through trusted operator tooling; do not expose this code as a
320
+ public route or rotate a token at startup. Tokens issued before agents survive a
321
+ state migration into this same D1 namespace but stop admitting until trusted
322
+ tooling binds each machine's token once with
323
+ `manager.adopt(tokenId, "ci")`, which keeps its secret working. Human-bound
324
+ tokens cannot be adopted; issue the agent a new token instead. Keep
325
+ `accessTokens(storage)` configured over the same storage.
291
326
 
292
327
  ### UI and encrypted credentials
293
328
 
@@ -323,11 +358,13 @@ does not create credentials or permissions by itself.
323
358
 
324
359
  ### Client authentication and activity
325
360
 
326
- Interactive MCP clients use Access Managed OAuth. Machine clients need both
327
- Access service credentials and a stored `cta_` bearer, provisioned as described
328
- in [Machine tokens](#machine-tokens). `accessTokens(storage)` is always enabled,
329
- and the example grants token management to every interactive Access operator.
330
- Keep token, connector, and pool grants in deployment code.
361
+ Interactive MCP clients use Access Managed OAuth. Agents send only a declared
362
+ agent's `cta_` bearer to `/agent/<pool>`, issued as described in
363
+ [Machine tokens](#machine-tokens). `accessTokens(storage)` is always
364
+ enabled, and every signed-in maintainer manages agent tokens.
365
+ Keep maintainers, pools, and their members in deployment code. An agent reaches
366
+ only `/agent/<pool>` for a pool that lists it (the example's `ci` pool lists
367
+ `agent:ci`); `/mcp` and `/mcp/<pool>` refuse its token.
331
368
 
332
369
  Activity uses `activityHistory({ store: d1ActivityStore(env.CONNECTA_DB) })`,
333
370
  with `activityHistory` from `@zackbart/connecta/activity` and `d1ActivityStore`
@@ -335,13 +372,15 @@ from `@zackbart/connecta/d1`. See [Activity history](#activity-history-optional)
335
372
  Omit the module and store wiring to record no history and show no Activity tab. Diagnostics remain independent; `logger: "silent"` suppresses
336
373
  them explicitly.
337
374
 
338
- Verify MCP health and the exact six tools with:
375
+ Verify MCP health and the exact six tools with the following. Doctor reads
376
+ `/health` through Access with the service credentials, then checks `/agent/ci`
377
+ with the `cta_` token alone:
339
378
 
340
379
  ```sh
341
380
  CF_ACCESS_CLIENT_ID='REPLACE_WITH_ACCESS_CLIENT_ID' \
342
381
  CF_ACCESS_CLIENT_SECRET='REPLACE_WITH_ACCESS_CLIENT_SECRET' \
343
382
  CONNECTA_TOKEN='cta_REPLACE_WITH_STORED_SECRET' \
344
- connecta doctor --url "$PUBLIC_URL"
383
+ connecta doctor --url "$PUBLIC_URL" --pool ci
345
384
  ```
346
385
 
347
386
  Doctor reports the configured `DynamicWorkerExecutor`, not a presumed Node
@@ -430,7 +469,7 @@ CREATE INDEX IF NOT EXISTS connecta_kv_expiry ON connecta_kv (expires_at_ms);
430
469
  - **Workers KV** (`CONNECTA_KV` and `cloudflare-kv.ts`). Workers KV is no longer
431
470
  supported: `createConnecta` refuses storage without `compareAndSet` at boot.
432
471
  Copy its state into D1 once, as described below, so OAuth grants, vault
433
- credentials, and `cta_` access tokens keep working.
472
+ credentials, and `cta_` access token records carry over.
434
473
 
435
474
  #### Copying Workers KV state into D1
436
475
 
@@ -462,7 +501,7 @@ then `wrangler login` and check the prepared deployment with a dry run.
462
501
  1. Back up KV with a bulk export including values and expiration metadata.
463
502
  Export D1 with `wrangler d1 export <db> --remote --output before-cutover.sql`.
464
503
  Store backups securely and preserve the exact credential encryption key,
465
- including One&Many's `CONNECTA_CREDENTIALS_KEY`.
504
+ whatever secret name the deployment stores it under.
466
505
  2. Block traffic using maintenance mode or turn the route off. Stop cron,
467
506
  queue consumers, scheduled jobs, and every other KV/D1 writer. Drain all
468
507
  in-flight requests, including OAuth callbacks and token refreshes.
@@ -488,8 +527,8 @@ then `wrangler login` and check the prepared deployment with a dry run.
488
527
  invalid entry or mismatch. Keep traffic and writers stopped until these
489
528
  counts are resolved. A verification pass does not rewrite copied entries.
490
529
  5. Deploy the D1 configuration with `wrangler deploy` while maintenance remains.
491
- Run `connecta doctor` using Access service credentials plus a stored `cta_`
492
- bearer through `CONNECTA_TOKEN`. Verify an existing
530
+ Run `connecta doctor --pool <pool>` using Access service credentials plus a
531
+ stored `cta_` bearer through `CONNECTA_TOKEN`. Verify an existing
493
532
  OAuth connector without re-consent, an existing `cta_` token, a connector
494
533
  using a vault credential, and prior activity in the Activity view. Run
495
534
  these controlled checks with other traffic and background writers blocked.
@@ -31,9 +31,15 @@
31
31
  * Claude's https://claude.ai/api/mcp/auth_callback plus ChatGPT's
32
32
  * https://chatgpt.com/connector_platform_oauth_redirect and
33
33
  * https://chatgpt.com/connector/oauth/* forms (see ../AGENTS.md).
34
+ * Then add one self-hosted Access application per hostname for
35
+ * `<host>/agent/*` with a single Bypass policy (Everyone), so agents send
36
+ * only their cta_ token (README.md § "Let agents through Access").
34
37
  * 5. Use the Workers Paid plan required by the `worker_loaders` binding.
35
- * 6. `wrangler deploy` from this folder (examples/worker), where wrangler.jsonc
36
- * lives. Point your MCP client at `<PUBLIC_URL>/mcp`.
38
+ * 6. List the maintainers' Access user ids (the identity's user_uuid, else
39
+ * its email) in the CONNECTA_MAINTAINERS var, comma-separated.
40
+ * 7. `wrangler deploy` from this folder (examples/worker), where wrangler.jsonc
41
+ * lives. Point a maintainer's MCP client at `<PUBLIC_URL>/mcp`, and an
42
+ * agent's at `<PUBLIC_URL>/agent/ci`.
37
43
  */
38
44
  import { api, defineConfig, remoteMcp } from "@zackbart/connecta";
39
45
  import { activityHistory } from "@zackbart/connecta/activity";
@@ -55,6 +61,8 @@ export interface Env {
55
61
  CREDENTIAL_ENCRYPTION_KEY?: string;
56
62
  DOWNSTREAM_TOKEN: string;
57
63
  PUBLIC_URL: string;
64
+ /** Comma-separated Access user ids (user_uuid, else email) of the maintainers. */
65
+ CONNECTA_MAINTAINERS: string;
58
66
  /** "on" switches on activity history, in CONNECTA_DB. */
59
67
  CONNECTA_ACTIVITY?: string;
60
68
  /**
@@ -77,26 +85,34 @@ export default defineConfig((env: Env) => {
77
85
  // Required adapter owns each run's handles; direct upstream construction throws.
78
86
  executor: workerExecutor({ loader: env.LOADER }),
79
87
  // Access owns edge admission and proves human identity for MCP and the
80
- // operator pages. Machines need Access service headers at the edge plus a
81
- // stored cta_ bearer inside connecta; a service identity alone is refused.
88
+ // operator pages. Agents use /agent/<pool>, which Access bypasses and
89
+ // connecta authenticates with the cta_ bearer alone; a service identity
90
+ // alone is refused everywhere.
82
91
  auth: [cloudflareAccessAuth()],
83
- // Always installed over the same D1 storage. Empty storage admits no
84
- // machine client; an Access-authenticated operator can provision tokens.
92
+ // Machine clients are agents declared here by name, each with one cta_
93
+ // token. An Access-authenticated operator issues, rotates, and revokes it
94
+ // in the operator UI; removing a name stops its token on the next request.
95
+ agents: ["ci"],
96
+ // Always installed over the same D1 storage. An agent with no issued token
97
+ // admits nothing.
85
98
  accessTokens: accessTokens(storage),
86
- // Code-owned roster. Access proves the identity; connecta derives
87
- // connector visibility and management permissions from the stable id it
88
- // supplies. Add `connectorAccess` and `activityAccess` to split members
89
- // from operators; here every signed-in human may manage every connector
90
- // and access token. Machine tokens never receive interactive authority.
91
- identity: {
92
- credentialAdministration: () => "all",
93
- personalConnection: () => "all",
94
- accessTokenManagement: () => true,
95
- },
96
- // Connectors that declare a `credential` slot become editable by every
97
- // signed-in human who can see that connector, encrypted with this key
98
- // before anything reaches D1. A saved replacement takes effect on the
99
- // next call; credentials fail at use.
99
+ // Code-owned roster. Access proves the identity; maintainers alone use
100
+ // plain /mcp, which serves every connector, and the operator UI, where they
101
+ // administer shared credentials, manage agent tokens, and read activity.
102
+ // Startup refuses an empty list.
103
+ maintainers: env.CONNECTA_MAINTAINERS.split(",")
104
+ .map((id) => id.trim())
105
+ .filter(Boolean),
106
+ // Everyone else uses a pool that lists them, which serves exactly its
107
+ // tools: people at /mcp/<name>, agents at /agent/<name>. Members are
108
+ // Access user ids or "agent:<name>". The starter pool is the agent's base
109
+ // pool (shared connectors, no owner), named after the agent as the agent
110
+ // pool layout requires.
111
+ pools: { ci: { tools: ["echo"], members: ["agent:ci"] } },
112
+ // Connectors that declare a `credential` slot become editable by a
113
+ // maintainer (shared) or by the member it belongs to (personal),
114
+ // encrypted with this key before anything reaches D1. A saved replacement
115
+ // takes effect on the next call; credentials fail at use.
100
116
  vault: env.CREDENTIAL_ENCRYPTION_KEY ? encryptedCredentialVault(storage, env.CREDENTIAL_ENCRYPTION_KEY) : undefined,
101
117
  // Payload-free activity at /ui/api/activity in CONNECTA_DB. The shared
102
118
  // D1 mapping persists the build version and validated client facts, and
@@ -13,6 +13,9 @@
13
13
  "vars": {
14
14
  // "CONNECTA_ACTIVITY": "on",
15
15
  "PUBLIC_URL": "https://connecta.example.workers.dev",
16
+ // Comma-separated Access user ids (user_uuid, else email) of the
17
+ // maintainers. This one matches the local dev identity below.
18
+ "CONNECTA_MAINTAINERS": "operator@example.com",
16
19
  },
17
20
 
18
21
  // Local-only Access identity. Remove this block to exercise the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zackbart/connecta",
3
- "version": "0.32.0",
3
+ "version": "0.33.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 six meta-tools.",
@@ -263,7 +263,8 @@
263
263
  "@cloudflare/codemode": "^0.5.0",
264
264
  "@cloudflare/vitest-pool-workers": "^0.18.8",
265
265
  "@cloudflare/workers-types": "^4.20250101.0 || ^5.0.0",
266
- "@fontsource-variable/inter": "^5.3.0",
266
+ "@fontsource-variable/geist": "^5.3.0",
267
+ "@fontsource-variable/geist-mono": "^5.3.0",
267
268
  "@playwright/test": "^1.62.0",
268
269
  "@radix-ui/react-dialog": "^1.2.0",
269
270
  "@radix-ui/react-slot": "^1.4.0",
@@ -26,6 +26,9 @@ CONNECTA_DATABASE=
26
26
  # may write a credential; machine access tokens grant no interactive authority.
27
27
  CLERK_PUBLISHABLE_KEY=
28
28
  CLERK_SECRET_KEY=
29
+ # Required with Clerk: comma-separated Clerk user ids of the maintainers, who
30
+ # alone use plain /mcp and the operator UI.
31
+ CONNECTA_MAINTAINERS=
29
32
 
30
33
  # Credential vault. Base64-encoded 32-byte AES key encrypting the values an
31
34
  # operator saves inside a connection on /. Generate one with:
@@ -2,6 +2,12 @@
2
2
 
3
3
  This repository is deployment configuration, not a copy of Connecta itself.
4
4
 
5
+ Start every operator task at
6
+ `node_modules/@zackbart/connecta/documentation/README.md#operator-tasks`.
7
+ That installed index routes adding/removing connectors, organizing agent pools,
8
+ adding people, connecting shared credentials, issuing/rotating/revoking agent
9
+ tokens, upgrading, and diagnosis with `connecta doctor` to their procedures.
10
+
5
11
  - Edit `src/connecta.config.ts` for connectors, authentication, storage, public
6
12
  URL, and optional modules. `src/index.ts` only starts it.
7
13
  - Keep `executor: quickJsExecutor()` for the prescribed six-tool code-first
@@ -19,15 +25,30 @@ This repository is deployment configuration, not a copy of Connecta itself.
19
25
  - Prefer `api()` when the agent must see an exact reviewed capability surface;
20
26
  `remoteMcp()` follows the downstream server's evolving tool catalog.
21
27
  - Optional modules (Clerk sign-in, credential vault, activity history) are type-checked code in `src/connecta.config.ts`, switched on by
22
- their environment variables. Follow README "Select optional modules". Auth management requires explicit
23
- `credentialAdministration` or `personalConnection` permissions; visibility
24
- alone never grants it. Clerk supplies human identity; `cta_` access tokens cover machine clients.
28
+ their environment variables. Follow README "Select optional modules". Shared auth management
29
+ requires a signed-in maintainer, and a pool member manages only their own
30
+ account on a personal connector in their pools; visibility alone never grants it. Clerk supplies human identity; machine clients are the
31
+ `agents` declared by name in `src/connecta.config.ts`, each with one `cta_` token.
25
32
  Keep `accessTokens(storage)` installed even before the first token exists.
26
- Bootstrap machines with `npm run provision-token -- "machine-name"` against
27
- the same database the server uses. `CONNECTA_TOKEN` is only for clients and
28
- doctor; never add a configured static bearer or an open startup mode.
33
+ Issue or rotate an agent's token with `npm run --silent rotate-token -- <agent>`
34
+ against the same configuration and database the server uses; bind a token
35
+ from before agents once with `npm run adopt-token -- <token-id> <agent>`.
36
+ Plain `/mcp` serves `maintainers` only; other people reach `/mcp/<pool>` and
37
+ agents `/agent/<pool>` for a pool listing them in `members` (`npm run doctor`
38
+ checks `/agent/local-machine`). Human endpoints refuse `cta_` tokens. `CONNECTA_TOKEN` is only for clients and doctor; never add a configured
39
+ static bearer or an open startup mode.
29
40
  All state, activity included, lives in the one SQLite file `CONNECTA_DATABASE`
30
41
  names, through `@zackbart/connecta/sqlite`.
42
+ - Organize agent pools by connecta's agent pool layout:
43
+ `node_modules/@zackbart/connecta/documentation/auth.md#organizing-an-agents-pools`
44
+ ([online](https://github.com/zackbart/connecta/blob/main/documentation/auth.md#organizing-an-agents-pools)).
45
+ Each agent keeps one Connecta connection whose client picks the pool per
46
+ turn: base pool `<agent>` (shared connectors, no `owner`) outside a
47
+ one-on-one, and `<agent>-<user>` (the base plus that user's own connector
48
+ ids, `owner` set to their Workspace address) only with that user. Never list
49
+ another person in a `<agent>-<user>` pool: its per-user connectors act as
50
+ that user for every member. Never add a second Connecta connection or mount
51
+ downstream tools directly.
31
52
  - Brand the operator UI in `operatorUi({ branding })`: product and owner names,
32
53
  description, favicon, and `theme` (`accent`, `radius`, `fontFamily`,
33
54
  `monoFamily`, `colorScheme`). Ask the deployment's owner for their brand
@@ -2,8 +2,8 @@
2
2
  # image installs @zackbart/connecta from the registry exactly like any other
3
3
  # consumer, so nothing here depends on the Connecta repository.
4
4
  #
5
- # Provision a stored machine token on the volume first; README.md gives the
6
- # exact build, provisioning, and startup commands. Empty storage is fail-closed.
5
+ # Issue a declared agent's token on the volume first; README.md gives the
6
+ # exact build, rotation, and startup commands. Empty storage is fail-closed.
7
7
  FROM node:22-slim
8
8
 
9
9
  WORKDIR /app