@zackbart/connecta 0.34.1 → 0.36.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 (441) hide show
  1. package/CHANGELOG.md +418 -0
  2. package/README.md +65 -105
  3. package/SECURITY.md +3 -2
  4. package/bin/connecta.mjs +41 -16
  5. package/bin/version.mjs +1 -1
  6. package/dist/access-tokens.d.ts +2 -1
  7. package/dist/access-tokens.js +66 -30
  8. package/dist/activity.d.ts +1 -1
  9. package/dist/activity.js +2 -0
  10. package/dist/agent-config.d.ts +23 -0
  11. package/dist/agent-config.js +59 -0
  12. package/dist/agent-description.js +16 -0
  13. package/dist/agent-identity.js +4 -4
  14. package/dist/agent-observation.js +39 -0
  15. package/dist/auth/clerk-sdk/client.d.ts +2 -0
  16. package/dist/auth/clerk-sdk/generated.js +554 -563
  17. package/dist/auth/clerk-transport.js +70 -3
  18. package/dist/auth/clerk.d.ts +13 -0
  19. package/dist/auth/clerk.js +653 -246
  20. package/dist/auth/cloudflare-access.js +46 -42
  21. package/dist/auth/downstream-client-metadata.js +2 -2
  22. package/dist/auth/downstream-oauth.js +2044 -1161
  23. package/dist/auth/downstream-revocation.js +88 -88
  24. package/dist/auth/native-call.js +20 -0
  25. package/dist/auth/native.js +184 -0
  26. package/dist/auth/oauth-start-reset.js +11 -5
  27. package/dist/auth/oauth-token-response.js +121 -102
  28. package/dist/auth/oauth-v2-migration.js +97 -78
  29. package/dist/auth/renewable-grant.js +10 -0
  30. package/dist/auth/static-oauth.js +751 -281
  31. package/dist/auth/vercel-agent.js +266 -0
  32. package/dist/auth-admission.js +204 -0
  33. package/dist/auth-elicitation.js +350 -241
  34. package/dist/authorization-handoff.js +164 -90
  35. package/dist/call-admission-context.js +3 -10
  36. package/dist/call-admission.js +2 -0
  37. package/dist/call-classification.js +9 -0
  38. package/dist/call-recovery.js +32 -13
  39. package/dist/canonical-json.js +8 -0
  40. package/dist/catalog-cache.js +369 -213
  41. package/dist/catalog-service.js +659 -530
  42. package/dist/catalog.js +5 -1
  43. package/dist/concurrency.js +0 -25
  44. package/dist/config-defaults.d.ts +1 -0
  45. package/dist/config-defaults.js +1 -0
  46. package/dist/config-schema.d.ts +3 -1
  47. package/dist/config-schema.js +13 -3
  48. package/dist/config.d.ts +40 -6
  49. package/dist/config.js +40 -26
  50. package/dist/connector-caller.d.ts +4 -0
  51. package/dist/connector-caller.js +17 -4
  52. package/dist/connectors/api-connector.d.ts +29 -7
  53. package/dist/connectors/api-connector.js +145 -57
  54. package/dist/connectors/guarded-fetch.d.ts +51 -17
  55. package/dist/connectors/guarded-fetch.js +215 -121
  56. package/dist/connectors/negotiation-cache.js +20 -10
  57. package/dist/connectors/remote-mcp.d.ts +10 -30
  58. package/dist/connectors/remote-mcp.js +955 -563
  59. package/dist/d1.js +10 -243
  60. package/dist/derived-context.js +5 -0
  61. package/dist/describe-config.d.ts +3 -1
  62. package/dist/describe-config.js +2 -0
  63. package/dist/downstream-input.js +347 -260
  64. package/dist/errors.d.ts +87 -6
  65. package/dist/errors.js +114 -4
  66. package/dist/execute.js +87 -50
  67. package/dist/executor-admission.js +3 -0
  68. package/dist/executors/quickjs.js +163 -112
  69. package/dist/guarded-transport.js +50 -0
  70. package/dist/guest-runtime.js +1 -1
  71. package/dist/http/client.js +439 -0
  72. package/dist/http/errors.js +558 -0
  73. package/dist/http/fields.js +49 -0
  74. package/dist/http/hatch-paging.js +318 -0
  75. package/dist/http/hatch.js +1279 -0
  76. package/dist/http/json-schema.js +267 -0
  77. package/dist/http/node-transport.js +241 -0
  78. package/dist/http/oauth.js +162 -0
  79. package/dist/http/paging.js +227 -0
  80. package/dist/http/provider.js +221 -0
  81. package/dist/http/safety.js +82 -0
  82. package/dist/http/services.js +79 -0
  83. package/dist/http/tools.js +379 -0
  84. package/dist/http/vendor-index.js +138 -0
  85. package/dist/http/vendor.js +448 -0
  86. package/dist/http/write-request.js +66 -0
  87. package/dist/http-policy.d.ts +11 -0
  88. package/dist/http-policy.js +45 -0
  89. package/dist/inbound-credential.js +1 -1
  90. package/dist/index.d.ts +2 -2
  91. package/dist/index.js +4 -0
  92. package/dist/invocation-auth.js +30 -19
  93. package/dist/invocation.js +218 -85
  94. package/dist/mcp-result.js +0 -4
  95. package/dist/membership.d.ts +4 -0
  96. package/dist/meta-output.js +1 -1
  97. package/dist/meta-tools.js +531 -406
  98. package/dist/native-recovery.js +52 -0
  99. package/dist/native-tools.js +382 -0
  100. package/dist/node.d.ts +4 -3
  101. package/dist/node.js +45 -8
  102. package/dist/oauth-handoff.js +177 -104
  103. package/dist/oauth-partition.js +6 -20
  104. package/dist/oauth-sealing.js +6 -7
  105. package/dist/operation-vocabulary.js +101 -0
  106. package/dist/operator-record.d.ts +2 -2
  107. package/dist/operator-record.js +17 -4
  108. package/dist/operator-ui/contract.d.ts +8 -3
  109. package/dist/operator-ui/generated.js +4 -4
  110. package/dist/operator-ui/model.d.ts +2 -3
  111. package/dist/program-writes.js +47 -26
  112. package/dist/provider-call-facts.d.ts +115 -0
  113. package/dist/provider-call-facts.js +182 -0
  114. package/dist/provider-call.d.ts +112 -0
  115. package/dist/provider-call.js +271 -0
  116. package/dist/provider.d.ts +65 -31
  117. package/dist/provider.js +116 -39
  118. package/dist/providers/_shared/google/hatch.js +91 -0
  119. package/dist/providers/_shared/google/kit.js +427 -0
  120. package/dist/providers/_shared/google/result-size.js +15 -2
  121. package/dist/providers/_shared/google/service-account.d.ts +2 -1
  122. package/dist/providers/_shared/google/service-account.js +8 -4
  123. package/dist/providers/_shared/google/workspace.d.ts +6 -210
  124. package/dist/providers/_shared/google/workspace.js +6 -425
  125. package/dist/providers/_shared/rest/compact-index.js +86 -0
  126. package/dist/providers/_shared/rest/kit.js +146 -0
  127. package/dist/providers/_shared/rest/operation-index.js +230 -14
  128. package/dist/providers/_shared/rest/response-media.js +11 -0
  129. package/dist/providers/_shared/rest/value-safety.js +353 -195
  130. package/dist/providers/_shared/sensitive-fields.js +27 -0
  131. package/dist/providers/breeze/api.js +584 -0
  132. package/dist/providers/breeze/index.d.ts +1 -3
  133. package/dist/providers/breeze/index.js +67 -1420
  134. package/dist/providers/breeze/openapi.generated.js +60 -0
  135. package/dist/providers/breeze/skill.generated.js +2 -6
  136. package/dist/providers/breeze/tools.js +696 -0
  137. package/dist/providers/ccb/api.js +496 -0
  138. package/dist/providers/ccb/index.d.ts +15 -37
  139. package/dist/providers/ccb/index.js +77 -1702
  140. package/dist/providers/ccb/lib/constants.d.ts +34 -0
  141. package/dist/providers/ccb/lib/constants.js +74 -0
  142. package/dist/providers/ccb/lib/guide.js +9 -0
  143. package/dist/providers/ccb/lib/verdicts.js +387 -0
  144. package/dist/providers/ccb/openapi.generated.js +397 -0
  145. package/dist/providers/ccb/safety.js +295 -0
  146. package/dist/providers/ccb/skill.generated.js +2 -6
  147. package/dist/providers/ccb/tools.js +853 -0
  148. package/dist/providers/cloudflare/api.js +1066 -0
  149. package/dist/providers/cloudflare/index.d.ts +5 -22
  150. package/dist/providers/cloudflare/index.js +79 -197
  151. package/dist/providers/cloudflare/lib/config.d.ts +16 -0
  152. package/dist/providers/cloudflare/lib/config.js +2 -0
  153. package/dist/providers/cloudflare/lib/discovery.js +29 -0
  154. package/dist/providers/cloudflare/{graphql.js → lib/graphql.js} +1 -1
  155. package/dist/providers/cloudflare/lib/hatch.js +103 -0
  156. package/dist/providers/cloudflare/lib/path-bounds.js +28 -0
  157. package/dist/providers/cloudflare/lib/response-media.js +75 -0
  158. package/dist/providers/cloudflare/openapi.generated.js +4 -3
  159. package/dist/providers/cloudflare/{value-safety.js → safety.js} +487 -0
  160. package/dist/providers/cloudflare/skill.generated.js +2 -10
  161. package/dist/providers/cloudflare/tools.js +360 -0
  162. package/dist/providers/docs/api.js +51 -0
  163. package/dist/providers/docs/index.d.ts +1 -12
  164. package/dist/providers/docs/index.js +53 -1165
  165. package/dist/providers/docs/lib/constants.d.ts +18 -0
  166. package/dist/providers/docs/lib/constants.js +18 -0
  167. package/dist/providers/docs/lib/discovery.js +26 -0
  168. package/dist/providers/docs/lib/requests.js +48 -0
  169. package/dist/providers/docs/lib/schemas.js +168 -0
  170. package/dist/providers/docs/openapi.generated.js +16 -0
  171. package/dist/providers/docs/safety.js +54 -0
  172. package/dist/providers/docs/skill.generated.js +2 -6
  173. package/dist/providers/docs/tools.js +801 -0
  174. package/dist/providers/drive/api.js +177 -0
  175. package/dist/providers/drive/index.d.ts +1 -12
  176. package/dist/providers/drive/index.js +38 -1448
  177. package/dist/providers/drive/lib/constants.d.ts +6 -0
  178. package/dist/providers/drive/lib/constants.js +6 -0
  179. package/dist/providers/drive/lib/contracts.js +532 -0
  180. package/dist/providers/drive/lib/hatch.js +197 -0
  181. package/dist/providers/drive/openapi.generated.js +77 -0
  182. package/dist/providers/drive/safety.js +90 -0
  183. package/dist/providers/drive/skill.generated.js +2 -6
  184. package/dist/providers/drive/tools.js +1290 -0
  185. package/dist/providers/forms/api.js +71 -0
  186. package/dist/providers/forms/index.d.ts +1 -12
  187. package/dist/providers/forms/index.js +49 -1145
  188. package/dist/providers/forms/lib/constants.d.ts +14 -0
  189. package/dist/providers/forms/lib/constants.js +19 -0
  190. package/dist/providers/forms/lib/hatch.js +72 -0
  191. package/dist/providers/forms/openapi.generated.js +23 -0
  192. package/dist/providers/forms/safety.js +34 -0
  193. package/dist/providers/forms/skill.generated.js +2 -8
  194. package/dist/providers/forms/tools.js +1023 -0
  195. package/dist/providers/github/api.js +150 -0
  196. package/dist/providers/github/index.d.ts +4 -5
  197. package/dist/providers/github/index.js +30 -87
  198. package/dist/providers/github/{auth.d.ts → lib/auth.d.ts} +5 -3
  199. package/dist/providers/github/{auth.js → lib/auth.js} +40 -43
  200. package/dist/providers/github/lib/discovery.js +132 -0
  201. package/dist/providers/github/lib/operations.js +58 -0
  202. package/dist/providers/github/{scope.d.ts → lib/scope.d.ts} +2 -0
  203. package/dist/providers/github/{scope.js → lib/scope.js} +25 -1
  204. package/dist/providers/github/openapi.generated.js +1216 -0
  205. package/dist/providers/github/safety.js +216 -0
  206. package/dist/providers/github/skill.generated.js +2 -5
  207. package/dist/providers/github/tools.js +506 -0
  208. package/dist/providers/gmail/api.js +114 -0
  209. package/dist/providers/gmail/index.d.ts +1 -12
  210. package/dist/providers/gmail/index.js +73 -1621
  211. package/dist/providers/gmail/lib/constants.d.ts +13 -0
  212. package/dist/providers/gmail/lib/constants.js +16 -0
  213. package/dist/providers/gmail/lib/index.js +173 -0
  214. package/dist/providers/gmail/lib/schemas.js +176 -0
  215. package/dist/providers/gmail/lib/threading.js +120 -0
  216. package/dist/providers/gmail/openapi.generated.js +92 -0
  217. package/dist/providers/gmail/safety.js +21 -0
  218. package/dist/providers/gmail/skill.generated.js +2 -6
  219. package/dist/providers/gmail/tools.js +1435 -0
  220. package/dist/providers/overflow/api.js +1008 -0
  221. package/dist/providers/overflow/index.d.ts +1 -17
  222. package/dist/providers/overflow/index.js +60 -1488
  223. package/dist/providers/overflow/skill.generated.js +2 -5
  224. package/dist/providers/overflow/tools.js +873 -0
  225. package/dist/providers/planning-center/api.js +467 -0
  226. package/dist/providers/planning-center/index.d.ts +4 -35
  227. package/dist/providers/planning-center/index.js +69 -2445
  228. package/dist/providers/planning-center/lib/constants.d.ts +58 -0
  229. package/dist/providers/planning-center/lib/constants.js +83 -0
  230. package/dist/providers/planning-center/lib/discovery.js +40 -0
  231. package/dist/providers/planning-center/lib/guide.js +39 -0
  232. package/dist/providers/planning-center/lib/json-api.js +83 -0
  233. package/dist/providers/planning-center/lib/verdicts.js +1758 -0
  234. package/dist/providers/planning-center/openapi.generated.js +1764 -0
  235. package/dist/providers/planning-center/safety.js +188 -0
  236. package/dist/providers/planning-center/skill.generated.js +2 -27
  237. package/dist/providers/planning-center/tools.js +1010 -0
  238. package/dist/providers/quickbooks/api.js +299 -0
  239. package/dist/providers/quickbooks/index.d.ts +13 -0
  240. package/dist/providers/quickbooks/index.js +93 -0
  241. package/dist/providers/quickbooks/lib/query.js +118 -0
  242. package/dist/providers/quickbooks/lib/writes.js +168 -0
  243. package/dist/providers/quickbooks/safety.js +222 -0
  244. package/dist/providers/quickbooks/skill.generated.js +5 -0
  245. package/dist/providers/quickbooks/tools.js +117 -0
  246. package/dist/providers/sheets/api.js +133 -0
  247. package/dist/providers/sheets/index.d.ts +4 -16
  248. package/dist/providers/sheets/index.js +61 -1172
  249. package/dist/providers/sheets/lib/constants.d.ts +14 -0
  250. package/dist/providers/sheets/lib/constants.js +14 -0
  251. package/dist/providers/sheets/lib/hatch.js +163 -0
  252. package/dist/providers/sheets/lib/requests.js +105 -0
  253. package/dist/providers/sheets/lib/schemas.js +138 -0
  254. package/dist/providers/sheets/openapi.generated.js +30 -0
  255. package/dist/providers/sheets/safety.js +27 -0
  256. package/dist/providers/sheets/skill.generated.js +2 -7
  257. package/dist/providers/sheets/tools.js +733 -0
  258. package/dist/providers/slides/api.js +147 -0
  259. package/dist/providers/slides/index.d.ts +2 -10
  260. package/dist/providers/slides/index.js +48 -2555
  261. package/dist/providers/slides/lib/constants.d.ts +11 -0
  262. package/dist/providers/slides/lib/constants.js +11 -0
  263. package/dist/providers/slides/lib/requests.js +60 -0
  264. package/dist/providers/slides/lib/schemas.js +565 -0
  265. package/dist/providers/slides/openapi.generated.js +18 -0
  266. package/dist/providers/slides/skill.generated.js +2 -6
  267. package/dist/providers/slides/tools.js +1968 -0
  268. package/dist/providers/stripe/api.js +401 -0
  269. package/dist/providers/stripe/index.d.ts +5 -27
  270. package/dist/providers/stripe/index.js +74 -210
  271. package/dist/providers/stripe/lib/config.d.ts +4 -0
  272. package/dist/providers/stripe/lib/config.js +2 -0
  273. package/dist/providers/stripe/openapi.generated.js +4 -3
  274. package/dist/providers/stripe/{value-safety.js → safety.js} +5 -5
  275. package/dist/providers/stripe/skill.generated.js +2 -11
  276. package/dist/providers/stripe/tools.js +77 -0
  277. package/dist/providers/tithely/api.js +531 -0
  278. package/dist/providers/tithely/index.d.ts +17 -0
  279. package/dist/providers/tithely/index.js +86 -965
  280. package/dist/providers/tithely/skill.generated.js +2 -5
  281. package/dist/providers/tithely/tools.js +426 -0
  282. package/dist/providers/vercel/api.js +551 -0
  283. package/dist/providers/vercel/index.d.ts +9 -25
  284. package/dist/providers/vercel/index.js +65 -260
  285. package/dist/providers/vercel/lib/config.d.ts +2 -0
  286. package/dist/providers/vercel/lib/config.js +2 -0
  287. package/dist/providers/vercel/lib/index.js +117 -0
  288. package/dist/providers/vercel/lib/logs.js +100 -0
  289. package/dist/providers/vercel/lib/pin.js +270 -0
  290. package/dist/providers/vercel/openapi.generated.js +10 -5
  291. package/dist/providers/vercel/{value-safety.js → safety.js} +247 -38
  292. package/dist/providers/vercel/skill.generated.js +2 -7
  293. package/dist/providers/vercel/tools.js +642 -0
  294. package/dist/redaction-identity.js +47 -0
  295. package/dist/redaction-marker.js +125 -0
  296. package/dist/registry.d.ts +21 -15
  297. package/dist/registry.js +409 -376
  298. package/dist/routes/access-tokens.js +1 -1
  299. package/dist/routes/actor-labels.js +1 -1
  300. package/dist/routes/connect.js +1 -1
  301. package/dist/routes/dispatch.js +103 -0
  302. package/dist/routes/mcp.js +170 -79
  303. package/dist/routes/oauth.js +6 -1
  304. package/dist/routes/operator.js +2 -2
  305. package/dist/routes/shared.d.ts +5 -4
  306. package/dist/routes/shared.js +244 -177
  307. package/dist/routes/skills.js +10 -8
  308. package/dist/routes/ui-config.js +27 -3
  309. package/dist/routes/ui-data.js +21 -20
  310. package/dist/routes/ui.js +1 -1
  311. package/dist/runtime/call-admission.js +0 -26
  312. package/dist/runtime/call-entry.js +30 -0
  313. package/dist/runtime/call.js +552 -0
  314. package/dist/runtime/connector-scope.js +7 -0
  315. package/dist/runtime/credential-probe.js +34 -0
  316. package/dist/runtime/executor.js +40 -0
  317. package/dist/runtime/native-storage.js +63 -0
  318. package/dist/runtime/registry.js +27 -0
  319. package/dist/runtime/request-scope.js +72 -0
  320. package/dist/runtime/run.js +15 -7
  321. package/dist/runtime/shared-read.js +34 -5
  322. package/dist/runtime/storage.js +34 -21
  323. package/dist/sent-secrets.d.ts +194 -37
  324. package/dist/sent-secrets.js +1743 -340
  325. package/dist/server.js +5 -56
  326. package/dist/skills.js +322 -223
  327. package/dist/sqlite.js +15 -14
  328. package/dist/storage/d1-copy.js +255 -0
  329. package/dist/storage/d1-driver.js +15 -0
  330. package/dist/storage/keys.js +28 -0
  331. package/dist/storage/sql-driver.js +88 -0
  332. package/dist/storage/sql-native.js +544 -0
  333. package/dist/storage/sql-schema.d.ts +6 -0
  334. package/dist/storage/sql-schema.js +15 -0
  335. package/dist/storage/sql.d.ts +14 -20
  336. package/dist/storage/sql.js +29 -535
  337. package/dist/timeout.js +0 -26
  338. package/dist/tool-safety.js +20 -10
  339. package/dist/types.d.ts +9 -109
  340. package/dist/ui.d.ts +1 -1
  341. package/dist/ui.js +0 -1
  342. package/dist/upward.js +3 -0
  343. package/dist/usage-guide.js +3 -1
  344. package/dist/validate.d.ts +4 -2
  345. package/dist/validate.js +86 -9
  346. package/dist/version.d.ts +1 -1
  347. package/dist/version.js +1 -1
  348. package/dist/worker.js +312 -292
  349. package/dist/workspace-subject.js +5 -1
  350. package/dist/write-outcome.js +454 -34
  351. package/documentation/README.md +28 -7
  352. package/documentation/architecture.md +2409 -255
  353. package/documentation/auth.md +867 -114
  354. package/documentation/code-mode.md +49 -16
  355. package/documentation/deploying.md +164 -20
  356. package/documentation/hosted-mcp-migration.md +460 -0
  357. package/documentation/integrating.md +129 -144
  358. package/documentation/meta-tools.md +409 -32
  359. package/documentation/operating.md +27 -3
  360. package/documentation/operator-tests.md +3 -0
  361. package/documentation/operator-ui.md +84 -48
  362. package/documentation/provider-auth-migration.md +16 -63
  363. package/documentation/provider-migration-0.29.md +10 -5
  364. package/documentation/provider-migration-0.35.md +975 -0
  365. package/documentation/providers.md +1347 -0
  366. package/examples/worker/AGENTS.md +23 -9
  367. package/examples/worker/README.md +254 -135
  368. package/examples/worker/alchemy.run.ts +116 -0
  369. package/examples/worker/gitignore +10 -0
  370. package/examples/worker/src/connecta.config.ts +23 -21
  371. package/examples/worker/wrangler.jsonc +14 -5
  372. package/guides/connecting-agents.md +157 -0
  373. package/guides/how-it-works.md +156 -0
  374. package/guides/safety-and-trust.md +153 -0
  375. package/package.json +30 -50
  376. package/templates/node/.env.example +3 -0
  377. package/templates/node/README.md +22 -2
  378. package/templates/node/docker-compose.yml +1 -0
  379. package/templates/node/package.json +3 -2
  380. package/templates/node/src/connecta.config.ts +8 -0
  381. package/dist/catalog-drift.d.ts +0 -138
  382. package/dist/catalog-drift.js +0 -578
  383. package/dist/operator-ui/agents.d.ts +0 -74
  384. package/dist/operator-ui/agents.js +0 -104
  385. package/dist/operator-ui/config-summary.js +0 -565
  386. package/dist/operator-ui/connection-map.js +0 -376
  387. package/dist/operator-ui/endpoints.js +0 -221
  388. package/dist/operator-ui/fix-prompts.d.ts +0 -18
  389. package/dist/operator-ui/fix-prompts.js +0 -87
  390. package/dist/operator-ui/health.js +0 -52
  391. package/dist/operator-ui/member-page.js +0 -97
  392. package/dist/operator-ui/overview.js +0 -123
  393. package/dist/operator-ui/setup-commands.js +0 -51
  394. package/dist/operator-ui/view.d.ts +0 -401
  395. package/dist/operator-ui/view.js +0 -705
  396. package/dist/providers/_shared/request-token.js +0 -83
  397. package/dist/providers/_shared/rest/dispatch.js +0 -41
  398. package/dist/providers/_shared/rest/operation-index.d.ts +0 -133
  399. package/dist/providers/_shared/rest/tools.d.ts +0 -141
  400. package/dist/providers/_shared/rest/tools.js +0 -569
  401. package/dist/providers/_shared/rest/value-safety.d.ts +0 -201
  402. package/dist/providers/basecamp/index.d.ts +0 -45
  403. package/dist/providers/basecamp/index.js +0 -997
  404. package/dist/providers/basecamp/skill.generated.js +0 -9
  405. package/dist/providers/cloudflare/rest.d.ts +0 -37
  406. package/dist/providers/cloudflare/rest.js +0 -1097
  407. package/dist/providers/github/hosted.js +0 -218
  408. package/dist/providers/github/rest.js +0 -321
  409. package/dist/providers/infisical/index.d.ts +0 -7
  410. package/dist/providers/infisical/index.js +0 -713
  411. package/dist/providers/infisical/skill.generated.js +0 -8
  412. package/dist/providers/intercom/index.d.ts +0 -24
  413. package/dist/providers/intercom/index.js +0 -121
  414. package/dist/providers/intercom/skill.generated.js +0 -8
  415. package/dist/providers/linear/index.d.ts +0 -55
  416. package/dist/providers/linear/index.js +0 -537
  417. package/dist/providers/linear/skill.generated.js +0 -8
  418. package/dist/providers/mixpanel/index.d.ts +0 -59
  419. package/dist/providers/mixpanel/index.js +0 -472
  420. package/dist/providers/mixpanel/operations.js +0 -20
  421. package/dist/providers/mixpanel/rest.js +0 -603
  422. package/dist/providers/mixpanel/skill.generated.js +0 -10
  423. package/dist/providers/mixpanel/value-safety.js +0 -16
  424. package/dist/providers/notion/index.d.ts +0 -67
  425. package/dist/providers/notion/index.js +0 -368
  426. package/dist/providers/notion/openapi.generated.js +0 -75
  427. package/dist/providers/notion/rest.d.ts +0 -24
  428. package/dist/providers/notion/rest.js +0 -1226
  429. package/dist/providers/notion/skill.generated.js +0 -11
  430. package/dist/providers/notion/value-safety.js +0 -56
  431. package/dist/providers/revenuecat/index.d.ts +0 -51
  432. package/dist/providers/revenuecat/index.js +0 -697
  433. package/dist/providers/revenuecat/skill.generated.js +0 -11
  434. package/dist/providers/stripe/rest.d.ts +0 -14
  435. package/dist/providers/stripe/rest.js +0 -402
  436. package/dist/providers/vercel/rest.d.ts +0 -20
  437. package/dist/providers/vercel/rest.js +0 -1020
  438. package/dist/providers/wispr-flow/index.d.ts +0 -14
  439. package/dist/providers/wispr-flow/index.js +0 -117
  440. package/dist/providers/wispr-flow/skill.generated.js +0 -8
  441. /package/dist/providers/github/{key.js → lib/key.js} +0 -0
@@ -6,16 +6,16 @@
6
6
  and package maintenance. This guide owns identity, membership, and downstream
7
7
  credential contracts; use [deployment setup](./deploying.md) for runnable steps.
8
8
 
9
- | Task | Sections |
10
- | ------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
11
- | Connect a machine or migrate an old client | [Agent tokens](#agent-tokens-and-upgrading-from-earlier-tokens), [agent endpoints](#agent-endpoints), [static bearer migration](#migrating-static-bearer-clients) |
12
- | Configure caller visibility, read-only endpoints, or auth ownership | [Principals and operators](#principals-visibility-and-operators), [pools](#pools), [shared and personal auth](#shared-and-personal-auth) |
13
- | Lay out an agent's pools | [Organizing an agent's pools](#organizing-an-agents-pools) |
14
- | Set up human sign-in | [Access on Workers](#cloudflare-access-on-workers), [Clerk tokens and sessions](#clerk-oauth-tokens-and-operator-sessions), [management](#human-authentication-management) |
15
- | Enable credential or connection controls | [Management permissions](#management-permissions), [start/restart/disconnect](#starting-restarting-and-disconnecting) |
16
- | Diagnose consent or token refresh | [State at rest](#downstream-oauth-state-at-rest), [callbacks](#consents-and-callbacks), [refresh failures](#refresh-failures), [safe errors](#what-a-servers-errors-may-say) |
17
- | Configure a downstream OAuth client or HTTP handler | [Client registration](#downstream-oauth-client-registration), [api() OAuth](#downstream-oauth-on-api), [request-local bearer tokens](#request-local-downstream-bearer-tokens) |
18
- | Review browser or downstream URL boundaries | [Origins](#origins), [advertised URLs](#urls-a-downstream-advertises), [one server per grant](#one-server-per-grant) |
9
+ | Task | Sections |
10
+ | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
11
+ | Connect a machine or migrate an old client | [Agent tokens](#agent-tokens-and-upgrading-from-earlier-tokens), [agent endpoints](#agent-endpoints), [static bearer migration](#migrating-static-bearer-clients) |
12
+ | Configure caller visibility, read-only endpoints, or auth ownership | [Principals and operators](#principals-visibility-and-operators), [pools](#pools), [shared and personal auth](#shared-and-personal-auth) |
13
+ | Lay out an agent's pools | [Organizing an agent's pools](#organizing-an-agents-pools) |
14
+ | Set up human sign-in | [Access on Workers](#cloudflare-access-on-workers), [Clerk tokens and sessions](#clerk-oauth-tokens-and-operator-sessions), [Clerk Organizations](#one-clerk-instance-one-organization-per-deployment), [management](#human-authentication-management) |
15
+ | Enable credential or connection controls | [Management permissions](#management-permissions), [start/restart/disconnect](#starting-restarting-and-disconnecting) |
16
+ | Diagnose consent or token refresh | [State at rest](#downstream-oauth-state-at-rest), [callbacks](#consents-and-callbacks), [refresh failures](#refresh-failures), [safe errors](#what-a-servers-errors-may-say) |
17
+ | Configure a downstream OAuth client or HTTP handler | [Client registration](#downstream-oauth-client-registration), [api() OAuth](#downstream-oauth-on-api), [request-local bearer tokens](#request-local-downstream-bearer-tokens) |
18
+ | Review browser or downstream URL boundaries | [Origins](#origins), [advertised URLs](#urls-a-downstream-advertises), [one server per grant](#one-server-per-grant) |
19
19
 
20
20
  [Credential recognition and admission](#credential-recognition-and-admission)
21
21
  defines refusal precedence.
@@ -30,7 +30,8 @@ Finish [Clerk setup](#clerk-oauth-tokens-and-operator-sessions) or
30
30
  provider yet. A machine-only deployment cannot admit people or manage shared
31
31
  auth through the operator UI.
32
32
 
33
- 1. Admit the person in the identity provider's sign-in policy. Obtain their
33
+ 1. Admit the person in the identity provider's sign-in policy. With Clerk's
34
+ `organizationId`, add them to the deployment's Clerk Organization. Obtain their
34
35
  stable principal id there: Clerk's user id, or Access's `user_uuid` (email
35
36
  only when no `user_uuid` is supplied). A display name or an email for a
36
37
  Clerk user is not their principal id.
@@ -223,15 +224,262 @@ Clerk, a `cta_` token refused at `/mcp` or `/mcp/<pool>` still receives Clerk's
223
224
  resource metadata challenge.
224
225
  An open deployment with connectors warns at construction.
225
226
 
227
+ ### Authentication capacity and ingress limiting
228
+
229
+ Connecta admits at most **16 active inbound adapter authorizations** per
230
+ loaded core in a process or Worker isolate. Human MCP endpoints, agent endpoints,
231
+ operator APIs, connection starts (`/connect/<connector>`), and OAuth callbacks
232
+ share this fixed bound, including network calls made inside an adapter's SDK.
233
+ It has no queue and no configuration option. When full, a request receives HTTP
234
+ 503 with `Retry-After: 1`, `Cache-Control: no-store`, and
235
+ `error: { code: "authentication_busy", message: "Authentication capacity reached.", retryable: true }`.
236
+ The operator log records only the checked `authentication_busy` reason, never
237
+ credentials or identity-provider diagnostics. Retry after the indicated delay;
238
+ Connecta does not automatically retry the authentication.
239
+
240
+ Underlying work stays charged until it settles, including cancellation cleanup.
241
+ A caller leaving alone never releases capacity. After an authorization's hard
242
+ deadline plus **one second of settlement grace**, unfinished work moves from the
243
+ active pool to a separate **abandoned allowance of 16 operations**. The native
244
+ Access lookup and agent-token verification have five-second deadlines; Clerk's
245
+ deadline is ten seconds, and custom adapters share that ten-second threshold.
246
+ The allowance equals the active limit, so normal authentication continues
247
+ while a partial wave of stalled operations is abandoned, and at most **32
248
+ authorizations** are ever outstanding per loaded core. A full wave of 16
249
+ stalled operations fills the allowance. While the allowance is full, no
250
+ authorization starts, even when active slots are free. Overdue work that finds
251
+ the allowance full keeps its active slot until an abandoned operation ends and
252
+ makes room. A binding that never settles therefore fills the allowance and
253
+ fails closed instead of accumulating I/O.
254
+
255
+ Each charge ends exactly once, in one of two ways:
256
+
257
+ - **Settlement.** The adapter's promise and every native operation it
258
+ registered have settled. This applies on every runtime and through every
259
+ entry.
260
+ - **Unreachable work.** Nothing can ever settle the charge: the garbage
261
+ collector has freed every promise and callback that could have. On Workers
262
+ this follows invocation cancellation. Cloudflare ends a fetch invocation at
263
+ most 30 seconds after its response is sent or its client disconnects
264
+ ([`waitUntil` limit](https://developers.cloudflare.com/workers/runtime-apis/context/)),
265
+ and drops each I/O operation it still owns without running its callbacks.
266
+ Once the collector frees that work, Connecta releases the charge, and the
267
+ next admission logs the checked reason `authentication_unreachable`.
268
+
269
+ Release through unreachable work is **eventual, never timed**. It waits for a
270
+ garbage collection, which runs as the process or isolate allocates. On
271
+ workerd, a collection triggered by allocation can keep a recently freed object
272
+ for several later collections, so one charge may linger after the rest are
273
+ released. While anything can still settle the work, the charge counts: a
274
+ connected caller, middleware that reads Connecta's response and then awaits
275
+ other work, and a scheduled handler that is still running all keep their
276
+ charges. A late settlement after such a release changes nothing, so no count
277
+ is released twice.
278
+
279
+ Release through unreachable work needs `FinalizationRegistry`. Node always
280
+ provides it. On Workers, set the `enable_weak_ref` compatibility flag, or use
281
+ a compatibility date of 2025-05-05 or later, and keep `enable_request_signal`;
282
+ the installed Worker example sets both flags. Without `FinalizationRegistry`,
283
+ only settlement releases a charge. A native authentication operation that
284
+ hangs past invocation cancellation then consumes one abandoned slot until the
285
+ isolate is recycled, and 16 such operations in one isolate make every later
286
+ authentication return `authentication_busy`, visible on `/health`.
287
+
288
+ A request refused because the allowance is full receives the same 503
289
+ `authentication_busy` response and headers. The operator log records the
290
+ checked reason `authentication_abandoned_saturated` instead. `GET /health`
291
+ reports `admission.authentication` with `active`, `limit`, `abandoned`,
292
+ `abandonedLimit`, `abandonedSaturated`, and `unreachableRelease`, for example:
293
+
294
+ ```json
295
+ {
296
+ "active": 0,
297
+ "limit": 16,
298
+ "abandoned": 16,
299
+ "abandonedLimit": 16,
300
+ "abandonedSaturated": true,
301
+ "unreachableRelease": true
302
+ }
303
+ ```
304
+
305
+ `unreachableRelease` is `true` when the runtime provides
306
+ `FinalizationRegistry`. On Workers, `false` means neither `enable_weak_ref` nor
307
+ a compatibility date of 2025-05-05 or later is in effect. A saturated allowance
308
+ refuses all new authentication until an underlying operation settles or its
309
+ unreachable work is collected. Investigate the identity binding or token
310
+ storage; restarting the process or isolate clears its local accounting.
311
+
312
+ Clerk creates a client per authorization: SDK verification (opaque
313
+ access tokens, JWT/JWKS, browser handshakes and session refresh), domain checks,
314
+ and gate-client lookups all use that request's signal and a ten-second
315
+ authentication deadline. Its transport reads at most 1 MiB per SDK response,
316
+ cancels the upstream body on every exit, and gives the SDK only buffered bytes.
317
+ JWKS fetches make one attempt; cancellation starts no retry timer. Additional
318
+ Backend API admission reads retain their 64 KiB response bound.
319
+
320
+ On Workers, the event's `ctx` reaches Connecta through the `ctx` argument of
321
+ `connecta.fetch`. Its `waitUntil` keeps only that request's
322
+ already-running authorization and cleanup runnable after disconnect, so an
323
+ aborted upstream settles before its slot is released. No other request shares
324
+ its promise or I/O. Custom adapters must honor `request.signal` and return a promise that settles
325
+ only after their work ends; ignoring cancellation consumes the bounded
326
+ abandoned allowance, and work the promise does not await is not counted.
327
+ The current adapters cannot cheaply establish a fully authenticated cache hit
328
+ before verification, so every adapter invocation counts, including one that
329
+ later uses a cached membership or policy verdict. Cache hits finish their slots
330
+ quickly; a cached policy verdict never bypasses credential verification.
331
+ Domain/gate and organization caches store only completed verdicts, never a
332
+ lookup failure, cancellation, or timeout. A `gate` that returns `false` after
333
+ its request was cancelled is not cached either. The JWKS cache stores only
334
+ signing keys from a completed fetch.
335
+ An adapter returning a synchronous local verdict releases before yielding;
336
+ it consumes no pending slot between requests.
337
+
338
+ This bound is independent of MCP request/program admission and downstream call
339
+ limits. `Mcp-Method: subscriptions/listen` still defers its MCP request permit:
340
+ after authentication and endpoint admission, a body-confirmed modern listen is
341
+ refused promptly even when execution capacity is full. It always passes the
342
+ authentication bound, and no request body is read before authentication.
343
+
344
+ Routes that do not invoke an inbound verifier consume no authentication slot:
345
+ health, public OAuth discovery and client metadata, UI shells and static assets,
346
+ preflight, redirects, unknown paths, and local header/origin/path refusals.
347
+ An invalid `/connect` handoff or unknown OAuth callback target also refuses
348
+ before a verifier. Downstream OAuth exchanges and display-label reads are
349
+ separate operations, with their existing limits; this bound covers the inbound
350
+ authentication that admits their caller. Cloudflare Access edge authentication
351
+ runs before the Worker; its in-Worker identity lookup is covered. Access uses
352
+ only the trusted runtime binding, with no JWT/JWKS fetch. That native binding
353
+ and token storage expose no cancellation API. Both answer promptly on caller
354
+ cancellation or their five-second deadline and keep the unfinished operation
355
+ in active or abandoned accounting until it settles. The five-second agent
356
+ deadline covers hashing and every verification read together, and returns the
357
+ machine `401` challenge; no further storage read starts after cancellation or
358
+ timeout. A late native result never admits its original caller.
359
+
360
+ This is a concurrency bound, **not a rate limit or a shared provider quota**.
361
+ Require ingress rate limiting for public deployments across all authenticated
362
+ paths, including `/agent/*`, `/ui/*`, `/connect/*`, and `/oauth/callback/*`.
363
+ For example, a deployment at `https://assistant.example.com` using Clerk must
364
+ budget all isolates and deployments sharing its Clerk secret key together.
365
+ Fast invalid tokens and repeated cache misses can exhaust a provider's request
366
+ budget without ever exceeding 16 active authentications. Set ingress limits
367
+ to that provider's quota and expected traffic; increasing MCP concurrency does
368
+ not increase authentication capacity.
369
+
226
370
  Clerk denial logs carry checked fixed reason codes. Provider user IDs, email
227
371
  addresses, and email domains stay out of denial logs, activity, and status,
228
372
  including email lookup failures and admission policy refusals.
229
373
 
374
+ ## Vercel workload credentials
375
+
376
+ Available from 0.35.0; earlier releases do not support workload admission.
377
+
378
+ An agent running on Vercel can present its project's signed OIDC token instead
379
+ of provisioning a Connecta token. Declare the binding beside its name:
380
+
381
+ ```ts
382
+ agents: [{
383
+ name: "assistant",
384
+ vercel: {
385
+ team: "team_example", // immutable owner_id, required; team slugs are refused
386
+ project: "prj_example", // immutable project_id (preferred), or exact project name
387
+ // environments: ["production"], // default; preview/development require explicit entries
388
+ },
389
+ }],
390
+ pools: {
391
+ assistant: { tools: ["crm", "tickets"], members: ["agent:assistant"] },
392
+ },
393
+ ```
394
+
395
+ Enable OIDC federation in the calling Vercel project's security settings.
396
+ Use exact values from that project's settings; wildcards and empty environment
397
+ lists are refused. The team must be its immutable `team_...` ID, matched against
398
+ `owner_id`; a team slug is not accepted. Prefer an immutable `prj_...` project ID,
399
+ matched against `project_id`. A project name instead matches `project` and follows
400
+ that name, including reuse within the team. Project names may contain dots, such
401
+ as `assistant.example.com`; colons are refused. A workload that could match two
402
+ agents is refused at construction; use project IDs consistently across agents
403
+ to avoid ambiguous bindings.
404
+
405
+ The default admits production only. List `preview` or `development` explicitly
406
+ when those environments should have the same pool access. Every deployment of
407
+ the bound project in an allowed environment can act as the agent. This is
408
+ project workload identity, with no end-user identity or forwarding. Enabling
409
+ `development` lets project members who can obtain development tokens act as the
410
+ agent. Enabling `preview` lets code on any pushed preview branch act as the agent.
411
+
412
+ Treat the project's OIDC token as a bearer credential for all of this agent's
413
+ pools. Its default audience, `https://vercel.com/<team-slug>`, is shared by every
414
+ service accepting Vercel's default OIDC tokens; it is not specific to Connecta.
415
+ Anything holding the project's token can act as the agent:
416
+ project code, builds (including build steps and dependency install scripts with
417
+ access to `VERCEL_OIDC_TOKEN`), and services the project sends the token to,
418
+ including other agents and third-party APIs. A holder may be able to obtain
419
+ fresh Vercel tokens through `@vercel/oidc`'s exchange, so access may outlast the
420
+ original token's expiry. Binding a project means trusting everything that can
421
+ obtain that project's tokens. Send them only to services trusted with the same
422
+ agent access. Signature verification establishes the project identity; it does
423
+ not isolate Connecta from these other token holders.
424
+
425
+ Both team (`https://oidc.vercel.com/<team-slug>`) and global
426
+ (`https://oidc.vercel.com`) issuer modes work. Connecta verifies RS256 signatures
427
+ against Vercel's fixed public JWKS, exact issuer/audience/subject consistency,
428
+ the configured team/project/environment, required expiration and issued-at,
429
+ and not-before when present, with 30 seconds of clock skew. External-user
430
+ tokens carrying `external_sub` are refused. Public keys cache for five minutes;
431
+ an unknown key can trigger one additional refresh per 30 seconds. Failed fetches
432
+ back off for 30 seconds and never admit with expired cached keys. Fetches have
433
+ the same five-second authentication deadline as agent tokens. Neither token
434
+ contents nor upstream errors enter activity; activity names the declared agent.
435
+ If the two concurrent key-fetch slots are occupied on a cold or expired cache,
436
+ the endpoint returns 503 with `Retry-After: 1`, rather than rejecting the credential
437
+ with 401.
438
+
439
+ Send the token as `Authorization: Bearer <OIDC token>` to
440
+ `https://connecta.example.com/agent/assistant`. The inbound
441
+ `x-vercel-oidc-token` header alone is not a Connecta credential. In Eve:
442
+
443
+ ```ts
444
+ import { getVercelOidcToken } from "@vercel/oidc";
445
+ import { defineMcpClientConnection } from "eve/connections";
446
+
447
+ export default defineMcpClientConnection({
448
+ url: "https://connecta.example.com/agent/assistant",
449
+ description: "Use the assistant's shared CRM and ticket tools.",
450
+ auth: {
451
+ principalType: "app",
452
+ getToken: async () => ({ token: await getVercelOidcToken() }),
453
+ },
454
+ });
455
+ ```
456
+
457
+ Resolve the token inside the request through `getToken`; never obtain it at
458
+ module initialization or cache it. Vercel supplies request tokens through
459
+ `x-vercel-oidc-token`; the helper can also read `VERCEL_OIDC_TOKEN` in builds
460
+ or development. Eve's inbound `vercelOidc()` and `vercelSubject()` configure
461
+ who may call Eve, separately from this outbound connection.
462
+
463
+ OIDC-only agents need no `accessTokens` module. To accept either credential
464
+ for the same agent, also configure `accessTokens(storage)`; its token slot,
465
+ rotation and revocation keep their existing behavior. Revoking a `cta_` token
466
+ does not revoke the workload binding: remove the binding or agent in code to
467
+ stop OIDC admission. Both credentials reach exactly the pools listing the
468
+ agent. Human endpoints refuse Vercel workload credentials without consulting
469
+ human adapters. Removing the agent stops both credential paths.
470
+
471
+ The [operator agent view](./operator-ui.md#operator-data-contract) lists each
472
+ declared agent's supported credential kinds and latest sampled successful pool
473
+ admission. Last seen is payload-free admission evidence sampled within a minute,
474
+ independent of completed-call history; a refused credential or pool does not
475
+ update it. Recording is best effort within 500 ms or the shorter configured probe
476
+ deadline; failure loses a sample and a later eligible admission can try again.
477
+
230
478
  ## Agent tokens and upgrading from earlier tokens
231
479
 
232
- Machine clients are **agents**, declared by name in code. `agents: string[]`
233
- lists them: each name matches `^[a-z0-9_-]+$`, at most 64 characters, and is
234
- unique; anything else, or agents without `accessTokens`, throws at
480
+ Machine clients are **agents**, declared by name in code. String entries in `agents`
481
+ list them: each name matches `^[a-z0-9_-]+$`, at most 64 characters, and is
482
+ unique; anything else, or token-only agents without `accessTokens`, throws at
235
483
  construction. `describeConfig()` lists the names and never token state.
236
484
  `accessTokens` from `@zackbart/connecta/auth/access-tokens` installs the `cta_`
237
485
  inbound adapter and, beside `ui`, the Tokens page and `/ui/access-tokens`
@@ -322,7 +570,7 @@ request without any storage write. An admitted agent's identity is:
322
570
  }
323
571
  ```
324
572
 
325
- `identity.agent` is set only by the built-in adapter; a result from any other
573
+ `identity.agent` is set only by built-in agent credential adapters; a result from any other
326
574
  inbound provider that carries `agent` is refused like an unknown token. An
327
575
  agent has no principal, so personal connectors are absent for it and it never
328
576
  reads activity. Its subject partitions result handles per
@@ -491,7 +739,7 @@ A member reference is one of:
491
739
  - `"agent:<name>"`, naming an agent declared in `agents` (pool `members` only).
492
740
 
493
741
  A person matches when the admitted principal equals the reference exactly; an
494
- agent matches by `identity.agent`, which only the built-in token adapter sets.
742
+ agent matches by `identity.agent`, which only built-in agent credential adapters set.
495
743
  Construction throws on a bare id without exactly one interactive
496
744
  provider, a bare id containing `:`, an undeclared agent, a repeated reference,
497
745
  an empty or missing pool `members`, an agent in `maintainers`, `maintainers`
@@ -607,8 +855,8 @@ and reports.
607
855
  ## Pools
608
856
 
609
857
  A pool is a named slice of the deployment served to its members: people at
610
- `/mcp/<pool>` and agents at `/agent/<pool>`. A support team sees three Notion
611
- tools and Linear, a calendar bot sees one tool, both over the same credentials. Private remote catalog
858
+ `/mcp/<pool>` and agents at `/agent/<pool>`. A support team sees two notes
859
+ tools and tickets, a calendar bot sees one tool, both over the same credentials. Private remote catalog
612
860
  cache entries remain partitioned by the admitted pool.
613
861
 
614
862
  ```ts
@@ -621,12 +869,12 @@ createConnecta({
621
869
  agents: ["calendar-bot"],
622
870
  pools: {
623
871
  support: {
624
- tools: ["linear", "notion.search_pages", "notion.fetch_page"],
872
+ tools: ["tickets", "notes.search_pages", "notes.fetch_page"],
625
873
  members: supportTeam,
626
- description: "Support tools: Linear and three Notion reads. Ask a maintainer for anything else.",
874
+ description: "Support tools: tickets and two notes reads. Ask a maintainer for anything else.",
627
875
  },
628
876
  calendar_bot: { tools: ["calendar.create_event"], members: ["agent:calendar-bot"] },
629
- reporting: { tools: ["linear", "notion"], readOnly: true, members: analysts },
877
+ reporting: { tools: ["tickets", "notes"], readOnly: true, members: analysts },
630
878
  },
631
879
  connectors, executor,
632
880
  });
@@ -643,6 +891,7 @@ admitted.
643
891
  | Scope | `/mcp/<pool>` and `/agent/<pool>` serve exactly the pool's `tools` (connector ids or exact `connector.tool` addresses), with the pool's `readOnly`, to its member people and member agents respectively. Plain `/mcp` serves the whole catalog to maintainers. Neither intersects with or widens anything. |
644
892
  | Refusal | A non-maintainer at `/mcp`, a non-member, and an undeclared pool get an identical 404 status, body, and headers, after auth. The operator log records `not_maintainer`, `not_pool_member`, or `pool_not_declared`. Names are not anonymously enumerable; valid credentials cannot distinguish these cases by content. |
645
893
  | Agents | An agent member reaches its pools only at `/agent/<pool>`, with its `cta_` token. Human endpoints refuse `cta_`; see [agent endpoints](#agent-endpoints). |
894
+ | Surface | From 0.35.0, optional `surface`: `"meta"` (default) serves the six meta-tools; `"native"` lists each of the pool's downstream tools directly, for clients with their own tool search, with optional `executeCode: false` to replace `execute_code` with `read_result` (invalid on meta pools). Calls take the same paths either way. See the [native tool surface](./meta-tools.md#native-tool-surface). |
646
895
  | Description | Optional `description`: plain text telling the model what this endpoint is for. `/mcp/<pool>` and `/agent/<pool>` append it to the MCP `initialize` instructions after `This endpoint: `; plain `/mcp` and a pool without one serve the shared instructions unchanged. It grants, hides, and refuses nothing, and changes no tool or tool description. |
647
896
  | Construction | Malformed names, unknown options or connectors, empty pools, empty or missing `members`, unresolvable member references, a non-boolean `readOnly`, an `owner` that is not a Workspace email address, a `description` that is not 1 to 500 characters of one-line text after trimming, a retired `trust` or `grant`, and addresses absent from an `api()` static catalog throw. Remote catalogs are checked at lazy load. |
648
897
  | Discovery | Clerk's pool 401 names `/.well-known/oauth-protected-resource/mcp/<pool>` with the pool URL as `resource`, matching RFC 9728. Cloudflare Managed OAuth is application-level and needs no pool setup. |
@@ -667,11 +916,11 @@ at the edge so an agent sends one credential, and it behaves the same on Node.
667
916
  `/%61gent/x`, `//agent/x`, `/agent/%2f..`, `/agentx`) answers the pool 404
668
917
  before any admission or auth, so a path Access bypasses never reaches a
669
918
  human route. The query string is not part of the match.
670
- - **Auth.** Only the built-in `cta_` adapter is consulted. Clerk and Access
919
+ - **Auth.** Only built-in `cta_` and configured Vercel workload adapters are consulted. Clerk and Access
671
920
  adapters never run; `ctx.access`, which Access attaches even to a bypassed
672
921
  path, is never read or passed on; cookies, Clerk handshake parameters,
673
922
  `Cf-Access-Jwt-Assertion`, and Access service headers are ignored. A
674
- missing, malformed, unknown, revoked, or non-`cta_` credential gets 401 with
923
+ missing, malformed, unknown, revoked, or unaccepted credential gets 401 with
675
924
  `WWW-Authenticate: Bearer` and no `resource_metadata`.
676
925
  - **Membership.** An admitted agent must be listed in that pool's `members`.
677
926
  A non-member agent, a pool that lists no agents, and an undeclared but
@@ -995,7 +1244,7 @@ intake-redacted successes, keyed by connector configuration. Set
995
1244
  `authScope: "personal"` when every human needs a separate downstream account:
996
1245
 
997
1246
  ```ts
998
- remoteMcp("linear", { url: "https://mcp.linear.app/mcp", authScope: "personal",
1247
+ remoteMcp("crm", { url: "https://crm.example.com/mcp", authScope: "personal",
999
1248
  auth: { type: "oauth" } });
1000
1249
  ```
1001
1250
 
@@ -1141,8 +1390,10 @@ leaves the local grant removed. The management route returns HTTP 200 with
1141
1390
  code. Revoke the old grant in the provider's console if removal there matters.
1142
1391
  With no advertised endpoint, Disconnect makes no request. Repeating Disconnect
1143
1392
  never retries revocation, because the removed grant is no longer available.
1144
- Restart makes no revocation request. Static `api()` OAuth has no discovered
1145
- revocation endpoint and continues to remove authorization locally.
1393
+ Restart makes no revocation request. Static `api()` OAuth uses the same
1394
+ removal and revocation flow when deployment configuration supplies
1395
+ `revocationEndpoint`; without it, removal remains local. The configured
1396
+ client secret is used in memory and is never saved with the grant.
1146
1397
 
1147
1398
  ## Refresh failures
1148
1399
 
@@ -1157,11 +1408,11 @@ Copy these durable records during storage migration.
1157
1408
 
1158
1409
  Each fingerprint record names its dispatch epoch, holder, and resolution state:
1159
1410
 
1160
- | State | Send gate |
1161
- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1162
- | `outstanding` | Dispatched without a definitive outcome yet. Refuse the fingerprint in every epoch. |
1163
- | `ambiguous` | The sent request's outcome is unknowable. Refuse the fingerprint in every epoch, including when re-consent returns it again. |
1164
- | `resolved` | The tokens were committed or a definitive failure was recorded. Refuse another send in the same epoch. A later epoch may send once only when its code exchange completed after the resolution was recorded. |
1411
+ | State | Send gate |
1412
+ | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1413
+ | `outstanding` | Dispatched without a definitive outcome yet. Refuse the fingerprint in every epoch. |
1414
+ | `ambiguous` | The sent request's outcome is unknowable. Refuse the fingerprint in every epoch, including when re-consent returns it again. |
1415
+ | `resolved` | The tokens were committed or a definitive failure was recorded. A completed lease whose token digest matches the current grant may start the next generation in the same epoch. A later epoch needs a code exchange that observed this exact resolved record. |
1165
1416
 
1166
1417
  Resolution writes use CAS against the exact outstanding record and record a
1167
1418
  `resolvedAt` timestamp with a resolved outcome. When accepting a code response,
@@ -1174,6 +1425,14 @@ outstanding dispatch. An earlier consent cannot become eligible merely because
1174
1425
  the pending refresh later resolves. Competing epochs cannot both replace the
1175
1426
  same record, and a late answer cannot turn an ambiguous record into resolved.
1176
1427
 
1428
+ For vendors that return unchanged refresh-token bytes, a successful token
1429
+ commit and its matching completed lease authorize the next refresh generation
1430
+ in that epoch. It must replace the exact resolved fingerprint by CAS before
1431
+ sending. Outstanding, ambiguous and failed attempts never authorize reuse;
1432
+ concurrent callers with an older access token adopt the committed result.
1433
+ An unsent successor retains the prior successful proof during cleanup, so a
1434
+ transient storage failure before dispatch can be retried without losing the grant.
1435
+
1177
1436
  An ambiguous token stays blocked even if re-consent returns identical bytes.
1178
1437
  Recovery requires re-consent that yields a different refresh token, or
1179
1438
  revocation at the provider. Restart never deletes spent records or moves old
@@ -1211,6 +1470,38 @@ a dispatched record. The refresh HTTP request and response body have a
1211
1470
  backoff. A waiter deadline returns retryable `unavailable`, keeps the grant,
1212
1471
  and starts no consent. It never permits resending a spent token.
1213
1472
 
1473
+ A `remoteMcp()` write the downstream answered with HTTP 401 is never sent
1474
+ again (INV-9). The SDK still refreshes the grant, and connecta stops its
1475
+ resend before it leaves, so any fresh call after the target is reconciled uses
1476
+ the fresh token. The call
1477
+ fails with a non-retryable `connector_call_failed` whose outcome is unknown.
1478
+ If the refresh fails instead, whatever it met (a dead grant, a waiter
1479
+ deadline, the caller leaving), or hands back the token the API just
1480
+ rejected, the 401's verdict stays:
1481
+ `downstream_oauth_required`, never retryable, with `reconciliationRequired`
1482
+ and `writeOutcome: "unknown"`, and an `authorize_connector` handoff that says
1483
+ to re-read the target before any fresh call. A read is refreshed and sent
1484
+ once more. MCP sends a tool only once when either its effective classification
1485
+ or its original server hints say write. A deployment write-to-read override
1486
+ cannot make a server-declared write replayable. Its failure after possible
1487
+ dispatch is also an unknown outcome, non-retryable and without wait advice;
1488
+ a proven unsent failure retains its own recovery advice. A direct call outside an
1489
+ invocation uses the dispatched definition, a reviewed write verdict, or the
1490
+ tool's hints, and fails closed as a write.
1491
+
1492
+ A remote MCP tool call that loses its internal dispatch record is refused
1493
+ before sending unless active calls for that tool prove it is a read. A
1494
+ write or an unproven call fails non-retryable with `writeOutcome: "unknown"`:
1495
+ tracking loss cannot establish whether an earlier message already sent it.
1496
+
1497
+ Google Workspace also uses the invocation's served classification
1498
+ before considering the HTTP method. A write-classified GET is never replayed
1499
+ after a 401; a read-classified POST may replay once. Without a served verdict,
1500
+ this transport uses GET as read and other methods as write. Google drops the
1501
+ rejected token for the next deliberate call and retains `auth_required` with
1502
+ `writeOutcome: "unknown"` and the operator handoff. A renewed credential does not establish the write outcome. Recovery
1503
+ that hands back the same token renewed nothing.
1504
+
1214
1505
  One preparation deadline covers initial epoch and grant reads, claim acquisition,
1215
1506
  dispatch preparation, and local joiners waiting for a grant commit. Cancellation
1216
1507
  before sending settles the local flight without awaiting cleanup storage. An
@@ -1287,7 +1578,29 @@ contain `key`, `token`, `secret`, `auth`, `signature`, or `session`. Custom
1287
1578
  `api()` handlers use `ctx.fetch` for this tracking; `ctx.oauth.fetch` and
1288
1579
  maintained-provider transports track the final request too. Credential-slot
1289
1580
  reads cover values sent in headers, queries, or bodies. Echoed sensitive
1290
- header lines are also withheld.
1581
+ header lines are also withheld, read before any echoed value in them is
1582
+ replaced: a private value that changes part of a header's name or takes a
1583
+ credential prefix (such as `Authorization: Bearer`) still leaves the whole
1584
+ header value withheld, every cookie pair included. A line that is not a
1585
+ sensitive header keeps its text.
1586
+
1587
+ Redaction removes every registered form whole and every run of eight
1588
+ consecutive code units (K) of a registered form of a value at least eight units
1589
+ long, wherever it starts, in the raw text and in each JSON-decoded layer. A cut,
1590
+ trim, or page can therefore leave at most seven units of a form outside the
1591
+ cases below. Text Connecta or a maintained provider shortens before returning
1592
+ it, such as a long error message, a capped field, or one page of a long text, is
1593
+ redacted whole before any trimming, whitespace normalization, number parsing,
1594
+ extraction, or cut, so derived titles, previews, summaries, sizes, and raw
1595
+ branches share the same protection. An extraction whose source changed under
1596
+ redaction returns `[redacted]`. Text read only up to a bound also loses a tail
1597
+ that begins a sent value the read stopped inside, at every JSON decoding layer.
1598
+ Complete matches and unfinished tails are found against the original text and
1599
+ replaced together; even a one-unit prefix is withheld. This can conservatively
1600
+ suppress a legitimate tail that shares a secret's prefix. Text paging that
1601
+ counts redacted units binds its cursor to that representation and refuses
1602
+ continuation if a changed request secret set changes it, even when the vendor
1603
+ revision is unchanged.
1291
1604
 
1292
1605
  Submitted values under JSON Schema `writeOnly: true` join this memory-only
1293
1606
  request set, including nested objects, arrays, and local references. Strings
@@ -1304,69 +1617,200 @@ submitted data and cached catalogs never depend on one request's arguments.
1304
1617
 
1305
1618
  Payload rules apply where downstream or guest data enters: downstream results
1306
1619
  and errors, resource reads, connector usage guides and downstream skill files,
1307
- program returns, logs, and emits, and relayed input prompts. JSON objects,
1308
- arrays, and string literals are parsed and walked, whether they are wire text
1309
- or a string inside a result, so nested JSON strings are decoded too. Every
1310
- decoded string leaf and property name is scanned, and a number, boolean, or
1311
- null whose canonical text matches becomes the string `[redacted]`, so the JSON
1312
- stays valid. Text is reserialized
1313
- only when something changed; otherwise its original bytes remain. The
1314
- serialized bytes are then scanned raw, and a literal the walk cannot see, such
1315
- as one spanning tokens, withholds the whole text. Other text is scanned raw and
1316
- through one more layer of JSON string escapes at a time while the last layer
1317
- could still hold an escape. A string is decoded through at most eight layers in
1318
- total, counting the nested JSON strings it was parsed from; text that would
1319
- need more is withheld. Every match is mapped back to the original text,
1320
- overlapping and adjacent spans merge, and each merged span is replaced once, so
1321
- one match can never split another. A span wholly inside an existing
1620
+ program returns, logs, and emits, and relayed input prompts. Each redaction
1621
+ projects the whole value: string and number leaves in serialization order,
1622
+ JSON objects, arrays, and string literals parsed into their own leaves whether
1623
+ they are wire text or a string inside a result, and errors' own fields. The
1624
+ joined projection is matched once, so a value split across adjacent leaves,
1625
+ such as text blocks or array items, is found; each match is replaced only in
1626
+ the leaves it touches, and the containers around them keep their shape. A
1627
+ number that a match touches becomes the string `[redacted]`, so the JSON stays
1628
+ valid. Text is reserialized only when something changed; otherwise its original
1629
+ bytes remain. Serialized JSON whose keys, syntax, and leaves together hold a run
1630
+ that no single leaf holds, as a value spanning tokens does, is withheld whole.
1631
+ Matching runs on the raw text and through one more layer of JSON string escapes
1632
+ at a time while the last layer still decodes an escape, at most eight layers;
1633
+ text still holding escapes past that is withheld. Each decoded layer is scanned
1634
+ around the escapes it decoded, since every other match was found a layer up. Every match is mapped back to
1635
+ the original text, overlapping spans merge, and each merged span is replaced
1636
+ once, so one match can never split another. A span wholly inside an existing
1322
1637
  `[redacted]` placeholder is that placeholder; a private value that contains the
1323
- placeholder text is still redacted. Connecta and MCP envelope field names, tag
1324
- values such as a content `type` or result `format`, and typed facts such as
1325
- `isError` and `retryable` are copied unchanged. The agent boundary around
1326
- Connecta's own envelopes repeats credential and literal private-value
1327
- replacement without the short-value rules, so envelope identifiers, counts, and
1328
- JSON-RPC ids are never rewritten.
1638
+ placeholder text is still redacted.
1639
+
1640
+ A maintained provider's parsed vendor JSON has every value redacted on arrival
1641
+ and keeps its object keys until the connector returns, so the provider's value
1642
+ safety finds each field by the name the vendor gave it. The connector boundary
1643
+ then applies payload rules to the whole result or failure, keys included.
1644
+ Response header names are kept; their values are redacted. When a redacted
1645
+ value is one value safety decides on, such as an object's type, a record's
1646
+ label, a link's parameter name or the response's Content-Type, value safety
1647
+ takes its most protective choice, so more of that response may be withheld.
1648
+
1649
+ Every string that can reach an agent, an executor, a page or storage goes
1650
+ through one checker that applies all of the checks in this section: text and
1651
+ number leaves, object keys, declared binary fields, the bytes of typed binary,
1652
+ cursor claims, error fields, provider text lanes, bounded reads, and structural
1653
+ fields that are refused instead of rewritten. Object keys are checked on their
1654
+ own, and a key holding a match is renamed. Image, audio, resource-blob, or
1655
+ `format: "base64"` content, or a REST body's `base64` beside its `contentType`,
1656
+ is checked as text and also decoded whole and checked as bytes and as UTF-8,
1657
+ then withheld whole as the base64 of `[redacted]` on a match; content that is
1658
+ not valid base64 as it stands is still checked through every text view. Base64
1659
+ or base64url in any string is decoded and checked in each of the string's text
1660
+ views: the text itself, the text with one layer of JSON escapes decoded
1661
+ (`\uXXXX` and the standard escapes), the text with its percent escapes
1662
+ decoded, and its form decoding, which reads a `+` as a space and decodes any
1663
+ percent escapes, whether or not the text holds one. So `x+bm90ZXM=` is read
1664
+ both as one run and as `x` followed by `bm90ZXM=`, the base64 of `notes`.
1665
+ A view that is wholly base64 is decoded from six characters. A run inside a
1666
+ view is decoded from six characters when a private value of four to seven
1667
+ characters is registered, and from eleven otherwise: each length is the
1668
+ shortest run that can carry the smallest value the pass matches, four bytes or
1669
+ eight, at any alignment, padded or not. Decoded bytes are checked as bytes, as
1670
+ UTF-8, and through base64 they carry, up to three encodings deep, and the field
1671
+ is withheld whole as `[redacted]` when only its decoding holds a match. Every
1672
+ decode is paid for from the call's work budget; a field the budget cannot
1673
+ check is withheld.
1674
+
1675
+ Only fixed words are copied unchanged whatever was sent: Connecta, MCP, and
1676
+ JSON-RPC envelope member names, tag values such as a content `type` or result
1677
+ `format`, booleans under outcome names (`isError`, `ok`, `retryable`,
1678
+ `truncated`, `hasMore`), and, on an error, its classification code and a fixed
1679
+ name: one a built-in error, `DOMException` (`DOMException`, `AbortError`,
1680
+ `TimeoutError`), Connecta's own classes, or the pinned MCP SDK's classes give
1681
+ their instances. Any other error `name`, own or inherited, a data field or an
1682
+ accessor's answer, including one a custom class gives itself, is a leaf of the
1683
+ projection like any other field: it is matched joined with the fields beside it
1684
+ and through any JSON it holds, and replaced whole by `[redacted]` when anything
1685
+ changes any part of it. The copy keeps its class, so classification and
1686
+ identity facts still see it. An error's fields are joined in its copy's order:
1687
+ its own fields, then what its class answers for `stack`, `message`, `cause`,
1688
+ `code` and `name`, then the other accessors its class defines. A number or a string under any name,
1689
+ such as `retryAfterMs` or a plain object's `code`, is matched like any other
1690
+ value; a matching typed number on an error is dropped. Relay states and connect
1691
+ links Connecta minted are kept whole. The agent boundary around Connecta's own
1692
+ envelopes repeats every check except short private values, so envelope
1693
+ identifiers, counts, and JSON-RPC ids are never rewritten.
1694
+
1695
+ Every base64 or base64url encoding Connecta itself returns, such as a Drive or
1696
+ Gmail download, a REST body's `base64`, or a page cursor, is produced by one
1697
+ helper that checks the content first and encodes `[redacted]` instead when it
1698
+ holds a match. A cursor holds Connecta's own names, counts and fingerprints
1699
+ beside vendor values redacted when they were placed: a vendor page token or
1700
+ page id is redacted, so one that echoes a sent value fails on the next page
1701
+ instead of carrying it; a vendor revision or id the cursor only compares is
1702
+ replaced by its digest when it holds a sent value. Before a cursor is encoded,
1703
+ every string in it is checked under payload rules, and its whole text and
1704
+ encoding under the agent boundary's, which leave Connecta's own keys and counts
1705
+ to short private values. A cursor that holds a match encodes `[redacted]`,
1706
+ which the next page refuses.
1707
+
1708
+ What redaction returns is what it checked, and only JSON data: strings,
1709
+ numbers, booleans, `null`, plain objects and arrays, a fresh built-in
1710
+ `Uint8Array`, and copies of errors. A caller's object or array is
1711
+ returned as it stands only as verified plain data: its prototype is
1712
+ `Object.prototype`, `Array.prototype`, or `null`; no `toJSON` is on its chain;
1713
+ it has no symbol-keyed field; every field is a data property the pass read, an
1714
+ array's holes read through its prototypes; and nothing in it changed. Anything
1715
+ else is returned as a copy of writable, configurable data properties, so a
1716
+ frozen or read-only input never makes redaction throw, and the caller's value is
1717
+ never mutated:
1718
+
1719
+ - An error is always copied, as its own class. Its stack, and every accessor its
1720
+ class defines, such as DOMException's `name` and `message` or an SDK error's
1721
+ `status`, is read once into a checked field, under its name checked as any key
1722
+ is. Its `toJSON`, a data field or an
1723
+ accessor, is never read and is shadowed by an `undefined` field on the copy,
1724
+ and a field holding a function is withheld, so the copy carries no
1725
+ serializer. Methods its class defines stay on its prototype; JSON calls none
1726
+ of them.
1727
+ - An object's own accessor is never invoked and is left out of the copy; an
1728
+ array index read through an accessor holds `[redacted]`.
1729
+
1730
+ Every other value becomes the data JSON shows for it, checked, or is withheld:
1731
+
1732
+ | Value | Returned as |
1733
+ | ------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- |
1734
+ | Function | `[redacted]`; a `toJSON` it holds is never called |
1735
+ | Symbol | `[redacted]` |
1736
+ | `bigint` | itself, checked as its digits like a number; JSON exits refuse it, as before |
1737
+ | `undefined` | itself; JSON omits it |
1738
+ | Object or array with a callable `toJSON`, such as a `Date` | the result of calling it once, checked: a `Date` becomes its ISO string, or `null` |
1739
+ | `String`, `Number`, or `Boolean` wrapper | its primitive, checked |
1740
+ | `Map`, `Set`, `WeakMap`, `WeakSet`, `Promise`, `RegExp`, or an instance of any other class | a plain object of its own data fields, checked: `{}` for the built-ins, as JSON shows them; accessors are never read |
1741
+ | Array of another class | a plain array of its indices, checked |
1742
+ | Typed array, `DataView`, or `ArrayBuffer`, including a subclass | a fresh built-in `Uint8Array` of the bytes it held, or `[redacted]` |
1743
+
1744
+ Typed binary is withheld as `[redacted]` when its bytes hold a match, checked as
1745
+ decoded binary content is, or when one of the numbers JSON shows for it does:
1746
+ its elements and, for the `Uint8Array` returned, its bytes, each checked as a
1747
+ JSON number and joined with its neighbours. A one-to-three-character numeric
1748
+ private value, such as `7`, therefore withholds a typed array holding that
1749
+ element, as it withholds an equal JSON number. Each of those numbers is an entry
1750
+ of the walk's 1,048,576-entry budget, so binary past it is withheld whole.
1751
+ Connecta returns downstream binary as base64 text, which none of this limits;
1752
+ only in-process code can return a typed array. With nothing registered for a
1753
+ request, redaction has nothing to match and returns its input as it stands.
1754
+
1755
+ A change copies only the containers on its path. An array's fields other than
1756
+ its indices are not read: JSON, which the MCP wire, the result stash, and every
1757
+ executor marshal values with, never serializes them.
1329
1758
 
1330
1759
  Empty private strings are exempt because they contain nothing to leak. A
1331
- non-empty private value shorter than eight characters redacts a structured
1332
- string leaf, property name, or scalar only when it equals the value or a
1333
- supported encoded form, including after JSON unescaping. A longer structured
1334
- field that merely contains it, such as `prod-db` for `prod`, is kept: this
1335
- documented tradeoff keeps identifiers and approval state usable. Prose that
1336
- contains a short value or encoded form anywhere is withheld as `[redacted]`:
1337
- plain-text results, `message`, `error`, `text`, `logs`, and `stack` strings,
1338
- string program results, and relayed prompts. Typed error classification,
1339
- retryability, retry delay, and write-outcome accounting remain. Private values
1340
- of at least eight characters are replaced wherever they occur.
1341
-
1342
- Matching runs one Aho-Corasick automaton over every registered form, so each
1343
- scanned code unit costs amortized constant work however many values are
1344
- registered. The automaton indexes the first 256 code units of each form,
1345
- credentials first, and at most 512 Ki indexed units for every registration
1346
- together, so a long credential or private value costs no more automaton memory
1347
- than its prefix. Where a longer form's prefix matches, the scanned view's
1348
- rolling hash confirms the rest once its last unit arrives; equal text always
1349
- confirms, so a hash collision could only redact more. Forms that share a prefix
1350
- and a length share each confirmation, and overlapping prefix occurrences in
1351
- self-similar text share one run of confirmations, so periodic text stays exact.
1352
- Each confirmation is charged to the call's work budget before it is queued, and
1353
- one scanned view holds at most 16 Ki runs; a view that needs more is withheld.
1354
- Submitted values register at most 512 KiB of encoded forms and 4,096
1355
- argument nodes. A form that does not fit either limit withholds every string
1356
- at least as long as that form; an argument tree that cannot be walked withholds
1357
- all downstream text.
1760
+ private value of one to three characters is matched only as a whole field
1761
+ value: a string, property name, number, boolean, or decoded binary view equal
1762
+ to it or a supported encoded form is withheld, and text around it stays, so a
1763
+ private `1` leaves `row 10` alone. A value of four to seven characters is also
1764
+ replaced wherever it occurs in payload text, numbers, property names, and
1765
+ decoded views, so `prod-db` becomes `[redacted]-db` for `prod`. Pieces of either
1766
+ are not matched. Private values of at least eight characters and credentials
1767
+ are matched whole and by every eight-unit run. At a cut, an unfinished prefix of
1768
+ any registered form is still withheld, however short.
1769
+
1770
+ Identifiers sent beside a credential are matched whole only, because consent
1771
+ URLs and vendor metadata carry their parts by design: a confidential OAuth
1772
+ client's ID, a Basic username beside a password of at least eight characters,
1773
+ and credential-slot fields named as identifiers (ending in `id`, `user`,
1774
+ `username`, `login`, or `email`, or starting with `public`). A Basic username
1775
+ beside a shorter or empty password is the credential itself and is fully
1776
+ matched. Query parameters named as continuation cursors (`pageToken`,
1777
+ `nextPageToken`, `next_token`, `continuationToken`, `syncToken`) are public
1778
+ result state and are not registered. Header values a connector adds to token
1779
+ requests are registered under any name, except values equal to Connecta's own
1780
+ public ones (`Accept: application/json`, and the media types maintained
1781
+ providers send): registering a media type would redact a vendor's name wherever
1782
+ its fragments recur.
1783
+
1784
+ The eight-unit runs are indexed as two independent 32-bit hashes per run, at
1785
+ most 512 Ki distinct runs, so a false match is vanishingly rare and only redacts
1786
+ more; repeated runs, as in a self-similar credential, are stored once. Indexing
1787
+ and scanning are linear in the registered forms and the scanned text, whatever
1788
+ the number of values. The `Bearer ` and `token ` framings are public, so no run
1789
+ starting inside them is indexed; an echo carrying a framing is replaced with it.
1790
+ Short private values and identifiers are matched whole by one Aho-Corasick
1791
+ automaton each, which also finds the unfinished tails of bounded reads. High-
1792
+ entropy credentials and private values do not match ordinary text. A value that
1793
+ embeds ordinary text, such as a password holding a word, a token holding a
1794
+ vendor's name, or a credential that is a URL, also redacts that text where it
1795
+ recurs within the same request. Submitted values register at most 512 KiB of
1796
+ encoded forms and 4,096 argument nodes. A value that does not fit either limit,
1797
+ or a set past the index's capacity, withholds every string of eight units or
1798
+ more and every run of adjacent shorter leaves reaching eight units; an argument
1799
+ tree that cannot be walked withholds all downstream text.
1358
1800
 
1359
1801
  One redaction call has one work budget across parsing and every scanned view:
1360
1802
  64 Mi units with private values, and 1 Gi for credentials alone, which keeps
1361
- multi-MiB document and skill-file reads. Work that does not fit the remaining
1362
- budget withholds its field, so smaller identifiers can still be checked. The
1363
- same call walks structured values iteratively, at most 512 containers deep and
1364
- 1 Mi entries in total, and copies only containers on a path that changed. A
1365
- container's entries are counted before it is walked. One that does not fit the
1366
- remaining entries is scanned as serialized text instead: kept unchanged when
1367
- nothing registered appears, otherwise withheld. A cycle or a container past the
1368
- depth limit is withheld. No limit throws or exposes a matcher diagnostic, and
1369
- typed success outcomes remain.
1803
+ multi-MiB document and skill-file reads. When the joined projection does not
1804
+ fit, leaves are matched alone, in order, and one that does not fit is withheld
1805
+ without spending what a smaller one needs; runs crossing two kept leaves are
1806
+ then matched 512 units deep on each side of their seam. The same call visits at
1807
+ most 512 containers deep and 1 Mi entries in total. A container's entries are
1808
+ counted before it is walked.
1809
+ One that does not fit the remaining entries is withheld whole: it is never
1810
+ serialized and checked as text, so nothing past the budget reaches a result, a
1811
+ page, or the result stash. A cycle or a container past the depth limit is
1812
+ withheld. No limit throws or
1813
+ exposes a matcher diagnostic, and typed success outcomes remain.
1370
1814
 
1371
1815
  Only credentials of at least eight characters enter the redaction and structural
1372
1816
  matchers. Credentials shorter than 8 characters are not redacted from echoes;
@@ -1376,9 +1820,9 @@ this floor produces one operator warning per connector with the typed code
1376
1820
  `short_secret_not_redacted` and no credential value. Remote connectors warn at
1377
1821
  construction; static `api()` OAuth warns on its first provider operation.
1378
1822
  Connecta's own messages never quote credential values, regardless of length.
1379
- One automaton is cached until the secret set changes, and an empty set skips matching. Redaction runs after
1380
- JSON unwrapping or joining text blocks and on final serialized text and every
1381
- structured string, before result paging, emits, and program outputs/errors/logs. See
1823
+ The index and automata are cached until the secret set changes, and an empty set skips matching. Redaction
1824
+ runs before JSON unwrapping or joining text blocks, on final serialized text and every
1825
+ structured value, and before result paging, emits, and program outputs/errors/logs. See
1382
1826
  [the agent boundary](./architecture.md#errors-and-records).
1383
1827
 
1384
1828
  ## URLs a downstream advertises
@@ -1498,8 +1942,8 @@ createConnecta({
1498
1942
  auth: cloudflareAccessAuth(),
1499
1943
  // Administers shared credentials, agent tokens, and activity.
1500
1944
  maintainers: ["owner-id"],
1501
- // Each member connects their own account on personal_linear.
1502
- pools: { team: { tools: ["shared_docs", "personal_linear", "notion.search_pages"], members: team } },
1945
+ // Each member connects their own account on personal_crm.
1946
+ pools: { team: { tools: ["shared_docs", "personal_crm", "notes.search_pages"], members: team } },
1503
1947
  connectors, executor,
1504
1948
  readOnly: false, // required: true refuses every write call
1505
1949
  });
@@ -1539,7 +1983,8 @@ Connecta configuration still decides connector access and management: `maintaine
1539
1983
  Protect the Worker with a Worker-level Access application whose destination is
1540
1984
  `{ "type": "worker", "worker_id": "<the Worker script tag>" }`. A traditional
1541
1985
  hostname-level application blocks the URL but does not attach `ctx.access` to
1542
- the Worker. Enable [**Managed OAuth**](https://developers.cloudflare.com/cloudflare-one/access-controls/applications/http-apps/managed-oauth/)
1986
+ the Worker. The Worker example's `alchemy.run.ts` declares this application and
1987
+ attaches it through the Worker's `access` property. Enable [**Managed OAuth**](https://developers.cloudflare.com/cloudflare-one/access-controls/applications/http-apps/managed-oauth/)
1543
1988
  on that Worker-level application for interactive MCP clients. Cloudflare then
1544
1989
  owns the unauthenticated challenge and `/.well-known/` metadata, issues opaque
1545
1990
  RFC 8707 tokens, and resolves them into the same trusted Worker identity.
@@ -1627,7 +2072,10 @@ claims. SDK 3.12.0 drops opaque `aud`, so connecta makes one additional Backend
1627
2072
  API verification request and reads the raw response. Its subject and client
1628
2073
  must agree with the SDK, and it must report an unrevoked, unexpired token.
1629
2074
  The extra call follows request cancellation, times out after ten seconds,
1630
- caches no token or audience, and denies admission on failure.
2075
+ reads at most 64 KiB, follows no redirect, caches no token or audience, and
2076
+ denies admission on failure, including a redirect. It ends its request and
2077
+ response body on every outcome, including an error status and a body that
2078
+ stops arriving.
1631
2079
 
1632
2080
  ### Explicit fallback for unbound tokens
1633
2081
 
@@ -1644,6 +2092,140 @@ for this fallback. Hosts may cache registrations, but the list is impractical
1644
2092
  for standard onboarding. A CIMD client identifies the host, not the deployment
1645
2093
  resource. Prefer resource audiences with `allowedOAuthClientIds` omitted or `[]`.
1646
2094
 
2095
+ ### One Clerk instance, one Organization per deployment
2096
+
2097
+ `organizationId` lets one Clerk instance serve several deployments. Give each
2098
+ deployment its own Clerk Organization and pass that Organization's id. In the
2099
+ Node template, set `CLERK_ORGANIZATION_ID` instead of editing source.
2100
+
2101
+ ```ts
2102
+ clerkAuth({ publishableKey, secretKey,
2103
+ publicUrl: "https://crm.example.com",
2104
+ organizationId: "org_..." });
2105
+ ```
2106
+
2107
+ Admission in organization mode:
2108
+
2109
+ - Membership is the only organization rule. Every admitted Clerk user is a
2110
+ member of that Organization. Connecta asks Clerk's Backend API,
2111
+ `GET /v1/organizations/{organization_id}/memberships?user_id={user_id}`,
2112
+ for MCP OAuth tokens (JWT and opaque) and for operator sessions. It caches
2113
+ each answer per Organization and user, counted from when the lookup
2114
+ started: a member for 60 seconds, a non-member for 30. Each request that
2115
+ misses the cache runs its own lookup, subject to the bounds below. An answer
2116
+ is cached only if no lookup that started later has already cached one, so an
2117
+ older answer never replaces a newer one. If eviction suppresses a newer
2118
+ answer, any older answer for that user is invalidated. Assuming the clock
2119
+ does not step back unseen within the TTL window described below, every
2120
+ request reaching the check 60 seconds or more after a member's removal in
2121
+ Clerk is refused, even while their day-long access token remains valid.
2122
+ Under the same clock assumption, a non-member answer expires within 30
2123
+ seconds of an addition; admission then requires a successful lookup.
2124
+ - The cache reads the deployment's clock. An answer is used only while the
2125
+ clock reads from its lookup's start to that start plus 60 or 30 seconds.
2126
+ A step forward only ends it early. A step back to before the start ends it
2127
+ too, and once an answer has been found expired no later step revives it. A
2128
+ step back that lands within that window before any request has seen the
2129
+ answer expire cannot be detected, and keeps the answer until the clock
2130
+ again reads its start plus the TTL. That unseen rollback can extend cached
2131
+ admission beyond 60 seconds of elapsed time after removal.
2132
+ - Organization claims are never read. A token's `org_id` and a session's
2133
+ active Organization neither admit nor refuse. A member's token is bound to
2134
+ this deployment's URL whichever Organization they chose in Clerk, and a
2135
+ non-member gets `403` from the lookup.
2136
+ - A lookup that fails, answers with a redirect, an error status other than
2137
+ `429` (handled below), or an unreadable or oversized body, or times out ten seconds after it started
2138
+ refuses its request with `403`. Redirects are never followed. A caller that
2139
+ leaves cancels its own lookup and is refused; other requests' lookups go
2140
+ on. Connecta ends the Backend API request and its body on every outcome and
2141
+ caches neither a failure nor a cancelled lookup, so the next request asks
2142
+ Clerk again, subject to the bounds and 429 backoff below.
2143
+ - Connecta checks the token's audience, issuer, and subject before the lookup.
2144
+ The issuer must be the Frontend API origin encoded in `publishableKey`, and
2145
+ the subject must be the user the lookup names. An opaque token's
2146
+ verification response comes from the instance's Backend API and is matched by
2147
+ subject instead. A mismatch gets `401`.
2148
+ - Organization roles and permissions grant nothing. `maintainers` and pool
2149
+ `members` still list Clerk user ids in code. A member of the Organization in
2150
+ neither list gets `404` at `/mcp` and at every `/mcp/<pool>`.
2151
+ - `allowedDomains` and `gate` still apply after membership. Every configured
2152
+ check must pass, and an outsider never reaches `gate`.
2153
+ - Resource-bound tokens are required. A nonempty `allowedOAuthClientIds`
2154
+ throws at construction; omit it or pass `[]`. Every deployment on a shared
2155
+ instance gets tokens from one issuer, so the resource audience is what keeps
2156
+ one deployment's token out of another.
2157
+ - Advertised scopes are unchanged. Organization mode needs no Clerk
2158
+ organization scope, operator page, or sign-in page setting.
2159
+
2160
+ Protect the shared Backend API quota:
2161
+
2162
+ - Put ingress rate limiting in front of every deployment. Budget the combined
2163
+ traffic of all deployments and other applications using the instance,
2164
+ including authenticated non-members and opaque-token authentication. For
2165
+ example, rate limit both `crm.example.com` and `tickets.example.com` within
2166
+ one shared quota budget when they use the same Clerk instance; independent
2167
+ limits that each consume the whole quota do not protect the other deployment.
2168
+ [Clerk's system limits](https://clerk.com/docs/guides/how-clerk-works/system-limits)
2169
+ apply per instance identified by the secret key: 1,000 Backend API requests
2170
+ per ten seconds in production, 100 in development. Membership reads use that
2171
+ general limit. A deployment's MCP concurrency setting does not bound all
2172
+ authentication paths, including `subscriptions/listen` and operator routes.
2173
+ The independent [authentication capacity bound](#authentication-capacity-and-ingress-limiting)
2174
+ covers those paths and the SDK's authentication calls before this adapter's
2175
+ organization-specific bounds; it still does not impose a rate budget.
2176
+ - Organization mode permits at most 16 concurrent membership/extra opaque
2177
+ verification calls per `clerkAuth` instance (per isolate on Workers), with at
2178
+ most two per user. Each call owns its signal, deadline, and response body;
2179
+ there is no shared promise or waiting queue. Excess calls immediately get
2180
+ HTTP `503`, fixed code `organization_lookup_busy`, `retryable: true`, and
2181
+ `Retry-After: 1`. These fixed caps limit bursts relative to the documented
2182
+ quotas; they do not limit sustained request rates or coordinate isolates,
2183
+ deployments, or the SDK's own authentication calls. Do not treat them as a
2184
+ substitute for ingress rate limiting.
2185
+ - A `429` from a membership read or extra opaque verification starts backoff
2186
+ shared by those calls in that isolate. Connecta honors `Retry-After` seconds
2187
+ (or an HTTP date), clamped to 1–60 seconds; missing or malformed values use
2188
+ ten seconds. Concurrent 429s can extend, never shorten, the current backoff.
2189
+ The triggering request and new misses during backoff receive the same
2190
+ retryable `503` and a `Retry-After` for the remaining whole seconds, rounded
2191
+ up. Backoff is timed on the wall clock, so a clock step back while it runs
2192
+ lengthens the window by the size of the step. No membership verdict is
2193
+ cached for these refusals. Valid cached
2194
+ membership answers remain usable; opaque tokens still need verification.
2195
+ After backoff expires the next miss asks Clerk again, with no automatic
2196
+ retry of the original request. Monitor the payload-free
2197
+ `organization_lookup_busy` log reason and adjust the ingress budget when it
2198
+ recurs. A quota exhausted by another deployment can still refuse uncached
2199
+ admission here.
2200
+
2201
+ Set up a shared instance:
2202
+
2203
+ 1. Enable Organizations on the Clerk instance. Create one Organization per
2204
+ deployment. Add each person who signs in to that deployment as a member;
2205
+ any role works.
2206
+ 2. Enable resource audiences as described in
2207
+ [Enable resource audiences in Clerk](#enable-resource-audiences-in-clerk).
2208
+ Each deployment has its own public URL, so its tokens name only it.
2209
+ 3. Set `organizationId` to the deployment's Organization id. List
2210
+ `maintainers` and pool `members` by Clerk user id as usual.
2211
+ 4. Restart or redeploy. Verify that a member is admitted at `/mcp` and on the
2212
+ operator pages, a signed-in non-member gets `403`, and a token issued for
2213
+ another deployment's URL gets `401`.
2214
+
2215
+ Isolation tradeoff: the secret key is instance-wide. It reaches every
2216
+ Organization, user, and OAuth application on the instance, so whoever reads it
2217
+ from one deployment's environment can read and change every other
2218
+ deployment's membership. Organization mode separates admission, not
2219
+ administration. When a deployment needs its own keys, users, or instance
2220
+ settings, give it its own Clerk instance and omit `organizationId`; that shape
2221
+ remains fully supported.
2222
+
2223
+ Organization denials log the fixed codes `organization_membership_denied`,
2224
+ `organization_lookup_failed`, and `token_identity_mismatch`. They carry no user
2225
+ ids, Organization ids, or Backend API response text.
2226
+
2227
+ ### Session and diagnostic boundaries
2228
+
1647
2229
  Clerk session tokens authenticate operator routes and browser OAuth callbacks
1648
2230
  only, with the deployment-origin `azp` check. MCP rejections retain the `401`
1649
2231
  Bearer challenge and endpoint `resource_metadata` URL required by
@@ -1679,7 +2261,9 @@ that is not `pk_test_`/`pk_live_` and a base64-encoded domain throws when
1679
2261
  `clerkAuth` is called, as `allowedDomains` does, naming the option without
1680
2262
  quoting the value — usually the _secret_ key pasted into the publishable slot.
1681
2263
  A per-request deployment, as on Workers, sees that error on its first request
1682
- instead of a base64 stack on every route.
2264
+ instead of a base64 stack on every route. `organizationId` must be `org_`
2265
+ followed by 27 letters or digits, and it cannot be combined with a nonempty
2266
+ `allowedOAuthClientIds`; either mistake throws at construction.
1683
2267
 
1684
2268
  ## Human authentication management
1685
2269
 
@@ -1739,6 +2323,25 @@ and Origin checks. Vaults without sealing keep ordinary handoffs and cannot
1739
2323
  serve URL elicitation. See [authorization recovery](./meta-tools.md#authorization-recovery)
1740
2324
  for capability gating, retry termination, and dispatch restrictions.
1741
2325
 
2326
+ Downstream OAuth credential POSTs (code exchange, refresh, dynamic client
2327
+ registration and revocation) use a transport that sends each request at most
2328
+ once and never follows redirects. On Node, serve with `listen` from
2329
+ `@zackbart/connecta/node` and retain the `undici` peer; custom root-only Node
2330
+ hosts refuse these POSTs before dispatch. Node's explicit `listen({ proxy })`
2331
+ option applies to them. Discovery GETs keep native fetch and its host proxy
2332
+ policy. Workers use their native fetch transport. See [Node transport setup](./deploying.md#start-on-node).
2333
+
2334
+ A refresh without a confirmed usable answer may have rotated the refresh token.
2335
+ Connecta blocks its fingerprint and requires a fresh authorization flow, without
2336
+ replaying that token or claiming the server did not receive it. Authorization
2337
+ can still be restarted; an unresolved fingerprint cannot become redeemable again
2338
+ by writing the same token into an old grant. Registration failure can leave a
2339
+ client at the provider; check the provider's client administration before
2340
+ starting another registration if its reply was lost. Disconnect removes the
2341
+ local grant before attempting revocation, and an unconfirmed revocation asks the
2342
+ operator to revoke it at the provider. No token forms or provider error prose
2343
+ are included in diagnostics.
2344
+
1742
2345
  A verified `/connect` visit gives downstream OAuth work and its state handoff 30 seconds. The request signal
1743
2346
  and deadline reach discovery, registration, and other downstream fetches; expiry returns `504 OAuth
1744
2347
  authorization start timed out`. A restart's grant write, catalog invalidation, and scope close drain before
@@ -1759,9 +2362,15 @@ its URL.
1759
2362
 
1760
2363
  Credential and OAuth mutation require interactive identity, connector visibility, management permission, and
1761
2364
  an exact same-origin `Origin` for browser mutations. A static credential `authorize_connector` call needs
1762
- visibility only: with `ui: operatorUi()` and a vault it returns a secret-free `operator_config` handoff;
1763
- without either it returns `unavailable`. The OAuth branch also checks management permission and returns
1764
- `unavailable` when refused.
2365
+ visibility only. With `ui: operatorUi()` and a vault, a missing credential returns a secret-free
2366
+ `operator_config` handoff; without either it returns `unavailable`. When all declared credential
2367
+ fields are configured, the explicit result returns `ready` with conditional guidance and retains
2368
+ the operator handoff fields (or the deployment configuration message when recovery is unavailable).
2369
+ If a call just failed with an authentication or operator-configuration error, give that handoff
2370
+ to the operator; otherwise proceed with the call. Presence does not validate a key, and this
2371
+ stateless result cannot remember failures from earlier requests. Authentication errors retain
2372
+ `oauth`, `operator_config` or `unavailable` recovery, never `ready`. The OAuth branch also checks
2373
+ management permission and returns `unavailable` when refused.
1765
2374
 
1766
2375
  Core owns `/connect/<connector>` and the callback without the optional UI. Both require Clerk sessions or
1767
2376
  Cloudflare Access users; MCP OAuth tokens, `cta_` tokens, and Access service identities
@@ -1801,6 +2410,13 @@ need `consumeAuthError` to accept error callbacks, sharing `finishAuth`'s atomic
1801
2410
  consent claim. Reissue
1802
2411
  pending consent links after upgrading; callbacks without a saved initiating user cannot complete.
1803
2412
 
2413
+ Custom connectors may implement `verifyCallbackParameters(params, ctx)` for
2414
+ provider-specific callback assertions. The route calls it after successful
2415
+ state verification and before issuer validation, handoff consumption, error
2416
+ handling, or code exchange. Returning false or throwing produces the generic
2417
+ invalid-callback refusal. Apply the same assertions inside `finishAuth` when
2418
+ supporting direct programmatic completion.
2419
+
1804
2420
  `compareAndSet` atomically claims link nonces, callback handoffs, and consents, with one concurrent winner,
1805
2421
  so each completes once.
1806
2422
  [Meta-tools](./meta-tools.md#authorization-recovery) describes recovery.
@@ -1844,9 +2460,9 @@ path, without credentials or a fragment. Its `client_id` must equal its URL,
1844
2460
  and its metadata must include the deployment's exact callback and public-client
1845
2461
  authentication. Changing from an external document to the self-hosted URL needs
1846
2462
  one Disconnect and reconnect because tokens belong to the old client ID.
1847
- `basecamp()` now defaults to the self-hosted document and accepts an external
1848
- URL override. Basecamp restricts DCR for HTTPS callbacks, so it needs a public
1849
- HTTPS deployment or an external document.
2463
+ Basecamp restricts DCR for HTTPS callbacks, so its generic `remoteMcp()` entry
2464
+ needs a public HTTPS deployment or an external document. Preserve its client
2465
+ identity when following the [hosted MCP migration guide](./hosted-mcp-migration.md).
1850
2466
 
1851
2467
  For a pre-registered client, use:
1852
2468
 
@@ -1907,15 +2523,28 @@ api("church", {
1907
2523
  No discovery or registration occurs; all URLs are configuration, so the
1908
2524
  [advertised-URL rule](#urls-a-downstream-advertises) does not apply.
1909
2525
 
1910
- | Option or binding | Contract |
1911
- | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1912
- | Endpoints | Checked at construction: HTTPS, HTTP only on loopback, no credentials or fragment. |
1913
- | `pkce` | S256 by default; `false` omits challenge and verifier. |
1914
- | `authorizationParams` | Adds provider parameters; cannot restate the grant's own. |
1915
- | `tokenRequestHeaders` | Adds code-exchange and every refresh's headers; cannot set `Authorization`, `Content-Type`, `Content-Length`, `Cookie`, or `Host`. |
1916
- | `tokenEndpointAuthMethod` | Defaults to `client_secret_basic` with a secret, `none` without; mismatched pairings refuse construction. |
1917
- | Issuer/resource | No advertised issuer means no required RFC 9207 `iss` or sent RFC 8707 `resource`. The grant binds to the token endpoint; after it changes, old tokens are never sent and the next consent replaces them. |
1918
- | Client | One deployment-config identity, never written or sealed to storage; no owner vault slot. Store leaks contain no client secret; Disconnect deletes none and Restart carries none. Read from environment or Worker secrets; empty strings refuse construction without quoting values. |
2526
+ | Option or binding | Contract |
2527
+ | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
2528
+ | Endpoints | Checked at construction: HTTPS, HTTP only on loopback, no credentials or fragment. |
2529
+ | `pkce` | S256 by default; `false` omits challenge and verifier. |
2530
+ | `authorizationParams` | Adds provider parameters; cannot restate the grant's own. |
2531
+ | `expectedCallbackParameters` | Optional `Readonly<Record<string, string>>` of fixed provider response values. Each configured parameter must occur exactly once with its exact decoded value, after pending-state verification and before issuer validation, flow claims, code exchange, or grant persistence. Missing, duplicate, or mismatched values refuse without exchanging a code or returning parameter values. Omission preserves existing behavior. |
2532
+ | `revocationEndpoint` | Optional trusted HTTPS revocation URL (loopback HTTP allowed), without credentials, query or fragment. Disconnect removes the local grant before one bounded attempt using the configured client. Failure reports `oauth_revocation_failed`; a repeated Disconnect never retries. |
2533
+ | `revocationRequestFormat` | `"form"` by default; `"json"` sends JSON without `token_type_hint`. Requires `revocationEndpoint` when set. |
2534
+ | `tokenRequestHeaders` | Adds code-exchange and every refresh's headers; cannot set `Authorization`, `Content-Type`, `Content-Length`, `Cookie`, or `Host`. |
2535
+ | `tokenEndpointAuthMethod` | Defaults to `client_secret_basic` with a secret, `none` without; mismatched pairings refuse construction. |
2536
+ | Issuer/resource | No advertised issuer means no required RFC 9207 `iss` or sent RFC 8707 `resource`. The grant binds to the token endpoint; after it changes, old tokens are never sent and the next consent replaces them. |
2537
+ | Client | One deployment-config identity, never written or sealed to storage; no owner vault slot. Store leaks contain no client secret; Disconnect deletes none and Restart carries none. Read from environment or Worker secrets; empty strings refuse construction without quoting values. |
2538
+
2539
+ For a connector bound to a fixed company, set
2540
+ `expectedCallbackParameters: { realmId: "company_alice" }`. This checks all
2541
+ callbacks, including errors, and direct `finishAuth` calls. Names must be
2542
+ nonempty ASCII letters, digits, dots, underscores, or hyphens; values must be
2543
+ nonempty strings. The protocol fields `code`, `state`, `iss`, `error`,
2544
+ `error_description`, and `error_uri` are reserved. Malformed expectations throw
2545
+ at construction without quoting values. Unconfigured provider parameters are
2546
+ ignored. Expectations are deployment configuration, never grant metadata or
2547
+ dynamic company selection; they do not inspect or rebind an existing grant.
1919
2548
 
1920
2549
  Handlers never see the grant. `ctx.oauth.fetch(url, init)` sends the calling
1921
2550
  owner's access token as `Authorization: Bearer` with these rules:
@@ -1924,10 +2553,28 @@ owner's access token as `Authorization: Bearer` with these rules:
1924
2553
  untrusted response URLs. Return redirects unfollowed; handlers cannot set `Authorization`.
1925
2554
  - On 401, recover exactly once using an already rotated token or one coalesced
1926
2555
  refresh through `remoteMcp()`'s coordinator. Persist rotation even when its
1927
- owner is cancelled after the answer. Replay once; stream bodies are refused.
2556
+ owner is cancelled after the answer. Only a positively read-classified
2557
+ GET/HEAD without method-override headers replays once;
2558
+ stream bodies are refused.
2559
+ - A request the core classifies as a write never replays (INV-9). Positive read
2560
+ classification comes from deployment `classification` first and otherwise the rule the
2561
+ registry applies to the tool's hints (a contradictory `readOnlyHint: true`
2562
+ with `destructiveHint: true` is a write). The actual method must also be GET
2563
+ or HEAD; an override header, missing read evidence, or a POST remains a
2564
+ one-attempt write, even when the tool calls it a read. After the recovery,
2565
+ the call fails with a non-retryable `connector_call_failed` whose outcome is
2566
+ unknown, and the renewed grant serves any fresh call the agent makes after
2567
+ reconciling the target.
2568
+ - Whatever that recovery meets, a write's failure is never retryable and its
2569
+ outcome is unknown. Until a refresh succeeds the 401's verdict stands:
2570
+ `downstream_oauth_required`, which the invocation marks
2571
+ `reconciliationRequired`, with the `authorize_connector` handoff, whether
2572
+ the refresh was [dead](#refresh-failures), lost after it was sent, raced
2573
+ by a disconnect or reset, failed in storage or contention, or handed back
2574
+ the token the API just rejected, which renews nothing.
1928
2575
  - No grant, a second 401, or a [dead refresh](#refresh-failures) means
1929
2576
  `downstream_oauth_required`, directing `authorize_connector`. Refresh waiter deadlines
1930
- return retryable `unavailable`. Latch second 401s for the request scope,
2577
+ return retryable `unavailable` for a read. Latch second 401s for the request scope,
1931
2578
  preventing repeated refreshes by later program calls.
1932
2579
  - Handlers can name no storage, sealing, or owner partition; the registry owns them.
1933
2580
  - The answer's `.text()` and `.json()`, clones' too, decode its bytes as UTF-8, so
@@ -1963,6 +2610,90 @@ provider skill explains App permissions and installation prerequisites.
1963
2610
 
1964
2611
  ## Contributor reference
1965
2612
 
2613
+ ### INV-5 output redaction limits
2614
+
2615
+ The output boundary's guarantee is K = 8 units, and these are its exact limits:
2616
+
2617
+ - A run of eight consecutive UTF-16 units of a registered form is matched
2618
+ wherever it appears, raw, under JSON escapes, or in a decoded base64 view.
2619
+ - Pieces shorter than eight units are matched only where they are adjacent:
2620
+ the projection joins neighbouring leaves (array items, text blocks), and
2621
+ provider lanes join one source's runs (one Slides shape's text runs, one Docs document's text). Pieces shorter than eight
2622
+ units that are not adjacent, such as the two seven-unit halves of a fourteen-
2623
+ unit value in two Slides shapes with other leaves between them, survive: they
2624
+ are indistinguishable from ordinary text without redacting ordinary words.
2625
+ - Object keys are checked on their own and never join the value projection, so
2626
+ a key never separates two adjacent values. A value split between a key and
2627
+ its value with each piece shorter than eight units, such as a seven-unit key
2628
+ holding a seven-unit value, survives like any other non-adjacent pieces; a
2629
+ piece of eight or more units is still matched in either place.
2630
+ - Private values of one to three units match whole field values only; four to
2631
+ seven also as substrings; eight and more also by every eight-unit run.
2632
+ - One checker (`checked()` in `src/sent-secrets.ts`) applies everything below
2633
+ to every output-bound string: leaves, keys, scalars, declared binary, typed
2634
+ binary, cursor claims and Connecta's own encodings, provider lanes, bounded
2635
+ reads and structural refusals. Its matching steps run only inside it; a
2636
+ serialized container's cross-token scan, which can only withhold it whole, is
2637
+ the one other reader of the fragment index. The test "there is one string
2638
+ checker" spies on every step and fails when one runs outside it.
2639
+ - A Proxy, which only in-process code can construct, can answer a later read
2640
+ differently from the one redaction made; the guarantee covers what redaction
2641
+ read. So can code on an error class's prototype, which its copy keeps so that
2642
+ classification still sees its class; JSON calls none of it once `toJSON` is
2643
+ shadowed. Only verified plain data is returned as it stands; everything else
2644
+ is a copy of checked data properties, and every value is JSON data, a fresh
2645
+ `Uint8Array`, or an error copy.
2646
+ - An array's fields other than its indices are never read, because JSON never
2647
+ serializes them.
2648
+
2649
+ The encoding lattice is exactly this, and anything outside it is out of scope:
2650
+
2651
+ - Text views of every string, whether a leaf, a key, a declared binary field or
2652
+ a cursor claim: the raw text; one layer of JSON escapes decoded
2653
+ (`\uXXXX` and `\" \\ \/ \b \f \n \r \t`); percent escapes decoded; and
2654
+ the form decoding, a `+` read as a space with any percent escapes decoded,
2655
+ generated whenever a `+` appears. Text matching itself also reads
2656
+ through up to eight JSON escape layers and matches registered percent forms.
2657
+ - In every text view, base64 and base64url runs are decoded. A run is a maximal
2658
+ span of `A-Z a-z 0-9 + / - _`, decoded from its first unit; up to two `=`
2659
+ after it are padding and are never counted. A whole view is decoded from six
2660
+ units; a run inside one from `SHORT_RUN_UNITS` = 6 under payload rules with a
2661
+ four-to-seven-unit private value registered, otherwise `FRAGMENT_RUN_UNITS` = 11.
2662
+ - Derivation: a run of L units decodes to floor(3L/4) bytes, so p bytes that start
2663
+ a bytes into the encoding (alignment a = 0, 1 or 2) need L >= ceil(4(a + p)/3),
2664
+ least at a = 0. The smallest piece a payload pass matches is a short value,
2665
+ four units and so at least four bytes: ceil(16/3) = 6. Otherwise it is K = 8
2666
+ units, at least eight bytes: ceil(32/3) = 11. Padding follows the run, so a
2667
+ padded and an unpadded encoding decode alike. The tests cover four-, seven-
2668
+ and eight-unit values at every alignment, padded and unpadded, in both
2669
+ alphabets.
2670
+ - Decoded bytes are matched as bytes, as UTF-8 when a form is not ASCII, and
2671
+ through their own JSON escapes within the eight-layer limit. Every decode is
2672
+ charged to the call's work budget, and content the budget cannot check is
2673
+ withheld.
2674
+ - Bytes, decoded or held by a typed array, are checked as bytes, through their
2675
+ JSON escapes, as UTF-8, and through the base64 they carry. Text views are
2676
+ views of strings: bytes that need a percent or form decoding before their
2677
+ base64 shows are two decodes chained, outside the lattice.
2678
+ - Out of scope: two or more text-layer decodes chained (percent escapes inside
2679
+ JSON escapes, or two JSON layers before base64); base64 of base64 (decoded
2680
+ content is searched for base64 up to three levels deep, but the guarantee does
2681
+ not depend on it); a base64 encoding glued to preceding alphabet units with no
2682
+ separator, such as a path segment after `/`, which decodes misaligned; HTML
2683
+ entities; compression; custom alphabets; encryption; bytes written as a
2684
+ JSON array of numbers; and one encoding split across separate leaves, such as
2685
+ the base64 of a value cut into array items. Raw text and JSON escapes are
2686
+ matched across adjacent leaves, but base64 and base64url decoding operates
2687
+ within each string.
2688
+ - The boundary removes echoes of values Connecta sent. A downstream already
2689
+ holds what it was sent and can always return it in a form outside this
2690
+ lattice, so redaction is not a defense against a downstream that reshapes a
2691
+ value on purpose.
2692
+
2693
+ `test/sent-secret-output-boundary.test.ts` holds the evidence, including its
2694
+ `review round 3`, `review round 4` and `review round 5` blocks for these limits;
2695
+ `test/writeonly-wire-redaction.node.test.ts` runs the QuickJS guest cases.
2696
+
1966
2697
  ### Source and tests
1967
2698
 
1968
2699
  [Decision 0007](https://github.com/zackbart/connecta/blob/main/decisions/0007-agent-pool-layout.md)
@@ -1973,6 +2704,8 @@ boundaries, and `src/storage/keys.ts` owns credential key families.
1973
2704
  Inbound adapters live in [src/auth](https://github.com/zackbart/connecta/tree/main/src/auth);
1974
2705
  [auth routes](https://github.com/zackbart/connecta/blob/main/src/routes/oauth.ts)
1975
2706
  own connection handoffs and callbacks. Check [inbound auth tests](https://github.com/zackbart/connecta/blob/main/test/inbound-auth.test.ts),
2707
+ [Clerk organization tests](https://github.com/zackbart/connecta/blob/main/test/clerk-organization.test.ts)
2708
+ and their [workerd counterpart](https://github.com/zackbart/connecta/blob/main/test/clerk-organization-workerd.test.ts),
1976
2709
  [Access tests](https://github.com/zackbart/connecta/blob/main/test/cloudflare-access-auth.test.ts),
1977
2710
  and [identity-scope tests](https://github.com/zackbart/connecta/blob/main/test/identity-scope.test.ts)
1978
2711
  for credential precedence and disclosure boundaries.
@@ -1985,3 +2718,23 @@ Downstream OAuth implementation and evidence are in
1985
2718
  [refresh leases](https://github.com/zackbart/connecta/blob/main/test/oauth-refresh-lease.test.ts),
1986
2719
  and [API OAuth tests](https://github.com/zackbart/connecta/blob/main/test/api-oauth.test.ts).
1987
2720
  Use the test pointers within each section for narrower failure cases.
2721
+
2722
+ ### Authentication execution (contributor)
2723
+
2724
+ Built-in authentication and downstream OAuth use private native programs behind
2725
+ the same Promise adapter and SDK contracts. Recognition still selects one
2726
+ verifier, and a refusal never falls back to another identity provider. Native
2727
+ operations use the existing request owner; cancellation cannot make a closed
2728
+ caller consume a refresh result or send another resource request. Once the
2729
+ claim CAS is submitted, the bounded refresh coordinator completes independently
2730
+ and commits a successful rotation under the existing epoch and spent-token
2731
+ rules. Code exchange does not acquire that coordinator ownership.
2732
+
2733
+ Authentication capacity remains charged until the underlying uncancellable work
2734
+ settles or collection proves that work unreachable. Abandonment alone never
2735
+ releases capacity. A native
2736
+ refactor does not change membership cache TTLs, audience/resource bindings,
2737
+ agent namespaces, credential partitions, OAuth transport policy or the rule of
2738
+ one physical attempt per credential-bearing POST. See the
2739
+ [native auth implementation](./architecture.md#native-auth-programs-contributor)
2740
+ for contributor boundaries and retained Promise leaves.