@zackbart/connecta 0.31.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 (169) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/README.md +30 -20
  3. package/bin/connecta.mjs +25 -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/auth-elicitation.js +1 -1
  10. package/dist/call-admission-context.js +15 -0
  11. package/dist/call-admission.js +30 -43
  12. package/dist/call-recovery.js +6 -2
  13. package/dist/catalog-service.js +7 -2
  14. package/dist/config-value-sources.js +14 -9
  15. package/dist/config.d.ts +58 -78
  16. package/dist/config.js +143 -47
  17. package/dist/connector-access.d.ts +4 -33
  18. package/dist/connector-access.js +15 -99
  19. package/dist/connector-caller.d.ts +7 -1
  20. package/dist/connector-caller.js +3 -2
  21. package/dist/connectors/api-connector.d.ts +27 -4
  22. package/dist/connectors/api-connector.js +2 -0
  23. package/dist/connectors/guarded-fetch.d.ts +9 -0
  24. package/dist/connectors/guarded-fetch.js +7 -0
  25. package/dist/connectors/remote-mcp.js +2 -0
  26. package/dist/describe-config.d.ts +32 -7
  27. package/dist/describe-config.js +9 -18
  28. package/dist/errors.d.ts +1 -1
  29. package/dist/errors.js +1 -0
  30. package/dist/execute.js +16 -14
  31. package/dist/index.d.ts +4 -3
  32. package/dist/index.js +30 -10
  33. package/dist/invocation.js +12 -7
  34. package/dist/membership.d.ts +51 -0
  35. package/dist/membership.js +121 -0
  36. package/dist/meta-tools.js +17 -19
  37. package/dist/module-contracts.d.ts +23 -6
  38. package/dist/operator-record.d.ts +2 -2
  39. package/dist/operator-record.js +2 -2
  40. package/dist/operator-ui/agents.d.ts +74 -0
  41. package/dist/operator-ui/agents.js +104 -0
  42. package/dist/operator-ui/contract.d.ts +151 -29
  43. package/dist/operator-ui/endpoints.js +192 -0
  44. package/dist/operator-ui/fix-prompts.js +4 -4
  45. package/dist/operator-ui/generated.js +5 -4
  46. package/dist/operator-ui/member-page.js +112 -0
  47. package/dist/operator-ui/model.d.ts +15 -7
  48. package/dist/operator-ui/view.d.ts +84 -19
  49. package/dist/operator-ui/view.js +83 -21
  50. package/dist/page-styles.js +1 -1
  51. package/dist/program-writes.js +2 -2
  52. package/dist/provider.d.ts +63 -11
  53. package/dist/provider.js +46 -7
  54. package/dist/providers/_shared/google/workspace.d.ts +53 -16
  55. package/dist/providers/_shared/google/workspace.js +31 -9
  56. package/dist/providers/_shared/rest/tools.js +2 -2
  57. package/dist/providers/_shared/rest/value-safety.d.ts +50 -0
  58. package/dist/providers/_shared/rest/value-safety.js +699 -7
  59. package/dist/providers/breeze/skill.generated.js +2 -2
  60. package/dist/providers/ccb/index.js +1 -1
  61. package/dist/providers/cloudflare/index.js +3 -3
  62. package/dist/providers/cloudflare/rest.js +25 -3
  63. package/dist/providers/cloudflare/skill.generated.js +2 -2
  64. package/dist/providers/cloudflare/value-safety.js +32 -0
  65. package/dist/providers/docs/index.d.ts +1 -1
  66. package/dist/providers/docs/index.js +5 -5
  67. package/dist/providers/docs/skill.generated.js +2 -2
  68. package/dist/providers/drive/index.d.ts +1 -1
  69. package/dist/providers/drive/index.js +4 -4
  70. package/dist/providers/drive/skill.generated.js +2 -2
  71. package/dist/providers/forms/index.d.ts +1 -1
  72. package/dist/providers/forms/index.js +5 -5
  73. package/dist/providers/forms/skill.generated.js +2 -2
  74. package/dist/providers/github/skill.generated.js +2 -2
  75. package/dist/providers/gmail/index.d.ts +1 -1
  76. package/dist/providers/gmail/index.js +279 -26
  77. package/dist/providers/gmail/skill.generated.js +2 -2
  78. package/dist/providers/linear/index.js +1 -1
  79. package/dist/providers/mixpanel/index.d.ts +27 -12
  80. package/dist/providers/mixpanel/index.js +58 -6
  81. package/dist/providers/mixpanel/operations.js +20 -0
  82. package/dist/providers/mixpanel/rest.js +603 -0
  83. package/dist/providers/mixpanel/skill.generated.js +3 -2
  84. package/dist/providers/mixpanel/value-safety.js +16 -0
  85. package/dist/providers/notion/index.d.ts +7 -0
  86. package/dist/providers/notion/index.js +116 -11
  87. package/dist/providers/notion/rest.js +183 -24
  88. package/dist/providers/notion/skill.generated.js +4 -4
  89. package/dist/providers/notion/value-safety.js +30 -5
  90. package/dist/providers/overflow/index.js +1 -1
  91. package/dist/providers/planning-center/index.d.ts +27 -0
  92. package/dist/providers/planning-center/index.js +287 -25
  93. package/dist/providers/planning-center/skill.generated.js +24 -4
  94. package/dist/providers/sheets/index.d.ts +4 -1
  95. package/dist/providers/sheets/index.js +9 -6
  96. package/dist/providers/sheets/skill.generated.js +2 -2
  97. package/dist/providers/slides/index.d.ts +1 -1
  98. package/dist/providers/slides/index.js +6 -6
  99. package/dist/providers/slides/skill.generated.js +2 -2
  100. package/dist/providers/stripe/rest.js +9 -2
  101. package/dist/providers/stripe/skill.generated.js +2 -2
  102. package/dist/providers/stripe/value-safety.js +22 -0
  103. package/dist/providers/vercel/index.js +2 -2
  104. package/dist/providers/vercel/rest.js +20 -2
  105. package/dist/providers/vercel/skill.generated.js +2 -2
  106. package/dist/providers/vercel/value-safety.js +22 -0
  107. package/dist/providers/wispr-flow/index.d.ts +14 -0
  108. package/dist/providers/wispr-flow/index.js +117 -0
  109. package/dist/providers/wispr-flow/skill.generated.js +8 -0
  110. package/dist/registry.d.ts +2 -3
  111. package/dist/registry.js +8 -9
  112. package/dist/routes/access-tokens.js +82 -75
  113. package/dist/routes/activity-disclosure.js +17 -18
  114. package/dist/routes/activity.js +34 -54
  115. package/dist/routes/actor-labels.js +33 -4
  116. package/dist/routes/agent-path.js +43 -0
  117. package/dist/routes/connect.js +16 -12
  118. package/dist/routes/credentials.js +16 -40
  119. package/dist/routes/mcp.js +155 -85
  120. package/dist/routes/oauth-management.js +11 -5
  121. package/dist/routes/oauth.js +3 -9
  122. package/dist/routes/operator.js +25 -17
  123. package/dist/routes/shared.d.ts +60 -20
  124. package/dist/routes/shared.js +187 -96
  125. package/dist/routes/ui-config.js +437 -97
  126. package/dist/routes/ui-data.js +2 -2
  127. package/dist/routes/ui.js +9 -16
  128. package/dist/runtime/call-admission.js +9 -0
  129. package/dist/server.js +8 -0
  130. package/dist/skills.js +5 -4
  131. package/dist/storage/keys.js +9 -1
  132. package/dist/tool-safety.js +13 -5
  133. package/dist/types.d.ts +12 -1
  134. package/dist/ui.d.ts +2 -2
  135. package/dist/ui.js +17 -15
  136. package/dist/usage-guide.js +3 -3
  137. package/dist/version.d.ts +1 -1
  138. package/dist/version.js +1 -1
  139. package/dist/workspace-address.js +9 -0
  140. package/dist/workspace-subject.js +23 -0
  141. package/documentation/README.md +46 -22
  142. package/documentation/architecture.md +378 -58
  143. package/documentation/auth.md +806 -215
  144. package/documentation/code-mode.md +93 -81
  145. package/documentation/deploying.md +128 -26
  146. package/documentation/integrating.md +145 -22
  147. package/documentation/meta-tools.md +58 -46
  148. package/documentation/migration-0.33.md +344 -0
  149. package/documentation/operating.md +14 -9
  150. package/documentation/operator-tests.md +117 -31
  151. package/documentation/operator-ui.md +326 -55
  152. package/documentation/provider-auth-migration.md +42 -27
  153. package/documentation/provider-migration-0.29.md +3 -3
  154. package/examples/worker/AGENTS.md +35 -8
  155. package/examples/worker/README.md +92 -53
  156. package/examples/worker/src/connecta.config.ts +41 -20
  157. package/examples/worker/wrangler.jsonc +3 -0
  158. package/package.json +7 -2
  159. package/templates/node/.env.example +3 -0
  160. package/templates/node/AGENTS.md +33 -10
  161. package/templates/node/Dockerfile +2 -2
  162. package/templates/node/README.md +71 -44
  163. package/templates/node/docker-compose.yml +3 -2
  164. package/templates/node/package.json +4 -3
  165. package/templates/node/src/adopt-token.ts +23 -0
  166. package/templates/node/src/connecta.config.ts +33 -12
  167. package/templates/node/src/rotate-token.ts +17 -0
  168. package/dist/tool-safety.d.ts +0 -9
  169. package/templates/node/src/provision-token.ts +0 -17
@@ -8,11 +8,12 @@ maintenance. This README and [local agent instructions](./AGENTS.md) own the
8
8
  runnable steps for a copied Node deployment.
9
9
 
10
10
  This is the prescribed Node deployment. It runs locally from source and as
11
- a container. Install, provision a machine token, then start it:
11
+ a container. Install, issue the declared `local-machine` agent's token, then
12
+ start it:
12
13
 
13
14
  ```sh
14
15
  npm install
15
- npm run --silent provision-token -- "local-machine"
16
+ npm run --silent rotate-token -- local-machine
16
17
  # Save the returned cta_ token privately. It is shown only once.
17
18
  npm start
18
19
  ```
@@ -24,39 +25,57 @@ warning, and npm 12 does not block the script. A later esbuild version needs a
24
25
  new explicit review and approval; do not replace the pinned entry with a broad
25
26
  package-name approval.
26
27
 
27
- Point an MCP client at `http://localhost:8787/mcp` with
28
- `Authorization: Bearer <returned-cta-token>`. `CONNECTA_TOKEN` is a client and
28
+ Point an agent's MCP client at `http://localhost:8787/agent/local-machine`, the agent
29
+ endpoint of the pool that lists `agent:local-machine`, with
30
+ `Authorization: Bearer <returned-cta-token>`. Agents use only `/agent/<pool>`:
31
+ `/mcp` and `/mcp/<pool>` are for people and refuse `cta_` tokens, and plain
32
+ `/mcp` serves only the `maintainers`. `CONNECTA_TOKEN` is a client and
29
33
  doctor variable only. Setting it never configures server authentication.
30
34
 
31
- `accessTokens(storage)` is always installed. With no stored tokens and no
32
- Clerk keys, the server can start and answer `/health`, but every MCP request
33
- is refused. There is no open startup or configured static bearer fallback.
35
+ `accessTokens(storage)` is always installed. With no issued agent token and
36
+ no Clerk keys, the server can start and answer `/health`, but every MCP
37
+ request is refused. There is no open startup or configured static bearer
38
+ fallback.
34
39
 
35
- ## Provision machine tokens
40
+ ## Agent tokens
36
41
 
37
- Run `npm run --silent provision-token -- "machine-name"` from this project
38
- on a trusted machine. `src/provision-token.ts` imports the installed package's
39
- `AccessTokenManager`, then calls `manager.create(name, "local-provisioning")`
40
- on `sqliteStorage(openSqlite(process.env.CONNECTA_DATABASE || "./.connecta.sqlite"))`.
41
- The server uses that same database path. If you override it, use the same
42
- `CONNECTA_DATABASE` for provisioning and startup:
42
+ Machine clients are agents, declared by name in `agents` in
43
+ `src/connecta.config.ts`; the template declares `local-machine`. Each agent
44
+ has one token. Run `npm run --silent rotate-token -- <agent>` from this
45
+ project on a trusted machine to issue it, or to rotate it: the previous secret
46
+ stops working on its next request. `src/rotate-token.ts` loads the same
47
+ configuration the server does, so it uses the same database and refuses an
48
+ agent the configuration does not declare. If you override the database path,
49
+ use the same `CONNECTA_DATABASE` for rotation and startup:
43
50
 
44
51
  ```sh
45
52
  export CONNECTA_DATABASE=/absolute/path/connecta.sqlite
46
- npm run --silent provision-token -- "build-agent"
53
+ npm run --silent rotate-token -- local-machine
47
54
  npm start
48
55
  ```
49
56
 
50
57
  The local commands read the process environment; they do not load `.env`.
51
58
  Compose reads `.env` for the container commands below. Keep the returned
52
- secret in a client secret store and out of source control and logs. Each run
53
- creates a new token; it cannot recover an old secret. Only the hash persists.
54
- The token is unbound to a human principal and has no interactive management
55
- authority. Its name is a label, and deployment identity and pool rules decide
56
- its access on every request. To revoke it locally, use `manager.list()` to
57
- find its id, then `manager.revoke(id, "local-provisioning")` against that same
58
- storage. An explicitly permitted Clerk operator can also manage tokens.
59
- Never expose this provisioning script as an HTTP route or run it at startup.
59
+ secret in a client secret store and out of source control and logs. A
60
+ rotation cannot recover an old secret; only the hash persists. An agent has no
61
+ human principal, no personal connectors, and no interactive management
62
+ authority; deployment identity and pool rules decide its access on every
63
+ request through `identity.agent`. To stop an agent, remove its name from
64
+ `agents` and restart, or revoke its token with
65
+ `new AccessTokenManager(storage, { agents }).revoke(name)` against the same
66
+ storage. An explicitly permitted Clerk operator can also issue, rotate, and
67
+ revoke from the operator UI. Never expose these scripts as an HTTP route or
68
+ run them at startup.
69
+
70
+ ### Tokens issued before agents
71
+
72
+ Tokens from earlier releases stay in storage but stop admitting until they are
73
+ bound to an agent. Declare the agent, then run `npm run adopt-token` with no
74
+ arguments to list the machine tokens that can be bound, and
75
+ `npm run adopt-token -- <token-id> <agent>` to bind one, so that machine's
76
+ existing secret keeps working. A token binds once, and only to an agent whose
77
+ token was never issued. Human-bound tokens issued from the operator UI cannot
78
+ be adopted and stay refused; issue the agent a new token instead.
60
79
 
61
80
  ## Run it in Docker
62
81
 
@@ -66,12 +85,12 @@ Same source, same configuration, one long-lived service:
66
85
  cp .env.example .env
67
86
  # edit .env for the database path and any optional modules
68
87
  docker compose build
69
- docker compose run --rm --no-deps connecta npm run --silent provision-token -- "container-machine"
88
+ docker compose run --rm --no-deps connecta npm run --silent rotate-token -- local-machine
70
89
  # Save the returned cta_ token privately for clients and doctor.
71
90
  docker compose up -d
72
91
  ```
73
92
 
74
- The provisioning container writes through the installed package API into the
93
+ The rotation container writes through the installed package API into the
75
94
  same named volume and `CONNECTA_DATABASE` as the service. An empty volume
76
95
  admits no machine client. Compose never passes `CONNECTA_TOKEN` to the server;
77
96
  no environment value can substitute for a stored token.
@@ -121,12 +140,14 @@ whether or not it is switched on.
121
140
 
122
141
  The template explicitly enables `ui: operatorUi()` from
123
142
  `@zackbart/connecta/ui`. Open `http://localhost:8787/` and supply a
124
- stored machine token to inspect Connections. Sign in with Clerk for human
143
+ declared agent's token to inspect Connections. Sign in with Clerk for human
125
144
  connection management. Omit that option and import for an API-only
126
145
  server. OAuth callbacks remain in core even with no UI.
127
146
 
128
- The page labels each tool as a read or write. Trusted programs may write;
129
- read-only pools use `call_destructive_tool`, with approval controlled by the host. For
147
+ The page labels each tool as a read or write and each endpoint as writable or
148
+ read-only. `src/connecta.config.ts` must set `readOnly`: a read-only endpoint
149
+ refuses every write call and leaves write tools out of search, and the host
150
+ controls approval on a writable one. For
130
151
  classified connection failures it offers a fixed repair prompt, and its
131
152
  endpoint section has client setup commands for `/mcp` and any pool available
132
153
  to the signed-in identity. Those commands contain no bearer token.
@@ -142,7 +163,10 @@ Connection management needs an interactive identity. A machine access token
142
163
  never authorizes browser credential mutations. `@clerk/backend`
143
164
  ships as a dependency of this template. To enable Clerk, set both
144
165
  `CLERK_PUBLISHABLE_KEY` and `CLERK_SECRET_KEY` (one without the other refuses to
145
- start) and set `PUBLIC_URL`.
166
+ start), set `PUBLIC_URL`, and list your Clerk user id in `CONNECTA_MAINTAINERS`
167
+ (comma-separated; startup refuses Clerk without a maintainer). Maintainers
168
+ alone use plain `/mcp` and the operator UI; add other people to a pool's
169
+ `members` in `src/connecta.config.ts`.
146
170
  Enable `aud_claim_enabled: true` in Clerk's instance OAuth application settings
147
171
  (`PATCH /v1/instance/oauth_application_settings` in Clerk's Backend API), then
148
172
  read the setting back. Both JWT and opaque OAuth tokens are supported.
@@ -162,18 +186,20 @@ audience. New DCR registrations need new entries, so prefer audience binding
162
186
  for standard host onboarding. Clerk session tokens work only on operator
163
187
  routes. [Inbound auth](https://github.com/zackbart/connecta/blob/main/documentation/auth.md#clerk-oauth-tokens-and-operator-sessions)
164
188
  explains configuration, verification, and fixed rejection reason codes.
165
- Machine clients use connecta-issued `cta_` access tokens
166
- through `accessTokens(storage)`, which `src/connecta.config.ts` configures on
167
- the database. Grant explicit `identity.accessTokenManagement` permissions to
168
- let a signed-in human mint them. Trusted local provisioning above works without
189
+ Machine clients are the declared `agents`, each using a connecta-issued
190
+ `cta_` token through `accessTokens(storage)`, which `src/connecta.config.ts`
191
+ configures on the database. A signed-in maintainer issues, rotates, and
192
+ revokes them from the operator UI. Trusted local rotation above works without
169
193
  Clerk and never requires opening the server.
170
194
 
171
- Set the code-owned identity resolvers deliberately. `connectorAccess` governs
172
- use; `credentialAdministration` permits shared-auth changes, and
173
- `personalConnection` permits the signed-in principal's personal-auth changes.
174
- Both management permissions default to none. Use `authScope: "personal"` for a
175
- connector where each person should connect their own downstream account.
176
- `activityAccess` separately selects readers of global activity.
195
+ Set who uses what in code: `maintainers` for plain `/mcp` and the operator UI,
196
+ and each pool's `members` for `/mcp/<pool>` (people) and `/agent/<pool>`
197
+ (agents), which serve exactly that pool's `tools`. Agent endpoints publish no
198
+ protected-resource metadata and accept only `cta_` tokens. Maintainers alone
199
+ administer shared credentials and OAuth grants, manage agent tokens, and read
200
+ activity. Use `authScope: "personal"` for a connector where each person should
201
+ connect their own downstream account: a member connects their own account on
202
+ the personal connectors in their pools.
177
203
 
178
204
  ### Credential vault
179
205
 
@@ -218,7 +244,7 @@ Diagnostics are independent. Keep the default logger or provide your own;
218
244
 
219
245
  The UI displays connections and current permissions. Configuration still owns
220
246
  the connector set, tool definitions, and access rules. Access token management
221
- requires explicit Clerk operator permission. There is no team roster or policy editor.
247
+ requires a signed-in Clerk maintainer. There is no team roster or policy editor.
222
248
 
223
249
  ## Deployment contract
224
250
 
@@ -266,8 +292,9 @@ feature should not leave a tab behind.
266
292
  Upgrading from v0.23 does not require rotating managed `cta_…` tokens. Import
267
293
  `accessTokens` from `@zackbart/connecta/auth/access-tokens`, replace the old
268
294
  `accessTokens: true` with `accessTokens: accessTokens(storage)`, and keep the
269
- same persistent storage namespace and identity/tool/pool grant rules. Enable
270
- `identity.accessTokenManagement` only for the interactive operators who should
271
- manage tokens. New issuance needs storage with atomic `compareAndSet`; older
272
- storage adapters can still verify existing tokens. See the package's
295
+ same persistent storage namespace. From 0.33 `maintainers` and pool `members`
296
+ replace `identity.connectorAccess` and every `grant`, and maintainers replace
297
+ the four `identity` management permissions; delete them and the `identity`
298
+ section, and follow the package's `documentation/migration-0.33.md`. From 0.33 a stored token admits only once it is bound to a
299
+ declared agent: follow "Tokens issued before agents" above. See the package's
273
300
  `documentation/auth.md` for the migration and storage requirements.
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # cp .env.example .env
4
4
  # docker compose build
5
- # docker compose run --rm --no-deps connecta npm run --silent provision-token -- "machine-name"
5
+ # docker compose run --rm --no-deps connecta npm run --silent rotate-token -- local-machine
6
6
  # docker compose up -d --build
7
7
  #
8
8
  # Compose reads `.env` from this directory, so one file configures both the
@@ -16,7 +16,7 @@ services:
16
16
  ports:
17
17
  - "${PORT:-8787}:${PORT:-8787}"
18
18
  environment:
19
- # accessTokens(storage) is always installed; an empty database is closed.
19
+ # accessTokens(storage) is always installed; an agent with no issued token is closed.
20
20
  # CONNECTA_TOKEN belongs to clients and is never configured on this server.
21
21
  PORT: ${PORT:-8787}
22
22
  # Downstream OAuth calls back to this origin. Unset is fine until this
@@ -28,6 +28,7 @@ services:
28
28
  # on when set and off when empty.
29
29
  CLERK_PUBLISHABLE_KEY: ${CLERK_PUBLISHABLE_KEY:-}
30
30
  CLERK_SECRET_KEY: ${CLERK_SECRET_KEY:-}
31
+ CONNECTA_MAINTAINERS: ${CONNECTA_MAINTAINERS:-}
31
32
  CONNECTA_CREDENTIAL_KEY: ${CONNECTA_CREDENTIAL_KEY:-}
32
33
  CONNECTA_ACTIVITY: ${CONNECTA_ACTIVITY:-}
33
34
  volumes:
@@ -10,14 +10,15 @@
10
10
  "esbuild@0.28.2": true
11
11
  },
12
12
  "scripts": {
13
- "doctor": "connecta doctor",
14
- "provision-token": "tsx src/provision-token.ts",
13
+ "adopt-token": "tsx src/adopt-token.ts",
14
+ "doctor": "connecta doctor --pool local-machine",
15
+ "rotate-token": "tsx src/rotate-token.ts",
15
16
  "start": "tsx src/index.ts",
16
17
  "typecheck": "tsc --noEmit"
17
18
  },
18
19
  "dependencies": {
19
20
  "@clerk/backend": "^3.12.0",
20
- "@zackbart/connecta": "0.31.0",
21
+ "@zackbart/connecta": "0.33.0",
21
22
  "quickjs-emscripten": "0.32.0"
22
23
  },
23
24
  "devDependencies": {
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Trusted one-time migration: bind a machine token issued before agents to a
3
+ * declared agent, so that machine's existing cta_ secret keeps working. Run
4
+ * with no arguments to list the tokens that can be adopted. A token binds
5
+ * once, only to an agent that has never been issued one; human-bound tokens
6
+ * are refused and stop admitting.
7
+ */
8
+ import { AccessTokenManager } from "@zackbart/connecta/auth/access-tokens";
9
+ import connectaConfig from "./connecta.config.js";
10
+
11
+ const [tokenId, agent] = process.argv.slice(2);
12
+ const { storage, agents } = connectaConfig(process.env);
13
+ const manager = new AccessTokenManager(storage!, { agents: agents ?? [] });
14
+ if (tokenId === undefined) {
15
+ for (const token of await manager.unboundTokens()) {
16
+ process.stdout.write(`${token.id} ${token.createdAt} ${token.name}\n`);
17
+ }
18
+ } else if (!agent || process.argv.length !== 4) {
19
+ throw new Error("Usage: npm run adopt-token -- [<token-id> <agent>]");
20
+ } else {
21
+ await manager.adopt(tokenId, agent);
22
+ process.stdout.write(`${agent} now uses token ${tokenId}\n`);
23
+ }
@@ -25,6 +25,7 @@ export interface Env {
25
25
  CONNECTA_DATABASE?: string;
26
26
  CLERK_PUBLISHABLE_KEY?: string;
27
27
  CLERK_SECRET_KEY?: string;
28
+ CONNECTA_MAINTAINERS?: string;
28
29
  CONNECTA_CREDENTIAL_KEY?: string;
29
30
  CONNECTA_ACTIVITY?: string;
30
31
  }
@@ -68,22 +69,42 @@ export default defineConfig((env: Env) => {
68
69
  return {
69
70
  storage,
70
71
  publicUrl,
72
+ // Required: can /mcp write? false lets call_destructive_tool and programs
73
+ // write, with the MCP client approving; true leaves write tools out of
74
+ // search and describe and refuses every write call. Pools inherit it
75
+ // unless they set their own readOnly. Startup fails without it.
76
+ readOnly: false,
71
77
  // Required: model-written programs run in a bounded QuickJS child.
72
78
  executor: quickJsExecutor(),
73
79
  auth: clerk ? [clerk] : [],
74
- // Always installed: an empty database refuses machine requests. Provision
75
- // locally with `npm run provision-token -- "machine-name"`, or grant an
76
- // interactive human `identity.accessTokenManagement` for later issuance.
77
- // v0.23 client tokens survive `connecta migrate-state` on this storage.
80
+ // Required with Clerk: the Clerk user ids (comma-separated in
81
+ // CONNECTA_MAINTAINERS) who use plain /mcp, which serves every connector,
82
+ // and the operator UI. They alone administer shared credentials, manage
83
+ // agent tokens, and read activity. Everyone else uses a pool that lists
84
+ // them. Agents are never maintainers.
85
+ maintainers: clerk
86
+ ? (set("CONNECTA_MAINTAINERS") ?? "")
87
+ .split(",")
88
+ .map((id) => id.trim())
89
+ .filter(Boolean)
90
+ : [],
91
+ // Each pool serves exactly its tools to its members: Clerk user ids at
92
+ // /mcp/<name>, and "agent:<name>" for a declared agent at /agent/<name>.
93
+ // A member connects their own account on the pool's personal connectors.
94
+ // `npm run doctor` checks /agent/local-machine with its token. The starter
95
+ // pool is the agent's base pool (shared connectors, no owner), named after
96
+ // the agent as the agent pool layout requires.
97
+ pools: { "local-machine": { tools: ["time"], members: ["agent:local-machine"] } },
98
+ // Machine clients are agents declared here by name, each with one cta_
99
+ // token. Issue or rotate one locally with
100
+ // `npm run --silent rotate-token -- local-machine`, or as a signed-in
101
+ // maintainer from the operator UI. Removing a name stops its token on the
102
+ // next request.
103
+ agents: ["local-machine"],
104
+ // Always installed: a slot with no issued token refuses every request.
105
+ // Tokens from before agents stop admitting until `npm run adopt-token`
106
+ // binds each machine's token to an agent; human-bound tokens never do.
78
107
  accessTokens: accessTokens(storage),
79
- // Connection management permissions default to none, so the template
80
- // grants them. Split members from operators with `connectorAccess`,
81
- // `activityAccess`, and `accessTokenManagement`, each derived from the
82
- // authenticated identity and never from an MCP argument.
83
- identity: {
84
- credentialAdministration: () => "all",
85
- personalConnection: () => "all",
86
- },
87
108
  vault: credentialKey ? encryptedCredentialVault(storage, credentialKey) : undefined,
88
109
  // Payload-free activity history at /activity: who called what, when, how
89
110
  // long it took, and whether it worked. Each write prunes rows older than
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Trusted local provisioning: issue or rotate one declared agent's token
3
+ * against the same configuration and database the server uses. The previous
4
+ * secret stops working on its next request.
5
+ */
6
+ import { AccessTokenManager } from "@zackbart/connecta/auth/access-tokens";
7
+ import connectaConfig from "./connecta.config.js";
8
+
9
+ const agent = process.argv[2];
10
+ if (!agent || process.argv.length !== 3) {
11
+ throw new Error("Usage: npm run --silent rotate-token -- <agent>");
12
+ }
13
+ const { storage, agents } = connectaConfig(process.env);
14
+ const manager = new AccessTokenManager(storage!, { agents: agents ?? [] });
15
+ const { token } = await manager.rotate(agent, "local-provisioning");
16
+ // The secret is returned once. Keep stdout private; storage holds its hash.
17
+ process.stdout.write(`${token}\n`);
@@ -1,9 +0,0 @@
1
- import type { ToolDef, ToolVerdict } from "./types.js";
2
- export type PoolTrust = "trusted" | "read-only";
3
- /** Deployment override, then verified provider review, then fail-closed hints. */
4
- export declare function classifyTool(definition: ToolDef, override?: "read" | "write", review?: {
5
- verdict: ToolVerdict;
6
- stale?: boolean;
7
- }): "read" | "write";
8
- /** The only permission decision for a connector call. Approval is host-owned. */
9
- export declare function surfaceAllowsTool(classification: ToolDef["classification"], surface: string, trust?: PoolTrust): boolean;
@@ -1,17 +0,0 @@
1
- /** Trusted local provisioning against the same database the server uses. */
2
- import { AccessTokenManager } from "@zackbart/connecta/auth/access-tokens";
3
- import { openSqlite, sqliteStorage } from "@zackbart/connecta/sqlite";
4
-
5
- const name = process.argv[2];
6
- if (!name || process.argv.length !== 3) {
7
- throw new Error('Usage: npm run provision-token -- "machine-name"');
8
- }
9
- const database = openSqlite(process.env.CONNECTA_DATABASE || "./.connecta.sqlite");
10
- try {
11
- const manager = new AccessTokenManager(sqliteStorage(database));
12
- const { token } = await manager.create(name, "local-provisioning");
13
- // The secret is returned once. Keep stdout private; storage holds its hash.
14
- process.stdout.write(`${token}\n`);
15
- } finally {
16
- database.close();
17
- }